[MINOR] add the "force-persist" statement to force persistence on down servers

This is used to force access to down servers for some requests. This
is useful when validating that a change on a server correctly works
before enabling the server again.
This commit is contained in:
Willy Tarreau 2010-01-22 19:10:05 +01:00
parent ff7b5883c0
commit 4de9149f87
8 changed files with 131 additions and 12 deletions

View File

@ -1804,6 +1804,34 @@ errorloc303 <code> <url>
See also : "errorfile", "errorloc", "errorloc302"
force-persist { if | unless } <condition>
Declare a condition to force persistence on down servers
May be used in sections: defaults | frontend | listen | backend
no | yes | yes | yes
By default, requests are not dispatched to down servers. It is possible to
force this using "option persist", but it is unconditional and redispatches
to a valid server if "option redispatch" is set. That leaves with very little
possibilities to force some requests to reach a server which is artificially
marked down for maintenance operations.
The "force-persist" statement allows one to declare various ACL-based
conditions which, when met, will cause a request to ignore the down status of
a server and still try to connect to it. That makes it possible to start a
server, still replying an error to the health checks, and run a specially
configured browser to test the service. Among the handy methods, one could
use a specific source IP address, or a specific cookie. The cookie also has
the advantage that it can easily be added/removed on the browser from a test
page. Once the service is validated, it is then possible to open the service
to the world by returning a valid response to health checks.
The forced persistence is enabled when an "if" condition is met, or unless an
"unless" condition is met. The final redispatch is always disabled when this
is used.
See also : "option redispatch", "persist", and section 7 about ACL usage.
fullconn <conns>
Specify at what backend load the servers will reach their maxconn
May be used in sections : defaults | frontend | listen | backend
@ -2935,7 +2963,7 @@ no option persist
If this option has been enabled in a "defaults" section, it can be disabled
in a specific instance by prepending the "no" keyword before it.
See also : "option redispatch", "retries"
See also : "option redispatch", "retries", "force-persist"
option redispatch
@ -2962,7 +2990,7 @@ no option redispatch
If this option has been enabled in a "defaults" section, it can be disabled
in a specific instance by prepending the "no" keyword before it.
See also : "redispatch", "retries"
See also : "redispatch", "retries", "force-persist"
option smtpchk

View File

