Skip to content

Commit

Permalink
MEDIUM: server: add init-state
Browse files Browse the repository at this point in the history
Allow the user to set the "initial state" of a server.

Context:

Servers are always set in an UP status by default. In
some cases, further checks are required to determine if the server is
ready to receive client traffic.

This introduces the "init-state {up|down}" configuration parameter to
the server.

- when set to 'fully-up', the server is considered immediately available
  and can turn to the DOWN sate when ALL health checks fail.
- when set to 'up' (the default), the server is considered immediately
  available and will initiate a health check that can turn it to the DOWN
  state immediately if it fails.
- when set to 'down', the server initially is considered unavailable and
  will initiate a health check that can turn it to the UP state immediately
  if it succeeds.
- when set to 'fully-down', the server is initially considered unavailable
  and can turn to the UP state when ALL health checks succeed.

The server's init-state is considered when the HAProxy instance
is (re)started, a new server is detected (for example via service
discovery / DNS resolution), a server exits maintenance, etc.

Link: haproxy/haproxy#51
  • Loading branch information
indiebrain authored and wtarreau committed Sep 5, 2024
1 parent e8b1ad4 commit 50322df
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 1 deletion.
45 changes: 45 additions & 0 deletions doc/configuration.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17551,6 +17551,51 @@ downinter <delay>
"inter" setting will have a very limited effect as it will not be able to
reduce the time spent in the queue.

init-state { fully-up | up | down | fully-down }
May be used in the following contexts: tcp, http

May be used in sections : defaults | frontend | listen | backend
no | no | yes | yes

The "init-state" option sets the initial state of the server:
- when set to 'fully-up', the server is considered immediately available
and can turn to the DOWN sate when ALL health checks fail.
- when set to 'up' (the default), the server is considered immediately
available and will initiate a health check that can turn it to the DOWN
state immediately if it fails.
- when set to 'down', the server initially is considered unavailable and
will initiate a health check that can turn it to the UP state immediately
if it succeeds.
- when set to 'fully-down', the server is initially considered unavailable
and can turn to the UP state when ALL health checks succeed.

The server's init-state is considered when the HAProxy instance is
(re)started, a new server is detected (for example via service discovery /
DNS resolution), a server exits maintenance, etc.

Examples:
# pass client traffic ONLY to Redis "master" node
backend redis-master
mode tcp
balance first
option tcp-check
tcp-check send role\r\n
tcp-check expect string master
server-template redis 3 _redis._tcp.redis-headless-service.sandbox.svc.cluster.local:6379 check ... init-state down

# pass traffic to the server only after 3 successful health checks
backend google-backend
mode http
server srv1 google.com:80 check init-state down rise 3
server srv2 google.com:80 check init-state down rise 3

# or
listen
mode http
server-template srv 2 google.com:80 check init-state down rise 3

See also: "option tcp-check", "option httpchk"

log-bufsize <bufsize>
May be used in the following contexts: log

Expand Down
12 changes: 12 additions & 0 deletions include/haproxy/server-t.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ enum srv_initaddr {
SRV_IADDR_IP = 4, /* we set an arbitrary IP address to the server */
} __attribute__((packed));

/* options for servers' "init-state" parameter this parameter may be
* used to drive HAProxy's behavior when determining a server's status
* at start up time.
*/
enum srv_init_state {
SRV_INIT_STATE_FULLY_DOWN = 0, /* the server should initially be considered DOWN until it passes all health checks. Please keep set to zero. */
SRV_INIT_STATE_DOWN, /* the server should initially be considered DOWN until it passes one health check. */
SRV_INIT_STATE_UP, /* the server should initially be considered UP, but will go DOWN if it fails one health check. */
SRV_INIT_STATE_FULLY_UP, /* the server should initially be considered UP, but will go DOWN if it fails all health checks. */
} __attribute__((packed));

