From: Willy Tarreau Date: Tue, 13 Oct 2015 14:16:41 +0000 (+0200) Subject: MEDIUM: server: implement TCP_USER_TIMEOUT on the server X-Git-Tag: v1.6.0~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=163d4620c627bff4d8f29d1bc8c497f3819778b8;p=thirdparty%2Fhaproxy.git MEDIUM: server: implement TCP_USER_TIMEOUT on the server This is equivalent to commit 2af207a ("MEDIUM: tcp: implement tcp-ut bind option to set TCP_USER_TIMEOUT") except that this time it works on the server side. The purpose is to detect dead server connections even when checks are rare, disabled, or after a soft reload (since checks are disabled there as well), and to ensure client connections will get killed faster. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index b34a80ccae..c6219d2f16 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -10598,6 +10598,21 @@ ssl Supported in default-server: No +tcp-ut + Sets the TCP User Timeout for all outgoing connections to this server. This + option is available on Linux since version 2.6.37. It allows haproxy to + configure a timeout for sockets which contain data not receiving an + acknoledgement for the configured delay. This is especially useful on + long-lived connections experiencing long idle periods such as remote + terminals or database connection pools, where the client and server timeouts + must remain high to allow a long period of idle, but where it is important to + detect that the server has disappeared in order to release all resources + associated with its connection (and the client's session). One typical use + case is also to force dead server connections to die when health checks are + too slow or during a soft reload since health checks are then disabled. The + argument is a delay expressed in milliseconds by default. This only works for + regular TCP connections, and is ignored for other protocols. + track [/] This option enables ability to set the current state of the server by tracking another one. It is possible to track a server which itself tracks another diff --git a/include/types/server.h b/include/types/server.h index abd1130035..3e25c344c0 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -216,6 +216,7 @@ struct server { time_t last_change; /* last time, when the state was changed */ int puid; /* proxy-unique server ID, used for SNMP, and "first" LB algo */ + int tcp_ut; /* for TCP, user timeout */ struct check check; /* health-check specific configuration */ struct check agent; /* agent specific configuration */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index df10ccbd9c..cce0acbfae 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -39,7 +39,6 @@ #include #include -#include #include #include @@ -56,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -500,6 +500,11 @@ int tcp_connect_server(struct connection *conn, int data, int delack) setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &zero, sizeof(zero)); #endif +#ifdef TCP_USER_TIMEOUT + /* there is not much more we can do here when it fails, it's still minor */ + if (srv && srv->tcp_ut) + setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &srv->tcp_ut, sizeof(srv->tcp_ut)); +#endif if (global.tune.server_sndbuf) setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf)); @@ -2310,6 +2315,31 @@ static int bind_parse_namespace(char **args, int cur_arg, struct proxy *px, stru } #endif +#ifdef TCP_USER_TIMEOUT +/* parse the "tcp-ut" server keyword */ +static int srv_parse_tcp_ut(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err) +{ + const char *ptr = NULL; + unsigned int timeout; + + if (!*args[*cur_arg + 1]) { + memprintf(err, "'%s' : missing TCP User Timeout value", args[*cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + + ptr = parse_time_err(args[*cur_arg + 1], &timeout, TIME_UNIT_MS); + if (ptr) { + memprintf(err, "'%s' : expects a positive delay in milliseconds", args[*cur_arg]); + return ERR_ALERT | ERR_FATAL; + } + + if (newsrv->addr.ss_family == AF_INET || newsrv->addr.ss_family == AF_INET6) + newsrv->tcp_ut = timeout; + + return 0; +} +#endif + static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req }, { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep }, @@ -2385,6 +2415,12 @@ static struct bind_kw_list bind_kws = { "TCP", { }, { { NULL, NULL, 0 }, }}; +static struct srv_kw_list srv_kws = { "TCP", { }, { +#ifdef TCP_USER_TIMEOUT + { "tcp-ut", srv_parse_tcp_ut, 1, 0 }, /* set TCP user timeout on server */ +#endif + { NULL, NULL, 0 }, +}}; static struct action_kw_list tcp_req_conn_actions = {ILH, { { "silent-drop", tcp_parse_silent_drop }, @@ -2421,6 +2457,7 @@ static void __tcp_protocol_init(void) cfg_register_keywords(&cfg_kws); acl_register_keywords(&acl_kws); bind_register_keywords(&bind_kws); + srv_register_keywords(&srv_kws); tcp_req_conn_keywords_register(&tcp_req_conn_actions); tcp_req_cont_keywords_register(&tcp_req_cont_actions); tcp_res_cont_keywords_register(&tcp_res_cont_actions);