@ -171,6 +171,7 @@ struct proxy {
struct list block_cond; /* early blocking conditions (chained) */
struct list redirect_rules; /* content redirecting rules (chained) */
struct list switching_rules; /* content switching rules (chained) */
struct list force_persist_rules; /* 'force-persist' rules (chained) */
struct list sticking_rules; /* content sticking rules (chained) */
struct list storersp_rules; /* content store response rules (chained) */
struct { /* TCP request processing */
@ -297,6 +298,11 @@ struct switching_rule {
} be;
};
struct force_persist_rule {
struct list list; /* list linked to from the proxy */
struct acl_cond *cond; /* acl condition to meet */
};
struct sticking_rule {
struct list list; /* list linked to from the proxy */
struct acl_cond *cond; /* acl condition to meet */

View File

@ -2,7 +2,7 @@
* include/types/session.h
* This file defines everything related to sessions.
*
* Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
* Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -47,7 +47,7 @@
#define SN_ADDR_SET 0x00000004 /* this session's server address has been set */
#define SN_BE_ASSIGNED 0x00000008 /* a backend was assigned. Conns are accounted. */
/* unused: 0x00000010 */
#define SN_FORCE_PRST 0x00000010 /* force persistence here, even if server is down */
#define SN_MONITOR 0x00000020 /* this session comes from a monitoring system */
#define SN_CURR_SESS 0x00000040 /* a connection is currently being counted on the server */
#define SN_FRT_ADDR_SET 0x00000080 /* set if the frontend address has been filled */

View File

@ -888,7 +888,8 @@ int srv_redispatch_connect(struct session *t)
* would bring us on the same server again. Note that t->srv is set in
* this case.
*/
if ((t->flags & SN_DIRECT) && (t->be->options & PR_O_REDISP)) {
if (((t->flags & (SN_DIRECT|SN_FORCE_PRST)) == SN_DIRECT) &&
(t->be->options & PR_O_REDISP)) {
t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
t->prev_srv = t->srv;
goto redispatch;

View File

@ -849,6 +849,7 @@ static void init_new_proxy(struct proxy *p)
LIST_INIT(&p->redirect_rules);
LIST_INIT(&p->mon_fail_cond);
LIST_INIT(&p->switching_rules);
LIST_INIT(&p->force_persist_rules);
LIST_INIT(&p->sticking_rules);
LIST_INIT(&p->storersp_rules);
LIST_INIT(&p->tcp_req.inspect_rules);
@ -2038,6 +2039,58 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
LIST_INIT(&rule->list);
LIST_ADDQ(&curproxy->switching_rules, &rule->list);
}
else if (!strcmp(args[0], "force-persist")) {
int pol = ACL_COND_NONE;
struct acl_cond *cond;
struct force_persist_rule *rule;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if (warnifnotcap(curproxy, PR_CAP_FE|PR_CAP_BE, file, linenum, args[0], NULL))
err_code |= ERR_WARN;
if (!strcmp(args[1], "if"))
pol = ACL_COND_IF;
else if (!strcmp(args[1], "unless"))
pol = ACL_COND_UNLESS;
if (pol == ACL_COND_NONE) {
Alert("parsing [%s:%d] : '%s' requires either 'if' or 'unless' followed by a condition.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if ((cond = parse_acl_cond((const char **)args + 2, &curproxy->acl, pol)) == NULL) {
Alert("parsing [%s:%d] : error detected while parsing a 'force-persist' rule.\n",
file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
cond->file = file;
cond->line = linenum;
curproxy->acl_requires |= cond->requires;
if (cond->requires & ACL_USE_RTR_ANY) {
struct acl *acl;
const char *name;
acl = cond_find_require(cond, ACL_USE_RTR_ANY);
name = acl ? acl->name : "(unknown)";
Warning("parsing [%s:%d] : acl '%s' involves some response-only criteria which will be ignored.\n",
file, linenum, name);
err_code |= ERR_WARN;
}
rule = (struct force_persist_rule *)calloc(1, sizeof(*rule));
rule->cond = cond;
LIST_INIT(&rule->list);
LIST_ADDQ(&curproxy->force_persist_rules, &rule->list);
}
else if (!strcmp(args[0], "stick-table")) {
int myidx = 1;

View File

@ -310,7 +310,8 @@ static int redistribute_pending(struct server *s)
FOREACH_ITEM_SAFE(pc, pc_bck, &s->pendconns, pc_end, struct pendconn *, list) {
struct session *sess = pc->sess;
if (sess->be->options & PR_O_REDISP) {
if ((sess->be->options & (PR_O_REDISP|PR_O_PERSIST)) == PR_O_REDISP &&
!(sess->flags & SN_FORCE_PRST)) {
/* The REDISP option was specified. We will ignore
* cookie and force to balance or use the dispatcher.
*/

View File

@ -3504,7 +3504,7 @@ void http_end_txn_clean_session(struct session *s)
s->req->cons->flags = SI_FL_NONE;
s->req->flags &= ~(BF_SHUTW|BF_SHUTW_NOW|BF_AUTO_CONNECT|BF_WRITE_ERROR|BF_STREAMER|BF_STREAMER_FAST);
s->rep->flags &= ~(BF_SHUTR|BF_SHUTR_NOW|BF_READ_ATTACHED|BF_READ_ERROR|BF_READ_NOEXP|BF_STREAMER|BF_STREAMER_FAST|BF_WRITE_PARTIAL);
s->flags &= ~(SN_DIRECT|SN_ASSIGNED|SN_ADDR_SET|SN_BE_ASSIGNED);
s->flags &= ~(SN_DIRECT|SN_ASSIGNED|SN_ADDR_SET|SN_BE_ASSIGNED|SN_FORCE_PRST);
s->flags &= ~(SN_CURR_SESS|SN_REDIRECTABLE);
s->txn.meth = 0;
http_reset_txn(s);
@ -5215,7 +5215,9 @@ void manage_client_side_appsession(struct session *t, const char *buf, int len)
struct server *srv = t->be->srv;
while (srv) {
if (strcmp(srv->id, asession->serverid) == 0) {
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
if ((srv->state & SRV_RUNNING) ||
(t->be->options & PR_O_PERSIST) ||
(t->flags & SN_FORCE_PRST)) {
/* we found the server and it's usable */
txn->flags &= ~TX_CK_MASK;
txn->flags |= TX_CK_VALID;
@ -5411,7 +5413,9 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
while (srv) {
if (srv->cookie && (srv->cklen == delim - p3) &&
!memcmp(p3, srv->cookie, delim - p3)) {
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
if ((srv->state & SRV_RUNNING) ||
(t->be->options & PR_O_PERSIST) ||
(t->flags & SN_FORCE_PRST)) {
/* we found the server and it's usable */
txn->flags &= ~TX_CK_MASK;
txn->flags |= TX_CK_VALID;

View File

@ -276,7 +276,8 @@ int sess_update_st_cer(struct session *s, struct stream_interface *si)
* bit to ignore any persistence cookie. We won't count a retry nor a
* redispatch yet, because this will depend on what server is selected.
*/
if (s->srv && s->conn_retries == 0 && s->be->options & PR_O_REDISP) {
if (s->srv && s->conn_retries == 0 &&
s->be->options & PR_O_REDISP && !(s->flags & SN_FORCE_PRST)) {
if (may_dequeue_tasks(s->srv, s->be))
process_srv_queue(s->srv);
@ -543,12 +544,15 @@ static void sess_prepare_conn_req(struct session *s, struct stream_interface *si
}
/* This stream analyser checks the switching rules and changes the backend
* if appropriate. The default_backend rule is also considered.
* if appropriate. The default_backend rule is also considered, then the
* target backend's forced persistence rules are also evaluated last if any.
* It returns 1 if the processing can continue on next analysers, or zero if it
* either needs more data or wants to immediately abort the request.
*/
int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
{
struct force_persist_rule *prst_rule;
req->analysers &= ~an_bit;
req->analyse_exp = TICK_ETERNITY;
@ -594,6 +598,26 @@ int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
if (s->fe == s->be)
s->req->analysers &= ~AN_REQ_HTTP_PROCESS_BE;
/* as soon as we know the backend, we must check if we have a matching forced
* persistence rule, and report that in the session.
*/
list_for_each_entry(prst_rule, &s->be->force_persist_rules, list) {
int ret = 1;
if (prst_rule->cond) {
ret = acl_exec_cond(prst_rule->cond, s->be, s, &s->txn, ACL_DIR_REQ);
ret = acl_pass(ret);
if (prst_rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
}
if (ret) {
/* no rule, or the rule matches */
s->flags |= SN_FORCE_PRST;
break;
}
}
return 1;
sw_failed:
@ -669,7 +693,9 @@ int process_sticking_rules(struct session *s, struct buffer *req, int an_bit)
struct server *srv;
srv = container_of(node, struct server, conf.id);
if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
if ((srv->state & SRV_RUNNING) ||
(px->options & PR_O_PERSIST) ||
(s->flags & SN_FORCE_PRST)) {
s->flags |= SN_DIRECT | SN_ASSIGNED;
s->srv = srv;
}