/* server-state-file version */
#define SRV_STATE_FILE_VERSION 1
#define SRV_STATE_FILE_VERSION_MIN 1
Expand Down Expand Up @@ -279,6 +290,7 @@ struct proxy;
struct server {
/* mostly config or admin stuff, doesn't change often */
enum obj_type obj_type; /* object type == OBJ_TYPE_SERVER */
enum srv_init_state init_state; /* server's initial state among SRV_INIT_STATE */
enum srv_state next_state, cur_state; /* server state among SRV_ST_* */
enum srv_admin next_admin, cur_admin; /* server maintenance status : SRV_ADMF_* */
signed char use_ssl; /* ssl enabled (1: on, 0: disabled, -1 forced off) */
Expand Down
38 changes: 37 additions & 1 deletion src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,27 @@ static int srv_parse_init_addr(char **args, int *cur_arg,
return 0;
}

/* Parse the "init-state" server keyword */
static int srv_parse_init_state(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
{
if (strcmp(args[*cur_arg + 1], "fully-up") == 0)
newsrv->init_state= SRV_INIT_STATE_FULLY_UP;
else if (strcmp(args[*cur_arg + 1], "up") == 0)
newsrv->init_state = SRV_INIT_STATE_UP;
else if (strcmp(args[*cur_arg + 1], "down") == 0)
newsrv->init_state= SRV_INIT_STATE_DOWN;
else if (strcmp(args[*cur_arg + 1], "fully-down") == 0)
newsrv->init_state= SRV_INIT_STATE_FULLY_DOWN;
else {
memprintf(err, "'%s' expects one of 'fully-up', 'up', 'down', or 'fully-down' but got '%s'",
args[*cur_arg], args[*cur_arg + 1]);
return ERR_ALERT | ERR_FATAL;
}

return 0;
}

/* Parse the "log-bufsize" server keyword */
static int srv_parse_log_bufsize(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
Expand Down Expand Up @@ -2302,6 +2323,7 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
{ "hash-key", srv_parse_hash_key, 1, 1, 1 }, /* Configure how chash keys are computed */
{ "id", srv_parse_id, 1, 0, 1 }, /* set id# of server */
{ "init-addr", srv_parse_init_addr, 1, 1, 0 }, /* */
{ "init-state", srv_parse_init_state, 1, 1, 0 }, /* Set the initial state of the server */
{ "log-bufsize", srv_parse_log_bufsize, 1, 1, 0 }, /* Set the ring bufsize for log server (only for log backends) */
{ "log-proto", srv_parse_log_proto, 1, 1, 0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
{ "maxconn", srv_parse_maxconn, 1, 1, 1 }, /* Set the max number of concurrent connection */
Expand Down Expand Up @@ -2713,6 +2735,8 @@ void srv_settings_init(struct server *srv)
srv->agent.fall = DEF_AGENT_FALLTIME;
srv->agent.port = 0;

srv->init_state = SRV_INIT_STATE_UP;

srv->maxqueue = 0;
srv->minconn = 0;
srv->maxconn = 0;
Expand Down Expand Up @@ -2842,6 +2866,8 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl

srv->init_addr_methods = src->init_addr_methods;
srv->init_addr = src->init_addr;

srv->init_state = src->init_state;
#if defined(USE_OPENSSL)
srv_ssl_settings_cpy(srv, src);
#endif
Expand Down Expand Up @@ -6504,7 +6530,17 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca
*/
if (s->check.state & CHK_ST_ENABLED) {
s->check.state &= ~CHK_ST_PAUSED;
s->check.health = s->check.rise; /* start OK but check immediately */
if(s->init_state == SRV_INIT_STATE_FULLY_UP) {
s->check.health = s->check.rise + s->check.fall - 1; /* initially UP, when all checks fail to bring server DOWN */
}
else if(s->init_state == SRV_INIT_STATE_DOWN) {
s->check.health = s->check.rise - 1; /* initially DOWN, when one check is successful bring server UP */
}
else if(s->init_state == SRV_INIT_STATE_FULLY_DOWN) {
s->check.health = 0; /* initially DOWN, when all checks are successful bring server UP */
} else {
s->check.health = s->check.rise; /* initially UP, when one check fails check brings server DOWN */
}
}

if ((!s->track || s->track->next_state != SRV_ST_STOPPED) &&
Expand Down

0 comments on commit 50322df

Please sign in to comment.