mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-03 18:09:25 +00:00
[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:
parent
ff7b5883c0
commit
4de9149f87
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user