2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* Backend variables and functions.
|
|
|
|
|
*
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <syslog.h>
|
2006-11-14 09:40:51 -05:00
|
|
|
#include <string.h>
|
2008-04-14 14:47:37 -04:00
|
|
|
#include <ctype.h>
|
2009-08-24 07:11:06 -04:00
|
|
|
#include <sys/types.h>
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2021-10-06 12:23:40 -04:00
|
|
|
#include <import/ebmbtree.h>
|
|
|
|
|
|
2020-05-27 06:58:42 -04:00
|
|
|
#include <haproxy/api.h>
|
2021-10-06 13:54:09 -04:00
|
|
|
#include <haproxy/acl.h>
|
|
|
|
|
#include <haproxy/activity.h>
|
2020-06-09 03:07:15 -04:00
|
|
|
#include <haproxy/arg.h>
|
|
|
|
|
#include <haproxy/backend.h>
|
2020-06-04 15:07:02 -04:00
|
|
|
#include <haproxy/channel.h>
|
2020-06-04 12:21:56 -04:00
|
|
|
#include <haproxy/check.h>
|
2020-06-04 05:23:07 -04:00
|
|
|
#include <haproxy/frontend.h>
|
2020-06-09 03:07:15 -04:00
|
|
|
#include <haproxy/global.h>
|
2020-05-27 10:10:29 -04:00
|
|
|
#include <haproxy/hash.h>
|
2020-06-02 13:11:26 -04:00
|
|
|
#include <haproxy/http.h>
|
2020-06-04 15:21:03 -04:00
|
|
|
#include <haproxy/http_ana.h>
|
2020-06-04 03:08:41 -04:00
|
|
|
#include <haproxy/http_htx.h>
|
2020-06-03 02:44:35 -04:00
|
|
|
#include <haproxy/htx.h>
|
2020-06-04 08:34:27 -04:00
|
|
|
#include <haproxy/lb_chash.h>
|
2020-06-04 08:37:38 -04:00
|
|
|
#include <haproxy/lb_fas.h>
|
2020-06-04 08:41:04 -04:00
|
|
|
#include <haproxy/lb_fwlc.h>
|
2020-06-04 08:45:03 -04:00
|
|
|
#include <haproxy/lb_fwrr.h>
|
2020-06-04 14:22:59 -04:00
|
|
|
#include <haproxy/lb_map.h>
|
2024-03-28 12:24:53 -04:00
|
|
|
#include <haproxy/lb_ss.h>
|
2020-06-04 16:01:04 -04:00
|
|
|
#include <haproxy/log.h>
|
2020-06-09 03:07:15 -04:00
|
|
|
#include <haproxy/namespace.h>
|
2020-06-04 05:29:21 -04:00
|
|
|
#include <haproxy/obj_type.h>
|
2020-06-04 09:13:30 -04:00
|
|
|
#include <haproxy/payload.h>
|
2020-06-09 03:07:15 -04:00
|
|
|
#include <haproxy/proto_tcp.h>
|
|
|
|
|
#include <haproxy/protocol.h>
|
2020-06-04 16:29:18 -04:00
|
|
|
#include <haproxy/proxy.h>
|
2020-06-04 16:59:39 -04:00
|
|
|
#include <haproxy/queue.h>
|
2020-06-09 03:07:15 -04:00
|
|
|
#include <haproxy/sample.h>
|
2022-05-27 03:25:10 -04:00
|
|
|
#include <haproxy/sc_strm.h>
|
2020-06-04 17:20:13 -04:00
|
|
|
#include <haproxy/server.h>
|
2020-06-04 12:58:52 -04:00
|
|
|
#include <haproxy/session.h>
|
2020-06-04 14:30:20 -04:00
|
|
|
#include <haproxy/ssl_sock.h>
|
2022-05-27 03:47:12 -04:00
|
|
|
#include <haproxy/stconn.h>
|
2020-06-04 17:46:14 -04:00
|
|
|
#include <haproxy/stream.h>
|
2020-06-04 11:25:40 -04:00
|
|
|
#include <haproxy/task.h>
|
2020-06-02 12:15:32 -04:00
|
|
|
#include <haproxy/ticks.h>
|
2020-06-01 05:05:15 -04:00
|
|
|
#include <haproxy/time.h>
|
2020-06-04 17:46:14 -04:00
|
|
|
#include <haproxy/trace.h>
|
2015-07-09 05:40:25 -04:00
|
|
|
|
2019-11-05 10:18:10 -05:00
|
|
|
#define TRACE_SOURCE &trace_strm
|
|
|
|
|
|
2013-10-29 23:30:51 -04:00
|
|
|
/* helper function to invoke the correct hash method */
|
2023-09-19 04:51:53 -04:00
|
|
|
unsigned int gen_hash(const struct proxy* px, const char* key, unsigned long len)
|
2013-10-29 23:30:51 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash;
|
2013-10-29 23:30:51 -04:00
|
|
|
|
|
|
|
|
switch (px->lbprm.algo & BE_LB_HASH_FUNC) {
|
|
|
|
|
case BE_LB_HFCN_DJB2:
|
|
|
|
|
hash = hash_djb2(key, len);
|
|
|
|
|
break;
|
2013-11-14 08:30:35 -05:00
|
|
|
case BE_LB_HFCN_WT6:
|
|
|
|
|
hash = hash_wt6(key, len);
|
|
|
|
|
break;
|
2015-01-20 13:44:50 -05:00
|
|
|
case BE_LB_HFCN_CRC32:
|
|
|
|
|
hash = hash_crc32(key, len);
|
|
|
|
|
break;
|
2023-10-11 03:57:35 -04:00
|
|
|
case BE_LB_HFCN_NONE:
|
|
|
|
|
/* use key as a hash */
|
|
|
|
|
{
|
|
|
|
|
const char *_key = key;
|
|
|
|
|
|
|
|
|
|
hash = read_int64(&_key, _key + len);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2013-10-29 23:30:51 -04:00
|
|
|
case BE_LB_HFCN_SDBM:
|
|
|
|
|
/* this is the default hash function */
|
|
|
|
|
default:
|
|
|
|
|
hash = hash_sdbm(key, len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 05:35:48 -04:00
|
|
|
if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
|
|
|
|
|
hash = full_hash(hash);
|
|
|
|
|
|
2013-10-29 23:30:51 -04:00
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* This function recounts the number of usable active and backup servers for
|
|
|
|
|
* proxy <p>. These numbers are returned into the p->srv_act and p->srv_bck.
|
2007-11-25 19:15:43 -05:00
|
|
|
* This function also recomputes the total active and backup weights. However,
|
2008-03-08 15:42:54 -05:00
|
|
|
* it does not update tot_weight nor tot_used. Use update_backend_weight() for
|
2007-11-25 19:15:43 -05:00
|
|
|
* this.
|
2017-08-31 08:41:55 -04:00
|
|
|
* This functions is designed to be called before server's weight and state
|
|
|
|
|
* commit so it uses 'next' weight and states values.
|
2017-06-09 08:17:53 -04:00
|
|
|
*
|
|
|
|
|
* threads: this is the caller responsibility to lock data. For now, this
|
|
|
|
|
* function is called from lb modules, so it should be ok. But if you need to
|
|
|
|
|
* call it from another place, be careful (and update this comment).
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2009-10-01 03:17:05 -04:00
|
|
|
void recount_servers(struct proxy *px)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
struct server *srv;
|
|
|
|
|
|
2007-11-15 17:26:18 -05:00
|
|
|
px->srv_act = px->srv_bck = 0;
|
|
|
|
|
px->lbprm.tot_wact = px->lbprm.tot_wbck = 0;
|
2007-11-25 19:15:43 -05:00
|
|
|
px->lbprm.fbck = NULL;
|
2006-06-25 20:48:02 -04:00
|
|
|
for (srv = px->srv; srv != NULL; srv = srv->next) {
|
2017-08-31 08:41:55 -04:00
|
|
|
if (!srv_willbe_usable(srv))
|
2007-11-25 19:15:43 -05:00
|
|
|
continue;
|
|
|
|
|
|
2014-05-13 09:54:22 -04:00
|
|
|
if (srv->flags & SRV_F_BACKUP) {
|
2007-11-25 19:15:43 -05:00
|
|
|
if (!px->srv_bck &&
|
2008-03-08 15:42:54 -05:00
|
|
|
!(px->options & PR_O_USE_ALL_BK))
|
2007-11-25 19:15:43 -05:00
|
|
|
px->lbprm.fbck = srv;
|
|
|
|
|
px->srv_bck++;
|
2016-10-25 12:49:45 -04:00
|
|
|
srv->cumulative_weight = px->lbprm.tot_wbck;
|
2017-08-31 08:41:55 -04:00
|
|
|
px->lbprm.tot_wbck += srv->next_eweight;
|
2007-11-25 19:15:43 -05:00
|
|
|
} else {
|
|
|
|
|
px->srv_act++;
|
2016-10-25 12:49:45 -04:00
|
|
|
srv->cumulative_weight = px->lbprm.tot_wact;
|
2017-08-31 08:41:55 -04:00
|
|
|
px->lbprm.tot_wact += srv->next_eweight;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
2007-11-25 19:15:43 -05:00
|
|
|
}
|
2007-11-15 17:26:18 -05:00
|
|
|
|
2007-11-25 19:15:43 -05:00
|
|
|
/* This function simply updates the backend's tot_weight and tot_used values
|
|
|
|
|
* after servers weights have been updated. It is designed to be used after
|
|
|
|
|
* recount_servers() or equivalent.
|
2017-06-09 08:17:53 -04:00
|
|
|
*
|
|
|
|
|
* threads: this is the caller responsibility to lock data. For now, this
|
|
|
|
|
* function is called from lb modules, so it should be ok. But if you need to
|
|
|
|
|
* call it from another place, be careful (and update this comment).
|
2007-11-25 19:15:43 -05:00
|
|
|
*/
|
2009-10-01 03:17:05 -04:00
|
|
|
void update_backend_weight(struct proxy *px)
|
2007-11-25 19:15:43 -05:00
|
|
|
{
|
2007-11-15 17:26:18 -05:00
|
|
|
if (px->srv_act) {
|
|
|
|
|
px->lbprm.tot_weight = px->lbprm.tot_wact;
|
|
|
|
|
px->lbprm.tot_used = px->srv_act;
|
|
|
|
|
}
|
2007-11-25 19:15:43 -05:00
|
|
|
else if (px->lbprm.fbck) {
|
|
|
|
|
/* use only the first backup server */
|
2017-08-31 08:41:55 -04:00
|
|
|
px->lbprm.tot_weight = px->lbprm.fbck->next_eweight;
|
2007-11-25 19:15:43 -05:00
|
|
|
px->lbprm.tot_used = 1;
|
2007-11-15 17:26:18 -05:00
|
|
|
}
|
|
|
|
|
else {
|
2007-11-25 19:15:43 -05:00
|
|
|
px->lbprm.tot_weight = px->lbprm.tot_wbck;
|
|
|
|
|
px->lbprm.tot_used = px->srv_bck;
|
2007-11-15 17:26:18 -05:00
|
|
|
}
|
2007-11-25 19:15:43 -05:00
|
|
|
}
|
|
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
/*
|
|
|
|
|
* This function tries to find a running server for the proxy <px> following
|
|
|
|
|
* the source hash method. Depending on the number of active/backup servers,
|
|
|
|
|
* it will either look for active servers, or for backup servers.
|
|
|
|
|
* If any server is found, it will be returned. If no valid server is found,
|
|
|
|
|
* NULL is returned.
|
|
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_sh(struct proxy *px, const char *addr, int len, const struct server *avoid)
|
2009-10-01 15:11:15 -04:00
|
|
|
{
|
|
|
|
|
unsigned int h, l;
|
|
|
|
|
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
l = h = 0;
|
|
|
|
|
|
|
|
|
|
/* note: we won't hash if there's only one server left */
|
|
|
|
|
if (px->lbprm.tot_used == 1)
|
|
|
|
|
goto hash_done;
|
|
|
|
|
|
|
|
|
|
while ((l + sizeof (int)) <= len) {
|
|
|
|
|
h ^= ntohl(*(unsigned int *)(&addr[l]));
|
|
|
|
|
l += sizeof (int);
|
|
|
|
|
}
|
2023-10-11 05:35:48 -04:00
|
|
|
/* FIXME: why don't we use gen_hash() here as well?
|
|
|
|
|
* -> we don't take into account hash function from "hash_type"
|
|
|
|
|
* options here..
|
|
|
|
|
*/
|
2013-11-05 11:54:02 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
|
2010-11-24 09:04:29 -05:00
|
|
|
h = full_hash(h);
|
2009-10-01 15:11:15 -04:00
|
|
|
hash_done:
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, h, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, h);
|
2009-10-01 15:11:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function tries to find a running server for the proxy <px> following
|
|
|
|
|
* the URI hash method. In order to optimize cache hits, the hash computation
|
|
|
|
|
* ends at the question mark. Depending on the number of active/backup servers,
|
|
|
|
|
* it will either look for active servers, or for backup servers.
|
|
|
|
|
* If any server is found, it will be returned. If no valid server is found,
|
2019-01-14 10:14:15 -05:00
|
|
|
* NULL is returned. The lbprm.arg_opt{1,2,3} values correspond respectively to
|
2020-09-23 02:05:47 -04:00
|
|
|
* the "whole" optional argument (boolean, bit0), the "len" argument (numeric)
|
|
|
|
|
* and the "depth" argument (numeric).
|
2009-10-01 15:11:15 -04:00
|
|
|
*
|
|
|
|
|
* This code was contributed by Guillaume Dallaire, who also selected this hash
|
|
|
|
|
* algorithm out of a tens because it gave him the best results.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_uh(struct proxy *px, char *uri, int uri_len, const struct server *avoid)
|
2009-10-01 15:11:15 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash = 0;
|
2009-10-01 15:11:15 -04:00
|
|
|
int c;
|
|
|
|
|
int slashes = 0;
|
2013-10-29 23:30:51 -04:00
|
|
|
const char *start, *end;
|
2009-10-01 15:11:15 -04:00
|
|
|
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* note: we won't hash if there's only one server left */
|
|
|
|
|
if (px->lbprm.tot_used == 1)
|
|
|
|
|
goto hash_done;
|
|
|
|
|
|
2019-01-14 10:14:15 -05:00
|
|
|
if (px->lbprm.arg_opt2) // "len"
|
|
|
|
|
uri_len = MIN(uri_len, px->lbprm.arg_opt2);
|
2009-10-01 15:11:15 -04:00
|
|
|
|
2013-10-29 23:30:51 -04:00
|
|
|
start = end = uri;
|
2009-10-01 15:11:15 -04:00
|
|
|
while (uri_len--) {
|
2014-10-17 06:11:50 -04:00
|
|
|
c = *end;
|
2009-10-01 15:11:15 -04:00
|
|
|
if (c == '/') {
|
|
|
|
|
slashes++;
|
2019-01-14 10:14:15 -05:00
|
|
|
if (slashes == px->lbprm.arg_opt3) /* depth+1 */
|
2009-10-01 15:11:15 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2020-09-23 02:05:47 -04:00
|
|
|
else if (c == '?' && !(px->lbprm.arg_opt1 & 1)) // "whole"
|
2009-10-01 15:11:15 -04:00
|
|
|
break;
|
2014-10-17 06:11:50 -04:00
|
|
|
end++;
|
2009-10-01 15:11:15 -04:00
|
|
|
}
|
2013-10-29 23:30:51 -04:00
|
|
|
|
|
|
|
|
hash = gen_hash(px, start, (end - start));
|
|
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
hash_done:
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
2009-10-01 15:11:15 -04:00
|
|
|
}
|
|
|
|
|
|
2017-06-09 08:17:53 -04:00
|
|
|
/*
|
2007-11-01 17:48:15 -04:00
|
|
|
* This function tries to find a running server for the proxy <px> following
|
|
|
|
|
* the URL parameter hash method. It looks for a specific parameter in the
|
|
|
|
|
* URL and hashes it to compute the server ID. This is useful to optimize
|
|
|
|
|
* performance by avoiding bounces between servers in contexts where sessions
|
|
|
|
|
* are shared but cookies are not usable. If the parameter is not found, NULL
|
|
|
|
|
* is returned. If any server is found, it will be returned. If no valid server
|
|
|
|
|
* is found, NULL is returned.
|
|
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_ph(struct proxy *px, const char *uri, int uri_len, const struct server *avoid)
|
2007-11-01 17:48:15 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash = 0;
|
2013-10-29 23:30:51 -04:00
|
|
|
const char *start, *end;
|
2008-04-14 14:47:37 -04:00
|
|
|
const char *p;
|
|
|
|
|
const char *params;
|
2007-11-01 17:48:15 -04:00
|
|
|
int plen;
|
|
|
|
|
|
2008-04-14 14:47:37 -04:00
|
|
|
/* when tot_weight is 0 then so is srv_count */
|
2007-11-15 17:26:18 -05:00
|
|
|
if (px->lbprm.tot_weight == 0)
|
2007-11-01 17:48:15 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
2008-04-14 14:47:37 -04:00
|
|
|
if ((p = memchr(uri, '?', uri_len)) == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2007-11-01 17:48:15 -04:00
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
uri_len -= (p - uri);
|
2019-01-14 09:23:54 -05:00
|
|
|
plen = px->lbprm.arg_len;
|
2008-04-14 14:47:37 -04:00
|
|
|
params = p;
|
2007-11-01 17:48:15 -04:00
|
|
|
|
|
|
|
|
while (uri_len > plen) {
|
|
|
|
|
/* Look for the parameter name followed by an equal symbol */
|
2008-04-14 14:47:37 -04:00
|
|
|
if (params[plen] == '=') {
|
2019-01-14 09:23:54 -05:00
|
|
|
if (memcmp(params, px->lbprm.arg_str, plen) == 0) {
|
2008-04-14 14:47:37 -04:00
|
|
|
/* OK, we have the parameter here at <params>, and
|
2007-11-01 17:48:15 -04:00
|
|
|
* the value after the equal sign, at <p>
|
2008-04-14 14:47:37 -04:00
|
|
|
* skip the equal symbol
|
2007-11-01 17:48:15 -04:00
|
|
|
*/
|
2008-04-14 14:47:37 -04:00
|
|
|
p += plen + 1;
|
2013-10-29 23:30:51 -04:00
|
|
|
start = end = p;
|
2008-04-14 14:47:37 -04:00
|
|
|
uri_len -= plen + 1;
|
|
|
|
|
|
2013-10-29 23:30:51 -04:00
|
|
|
while (uri_len && *end != '&') {
|
2007-11-01 17:48:15 -04:00
|
|
|
uri_len--;
|
2013-10-29 23:30:51 -04:00
|
|
|
end++;
|
2007-11-01 17:48:15 -04:00
|
|
|
}
|
2013-10-29 23:30:51 -04:00
|
|
|
hash = gen_hash(px, start, (end - start));
|
|
|
|
|
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
2007-11-01 17:48:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
2008-04-14 14:47:37 -04:00
|
|
|
/* skip to next parameter */
|
|
|
|
|
p = memchr(params, '&', uri_len);
|
|
|
|
|
if (!p)
|
|
|
|
|
return NULL;
|
|
|
|
|
p++;
|
|
|
|
|
uri_len -= (p - params);
|
|
|
|
|
params = p;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2007-11-01 17:48:15 -04:00
|
|
|
|
2008-04-14 14:47:37 -04:00
|
|
|
/*
|
|
|
|
|
* this does the same as the previous server_ph, but check the body contents
|
|
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_ph_post(struct stream *s, const struct server *avoid)
|
2008-04-14 14:47:37 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash = 0;
|
2014-11-27 14:45:39 -05:00
|
|
|
struct channel *req = &s->req;
|
2008-04-14 14:47:37 -04:00
|
|
|
struct proxy *px = s->be;
|
2019-07-15 09:37:57 -04:00
|
|
|
struct htx *htx = htxbuf(&req->buf);
|
|
|
|
|
struct htx_blk *blk;
|
2019-01-14 09:23:54 -05:00
|
|
|
unsigned int plen = px->lbprm.arg_len;
|
2019-02-04 06:02:18 -05:00
|
|
|
unsigned long len;
|
|
|
|
|
const char *params, *p, *start, *end;
|
2008-04-14 14:47:37 -04:00
|
|
|
|
2019-02-04 06:02:18 -05:00
|
|
|
if (px->lbprm.tot_weight == 0)
|
2008-04-14 14:47:37 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
p = params = NULL;
|
|
|
|
|
len = 0;
|
|
|
|
|
for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
|
|
|
|
|
enum htx_blk_type type = htx_get_blk_type(blk);
|
|
|
|
|
struct ist v;
|
2015-05-01 18:05:47 -04:00
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
if (type != HTX_BLK_DATA)
|
|
|
|
|
continue;
|
|
|
|
|
v = htx_get_blk_value(htx, blk);
|
|
|
|
|
p = params = v.ptr;
|
|
|
|
|
len = v.len;
|
|
|
|
|
break;
|
2019-02-04 06:02:18 -05:00
|
|
|
}
|
2008-04-14 14:47:37 -04:00
|
|
|
|
|
|
|
|
while (len > plen) {
|
|
|
|
|
/* Look for the parameter name followed by an equal symbol */
|
|
|
|
|
if (params[plen] == '=') {
|
2019-01-14 09:23:54 -05:00
|
|
|
if (memcmp(params, px->lbprm.arg_str, plen) == 0) {
|
2008-04-14 14:47:37 -04:00
|
|
|
/* OK, we have the parameter here at <params>, and
|
|
|
|
|
* the value after the equal sign, at <p>
|
|
|
|
|
* skip the equal symbol
|
|
|
|
|
*/
|
|
|
|
|
p += plen + 1;
|
2013-10-29 23:30:51 -04:00
|
|
|
start = end = p;
|
2008-04-14 14:47:37 -04:00
|
|
|
len -= plen + 1;
|
|
|
|
|
|
2013-10-29 23:30:51 -04:00
|
|
|
while (len && *end != '&') {
|
2008-04-14 14:47:37 -04:00
|
|
|
if (unlikely(!HTTP_IS_TOKEN(*p))) {
|
2009-12-06 13:18:09 -05:00
|
|
|
/* if in a POST, body must be URI encoded or it's not a URI.
|
2013-10-29 23:30:51 -04:00
|
|
|
* Do not interpret any possible binary data as a parameter.
|
2009-12-06 13:18:09 -05:00
|
|
|
*/
|
2008-04-14 14:47:37 -04:00
|
|
|
if (likely(HTTP_IS_LWS(*p))) /* eol, uncertain uri len */
|
|
|
|
|
break;
|
|
|
|
|
return NULL; /* oh, no; this is not uri-encoded.
|
|
|
|
|
* This body does not contain parameters.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
len--;
|
2013-10-29 23:30:51 -04:00
|
|
|
end++;
|
2008-04-14 14:47:37 -04:00
|
|
|
/* should we break if vlen exceeds limit? */
|
|
|
|
|
}
|
2013-10-29 23:30:51 -04:00
|
|
|
hash = gen_hash(px, start, (end - start));
|
|
|
|
|
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
2008-04-14 14:47:37 -04:00
|
|
|
}
|
|
|
|
|
}
|
2007-11-01 17:48:15 -04:00
|
|
|
/* skip to next parameter */
|
2008-04-14 14:47:37 -04:00
|
|
|
p = memchr(params, '&', len);
|
2007-11-01 17:48:15 -04:00
|
|
|
if (!p)
|
|
|
|
|
return NULL;
|
|
|
|
|
p++;
|
2008-04-14 14:47:37 -04:00
|
|
|
len -= (p - params);
|
|
|
|
|
params = p;
|
2007-11-01 17:48:15 -04:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2008-04-14 14:47:37 -04:00
|
|
|
|
2009-03-25 08:02:10 -04:00
|
|
|
/*
|
|
|
|
|
* This function tries to find a running server for the proxy <px> following
|
|
|
|
|
* the Header parameter hash method. It looks for a specific parameter in the
|
|
|
|
|
* URL and hashes it to compute the server ID. This is useful to optimize
|
|
|
|
|
* performance by avoiding bounces between servers in contexts where sessions
|
|
|
|
|
* are shared but cookies are not usable. If the parameter is not found, NULL
|
|
|
|
|
* is returned. If any server is found, it will be returned. If no valid server
|
2019-01-14 10:04:54 -05:00
|
|
|
* is found, NULL is returned. When lbprm.arg_opt1 is set, the hash will only
|
|
|
|
|
* apply to the middle part of a domain name ("use_domain_only" option).
|
2009-03-25 08:02:10 -04:00
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_hh(struct stream *s, const struct server *avoid)
|
2009-03-25 08:02:10 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash = 0;
|
2009-03-25 08:02:10 -04:00
|
|
|
struct proxy *px = s->be;
|
2019-01-14 09:28:53 -05:00
|
|
|
unsigned int plen = px->lbprm.arg_len;
|
2009-03-25 08:02:10 -04:00
|
|
|
unsigned long len;
|
|
|
|
|
const char *p;
|
2013-10-29 23:30:51 -04:00
|
|
|
const char *start, *end;
|
2019-07-15 09:37:57 -04:00
|
|
|
struct htx *htx = htxbuf(&s->req.buf);
|
|
|
|
|
struct http_hdr_ctx ctx = { .blk = NULL };
|
2009-03-25 08:02:10 -04:00
|
|
|
|
|
|
|
|
/* tot_weight appears to mean srv_count */
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
/* note: we won't hash if there's only one server left */
|
|
|
|
|
if (px->lbprm.tot_used == 1)
|
|
|
|
|
goto hash_done;
|
|
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
http_find_header(htx, ist2(px->lbprm.arg_str, plen), &ctx, 0);
|
2019-02-04 06:02:18 -05:00
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
/* if the header is not found or empty, let's fallback to round robin */
|
|
|
|
|
if (!ctx.blk || !ctx.value.len)
|
|
|
|
|
return NULL;
|
2019-02-04 06:02:18 -05:00
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
/* Found a the param_name in the headers.
|
|
|
|
|
* we will compute the hash based on this value ctx.val.
|
|
|
|
|
*/
|
|
|
|
|
len = ctx.value.len;
|
|
|
|
|
p = ctx.value.ptr;
|
2019-02-04 06:02:18 -05:00
|
|
|
|
2019-01-14 10:04:54 -05:00
|
|
|
if (!px->lbprm.arg_opt1) {
|
2013-10-29 23:30:51 -04:00
|
|
|
hash = gen_hash(px, p, len);
|
2009-03-25 08:02:10 -04:00
|
|
|
} else {
|
|
|
|
|
int dohash = 0;
|
2015-01-04 09:17:36 -05:00
|
|
|
p += len;
|
2009-03-25 08:02:10 -04:00
|
|
|
/* special computation, use only main domain name, not tld/host
|
|
|
|
|
* going back from the end of string, start hashing at first
|
|
|
|
|
* dot stop at next.
|
|
|
|
|
* This is designed to work with the 'Host' header, and requires
|
|
|
|
|
* a special option to activate this.
|
|
|
|
|
*/
|
2015-01-04 09:17:36 -05:00
|
|
|
end = p;
|
2009-03-25 08:02:10 -04:00
|
|
|
while (len) {
|
2015-01-04 09:17:36 -05:00
|
|
|
if (dohash) {
|
|
|
|
|
/* Rewind the pointer until the previous char
|
|
|
|
|
* is a dot, this will allow to set the start
|
|
|
|
|
* position of the domain. */
|
|
|
|
|
if (*(p - 1) == '.')
|
2009-03-25 08:02:10 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2015-01-04 09:17:36 -05:00
|
|
|
else if (*p == '.') {
|
|
|
|
|
/* The pointer is rewinded to the dot before the
|
|
|
|
|
* tld, we memorize the end of the domain and
|
|
|
|
|
* can enter the domain processing. */
|
|
|
|
|
end = p;
|
|
|
|
|
dohash = 1;
|
|
|
|
|
}
|
2009-03-25 08:02:10 -04:00
|
|
|
p--;
|
2015-01-04 09:17:36 -05:00
|
|
|
len--;
|
2009-03-25 08:02:10 -04:00
|
|
|
}
|
2015-01-04 09:17:36 -05:00
|
|
|
start = p;
|
2013-10-29 23:30:51 -04:00
|
|
|
hash = gen_hash(px, start, (end - start));
|
2009-03-25 08:02:10 -04:00
|
|
|
}
|
2023-10-11 05:35:48 -04:00
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
hash_done:
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
2009-03-25 08:02:10 -04:00
|
|
|
}
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* RDP Cookie HASH. */
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_rch(struct stream *s, const struct server *avoid)
|
2009-06-30 11:56:00 -04:00
|
|
|
{
|
2014-07-08 11:51:03 -04:00
|
|
|
unsigned int hash = 0;
|
2009-06-30 11:56:00 -04:00
|
|
|
struct proxy *px = s->be;
|
|
|
|
|
unsigned long len;
|
|
|
|
|
int ret;
|
2012-04-23 10:16:37 -04:00
|
|
|
struct sample smp;
|
BUG/MAJOR: fix regression on content-based hashing and http-send-name-header
The recent split between the buffers and HTTP messages in 1.5-dev9 caused
a major trouble : in the past, we used to keep a pointer to HTTP data in the
buffer struct itself, which was the cause of most of the pain we had to deal
with buffers.
Now the two are split but we lost the information about the beginning of
the HTTP message once it's being forwarded. While it seems normal, it happens
that several parts of the code currently rely on this ability to inspect a
buffer containing old contents :
- balance uri
- balance url_param
- balance url_param check_post
- balance hdr()
- balance rdp-cookie()
- http-send-name-header
All these happen after the data are scheduled for being forwarded, which
also causes a server to be selected. So for a long time we've been relying
on supposedly sent data that we still had a pointer to.
Now that we don't have such a pointer anymore, we only have one possibility :
when we need to inspect such data, we have to rewind the buffer so that ->p
points to where it previously was. We're lucky, no data can leave the buffer
before it's being connecting outside, and since no inspection can begin until
it's empty, we know that the skipped data are exactly ->o. So we rewind the
buffer by ->o to get headers and advance it back by the same amount.
Proceeding this way is particularly important when dealing with chunked-
encoded requests, because the ->som and ->sov fields may be reused by the
chunk parser before the connection attempt is made, so we cannot rely on
them.
Also, we need to be able to come back after retries and redispatches, which
might change the size of the request if http-send-name-header is set. All of
this is accounted for by the output queue so in the end it does not look like
a bad solution.
No backport is needed.
2012-05-18 16:12:14 -04:00
|
|
|
int rewind;
|
2009-06-30 11:56:00 -04:00
|
|
|
|
|
|
|
|
/* tot_weight appears to mean srv_count */
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
memset(&smp, 0, sizeof(smp));
|
2009-06-30 11:56:00 -04:00
|
|
|
|
2018-06-19 01:04:45 -04:00
|
|
|
rewind = co_data(&s->req);
|
|
|
|
|
c_rew(&s->req, rewind);
|
BUG/MAJOR: fix regression on content-based hashing and http-send-name-header
The recent split between the buffers and HTTP messages in 1.5-dev9 caused
a major trouble : in the past, we used to keep a pointer to HTTP data in the
buffer struct itself, which was the cause of most of the pain we had to deal
with buffers.
Now the two are split but we lost the information about the beginning of
the HTTP message once it's being forwarded. While it seems normal, it happens
that several parts of the code currently rely on this ability to inspect a
buffer containing old contents :
- balance uri
- balance url_param
- balance url_param check_post
- balance hdr()
- balance rdp-cookie()
- http-send-name-header
All these happen after the data are scheduled for being forwarded, which
also causes a server to be selected. So for a long time we've been relying
on supposedly sent data that we still had a pointer to.
Now that we don't have such a pointer anymore, we only have one possibility :
when we need to inspect such data, we have to rewind the buffer so that ->p
points to where it previously was. We're lucky, no data can leave the buffer
before it's being connecting outside, and since no inspection can begin until
it's empty, we know that the skipped data are exactly ->o. So we rewind the
buffer by ->o to get headers and advance it back by the same amount.
Proceeding this way is particularly important when dealing with chunked-
encoded requests, because the ->som and ->sov fields may be reused by the
chunk parser before the connection attempt is made, so we cannot rely on
them.
Also, we need to be able to come back after retries and redispatches, which
might change the size of the request if http-send-name-header is set. All of
this is accounted for by the output queue so in the end it does not look like
a bad solution.
No backport is needed.
2012-05-18 16:12:14 -04:00
|
|
|
|
2019-01-14 09:28:53 -05:00
|
|
|
ret = fetch_rdp_cookie_name(s, &smp, px->lbprm.arg_str, px->lbprm.arg_len);
|
2018-07-13 04:54:26 -04:00
|
|
|
len = smp.data.u.str.data;
|
2011-12-16 13:11:42 -05:00
|
|
|
|
2018-06-06 01:13:22 -04:00
|
|
|
c_adv(&s->req, rewind);
|
BUG/MAJOR: fix regression on content-based hashing and http-send-name-header
The recent split between the buffers and HTTP messages in 1.5-dev9 caused
a major trouble : in the past, we used to keep a pointer to HTTP data in the
buffer struct itself, which was the cause of most of the pain we had to deal
with buffers.
Now the two are split but we lost the information about the beginning of
the HTTP message once it's being forwarded. While it seems normal, it happens
that several parts of the code currently rely on this ability to inspect a
buffer containing old contents :
- balance uri
- balance url_param
- balance url_param check_post
- balance hdr()
- balance rdp-cookie()
- http-send-name-header
All these happen after the data are scheduled for being forwarded, which
also causes a server to be selected. So for a long time we've been relying
on supposedly sent data that we still had a pointer to.
Now that we don't have such a pointer anymore, we only have one possibility :
when we need to inspect such data, we have to rewind the buffer so that ->p
points to where it previously was. We're lucky, no data can leave the buffer
before it's being connecting outside, and since no inspection can begin until
it's empty, we know that the skipped data are exactly ->o. So we rewind the
buffer by ->o to get headers and advance it back by the same amount.
Proceeding this way is particularly important when dealing with chunked-
encoded requests, because the ->som and ->sov fields may be reused by the
chunk parser before the connection attempt is made, so we cannot rely on
them.
Also, we need to be able to come back after retries and redispatches, which
might change the size of the request if http-send-name-header is set. All of
this is accounted for by the output queue so in the end it does not look like
a bad solution.
No backport is needed.
2012-05-18 16:12:14 -04:00
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || len == 0)
|
2009-06-30 11:56:00 -04:00
|
|
|
return NULL;
|
|
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
/* note: we won't hash if there's only one server left */
|
|
|
|
|
if (px->lbprm.tot_used == 1)
|
|
|
|
|
goto hash_done;
|
|
|
|
|
|
2019-01-14 09:28:53 -05:00
|
|
|
/* Found the param_name in the headers.
|
2009-06-30 11:56:00 -04:00
|
|
|
* we will compute the hash based on this value ctx.val.
|
|
|
|
|
*/
|
2018-07-13 04:54:26 -04:00
|
|
|
hash = gen_hash(px, smp.data.u.str.area, len);
|
2013-10-29 23:30:51 -04:00
|
|
|
|
2009-10-01 15:11:15 -04:00
|
|
|
hash_done:
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2019-01-02 08:48:31 -05:00
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
2009-06-30 11:56:00 -04:00
|
|
|
}
|
2017-06-09 08:17:53 -04:00
|
|
|
|
2022-04-25 04:25:34 -04:00
|
|
|
/* sample expression HASH. Returns NULL if the sample is not found or if there
|
|
|
|
|
* are no server, relying on the caller to fall back to round robin instead.
|
|
|
|
|
*/
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_expr(struct stream *s, const struct server *avoid)
|
2022-04-25 04:25:34 -04:00
|
|
|
{
|
|
|
|
|
struct proxy *px = s->be;
|
|
|
|
|
struct sample *smp;
|
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* note: no need to hash if there's only one server left */
|
|
|
|
|
if (px->lbprm.tot_used == 1)
|
|
|
|
|
goto hash_done;
|
|
|
|
|
|
|
|
|
|
smp = sample_fetch_as_type(px, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, px->lbprm.expr, SMP_T_BIN);
|
|
|
|
|
if (!smp)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* We have the desired data. Let's hash it according to the configured
|
|
|
|
|
* options and algorithm.
|
|
|
|
|
*/
|
|
|
|
|
hash = gen_hash(px, smp->data.u.str.area, smp->data.u.str.data);
|
|
|
|
|
|
|
|
|
|
hash_done:
|
|
|
|
|
if ((px->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
|
|
|
|
return chash_get_server_hash(px, hash, avoid);
|
|
|
|
|
else
|
|
|
|
|
return map_get_server_hash(px, hash);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-03 01:20:40 -04:00
|
|
|
/* random value */
|
2023-12-27 08:08:08 -05:00
|
|
|
struct server *get_server_rnd(struct stream *s, const struct server *avoid)
|
2018-05-03 01:20:40 -04:00
|
|
|
{
|
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
struct proxy *px = s->be;
|
2019-01-14 12:14:27 -05:00
|
|
|
struct server *prev, *curr;
|
|
|
|
|
int draws = px->lbprm.arg_opt1; // number of draws
|
2018-05-03 01:20:40 -04:00
|
|
|
|
|
|
|
|
/* tot_weight appears to mean srv_count */
|
|
|
|
|
if (px->lbprm.tot_weight == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2019-01-14 12:14:27 -05:00
|
|
|
curr = NULL;
|
|
|
|
|
do {
|
|
|
|
|
prev = curr;
|
2021-03-01 02:57:54 -05:00
|
|
|
hash = statistical_prng();
|
2019-01-14 12:14:27 -05:00
|
|
|
curr = chash_get_server_hash(px, hash, avoid);
|
|
|
|
|
if (!curr)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* compare the new server to the previous best choice and pick
|
|
|
|
|
* the one with the least currently served requests.
|
|
|
|
|
*/
|
|
|
|
|
if (prev && prev != curr &&
|
|
|
|
|
curr->served * prev->cur_eweight > prev->served * curr->cur_eweight)
|
|
|
|
|
curr = prev;
|
|
|
|
|
} while (--draws > 0);
|
|
|
|
|
|
2020-09-29 10:58:30 -04:00
|
|
|
/* if the selected server is full, pretend we have none so that we reach
|
|
|
|
|
* the backend's queue instead.
|
|
|
|
|
*/
|
|
|
|
|
if (curr &&
|
2025-01-15 10:37:35 -05:00
|
|
|
(curr->queueslength || (curr->maxconn && curr->served >= srv_dynamic_maxconn(curr))))
|
2020-09-29 10:58:30 -04:00
|
|
|
curr = NULL;
|
|
|
|
|
|
2019-01-14 12:14:27 -05:00
|
|
|
return curr;
|
2018-05-03 01:20:40 -04:00
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* This function applies the load-balancing algorithm to the stream, as
|
|
|
|
|
* defined by the backend it is assigned to. The stream is then marked as
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* 'assigned'.
|
|
|
|
|
*
|
2015-04-02 19:14:29 -04:00
|
|
|
* This function MAY NOT be called with SF_ASSIGNED already set. If the stream
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* had a server previously assigned, it is rebalanced, trying to avoid the same
|
2011-03-10 10:55:02 -05:00
|
|
|
* server, which should still be present in target_srv(&s->target) before the call.
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* The function tries to keep the original connection slot if it reconnects to
|
|
|
|
|
* the same server, otherwise it releases it and tries to offer it.
|
|
|
|
|
*
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* It is illegal to call this function with a stream in a queue.
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* It may return :
|
2011-03-10 05:38:29 -05:00
|
|
|
* SRV_STATUS_OK if everything is OK. ->srv and ->target are assigned.
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* SRV_STATUS_NOSRV if no server is available. Stream is not ASSIGNED
|
|
|
|
|
* SRV_STATUS_FULL if all servers are saturated. Stream is not ASSIGNED
|
2006-06-25 20:48:02 -04:00
|
|
|
* SRV_STATUS_INTERNAL for other unrecoverable errors.
|
|
|
|
|
*
|
2015-04-02 19:14:29 -04:00
|
|
|
* Upon successful return, the stream flag SF_ASSIGNED is set to indicate that
|
2011-03-10 10:55:02 -05:00
|
|
|
* it does not need to be called anymore. This means that target_srv(&s->target)
|
|
|
|
|
* can be trusted in balance and direct modes.
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
int assign_server(struct stream *s)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2018-12-01 08:40:40 -05:00
|
|
|
struct connection *conn = NULL;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
struct server *conn_slot;
|
2018-11-30 11:24:55 -05:00
|
|
|
struct server *srv = NULL, *prev_srv;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
int err;
|
[MEDIUM]: Prevent redispatcher from selecting the same server, version #3
When haproxy decides that session needs to be redispatched it chose a server,
but there is no guarantee for it to be a different one. So, it often
happens that selected server is exactly the same that it was previously, so
a client ends up with a 503 error anyway, especially when one sever has
much bigger weight than others.
Changes from the previous version:
- drop stupid and unnecessary SN_DIRECT changes
- assign_server(): use srvtoavoid to keep the old server and clear s->srv
so SRV_STATUS_NOSRV guarantees that t->srv == NULL (again)
and get_server_rr_with_conns has chances to work (previously
we were passing a NULL here)
- srv_redispatch_connect(): remove t->srv->cum_sess and t->srv->failed_conns
incrementing as t->srv was guaranteed to be NULL
- add avoididx to get_server_rr_with_conns. I hope I correctly understand this code.
- fix http_flush_cookie_flags() and move it to assign_server_and_queue()
directly. The code here was supposed to set CK_DOWN and clear CK_VALID,
but: (TX_CK_VALID | TX_CK_DOWN) == TX_CK_VALID == TX_CK_MASK so:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags ^= (TX_CK_VALID | TX_CK_DOWN);
was really a:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags &= TX_CK_VALID
Now haproxy logs "--DI" after redispatching connection.
- defer srv->redispatches++ and s->be->redispatches++ so there
are called only if a conenction was redispatched, not only
supposed to.
- don't increment lbconn if redispatcher selected the same sarver
- don't count unsuccessfully redispatched connections as redispatched
connections
- don't count redispatched connections as errors, so:
- the number of connections effectively served by a server is:
srv->cum_sess - srv->failed_conns - srv->retries - srv->redispatches
and
SUM(servers->failed_conns) == be->failed_conns
- requires the "Don't increment server connections too much + fix retries" patch
- needs little more testing and probably some discussion so reverting to the RFC state
Tests #1:
retries 4
redispatch
i) 1 server(s): b (wght=1, down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request failed
ii) server(s): b (wght=1, down), u (wght=1, down)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=1, retr=0, redis=0
-> request FAILED
iii) 2 server(s): b (wght=1, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
iv) 2 server(s): b (wght=100, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
v) 1 server(s): b (down for first 4 SYNS)
b) sessions=5, lbtot=1, err_conn=0, retr=4, redis=0
-> request OK
Tests #2:
retries 4
i) 1 server(s): b (down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request FAILED
2008-02-21 21:50:19 -05:00
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = SRV_STATUS_INTERNAL;
|
2015-04-02 19:14:29 -04:00
|
|
|
if (unlikely(s->pend_pos || s->flags & SF_ASSIGNED))
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
goto out_err;
|
[MEDIUM]: Prevent redispatcher from selecting the same server, version #3
When haproxy decides that session needs to be redispatched it chose a server,
but there is no guarantee for it to be a different one. So, it often
happens that selected server is exactly the same that it was previously, so
a client ends up with a 503 error anyway, especially when one sever has
much bigger weight than others.
Changes from the previous version:
- drop stupid and unnecessary SN_DIRECT changes
- assign_server(): use srvtoavoid to keep the old server and clear s->srv
so SRV_STATUS_NOSRV guarantees that t->srv == NULL (again)
and get_server_rr_with_conns has chances to work (previously
we were passing a NULL here)
- srv_redispatch_connect(): remove t->srv->cum_sess and t->srv->failed_conns
incrementing as t->srv was guaranteed to be NULL
- add avoididx to get_server_rr_with_conns. I hope I correctly understand this code.
- fix http_flush_cookie_flags() and move it to assign_server_and_queue()
directly. The code here was supposed to set CK_DOWN and clear CK_VALID,
but: (TX_CK_VALID | TX_CK_DOWN) == TX_CK_VALID == TX_CK_MASK so:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags ^= (TX_CK_VALID | TX_CK_DOWN);
was really a:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags &= TX_CK_VALID
Now haproxy logs "--DI" after redispatching connection.
- defer srv->redispatches++ and s->be->redispatches++ so there
are called only if a conenction was redispatched, not only
supposed to.
- don't increment lbconn if redispatcher selected the same sarver
- don't count unsuccessfully redispatched connections as redispatched
connections
- don't count redispatched connections as errors, so:
- the number of connections effectively served by a server is:
srv->cum_sess - srv->failed_conns - srv->retries - srv->redispatches
and
SUM(servers->failed_conns) == be->failed_conns
- requires the "Don't increment server connections too much + fix retries" patch
- needs little more testing and probably some discussion so reverting to the RFC state
Tests #1:
retries 4
redispatch
i) 1 server(s): b (wght=1, down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request failed
ii) server(s): b (wght=1, down), u (wght=1, down)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=1, retr=0, redis=0
-> request FAILED
iii) 2 server(s): b (wght=1, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
iv) 2 server(s): b (wght=100, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
v) 1 server(s): b (down for first 4 SYNS)
b) sessions=5, lbtot=1, err_conn=0, retr=4, redis=0
-> request OK
Tests #2:
retries 4
i) 1 server(s): b (down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request FAILED
2008-02-21 21:50:19 -05:00
|
|
|
|
2012-11-11 18:42:33 -05:00
|
|
|
prev_srv = objt_server(s->target);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
conn_slot = s->srv_conn;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
/* We have to release any connection slot before applying any LB algo,
|
|
|
|
|
* otherwise we may erroneously end up with no available slot.
|
|
|
|
|
*/
|
|
|
|
|
if (conn_slot)
|
|
|
|
|
sess_change_server(s, NULL);
|
|
|
|
|
|
2012-11-11 18:42:33 -05:00
|
|
|
/* We will now try to find the good server and store it into <objt_server(s->target)>.
|
|
|
|
|
* Note that <objt_server(s->target)> may be NULL in case of dispatch or proxy mode,
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* as well as if no server is available (check error code).
|
|
|
|
|
*/
|
2007-11-01 16:08:19 -04:00
|
|
|
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = NULL;
|
2012-11-11 18:42:33 -05:00
|
|
|
s->target = NULL;
|
2018-11-30 11:24:55 -05:00
|
|
|
|
2018-12-01 08:40:40 -05:00
|
|
|
if ((s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI &&
|
2019-05-29 09:01:50 -04:00
|
|
|
((s->sess->flags & SESS_FL_PREFER_LAST) ||
|
2018-12-01 08:40:40 -05:00
|
|
|
(s->be->options & PR_O_PREF_LAST))) {
|
2024-03-14 06:24:10 -04:00
|
|
|
struct sess_priv_conns *pconns;
|
|
|
|
|
list_for_each_entry(pconns, &s->sess->priv_conns, sess_el) {
|
|
|
|
|
struct server *tmpsrv = objt_server(pconns->target);
|
2018-11-30 11:24:55 -05:00
|
|
|
|
2018-12-01 08:40:40 -05:00
|
|
|
if (tmpsrv && tmpsrv->proxy == s->be &&
|
2019-05-29 09:01:50 -04:00
|
|
|
((s->sess->flags & SESS_FL_PREFER_LAST) ||
|
2018-12-01 08:40:40 -05:00
|
|
|
(!s->be->max_ka_queue ||
|
|
|
|
|
server_has_room(tmpsrv) || (
|
2025-01-15 10:37:35 -05:00
|
|
|
tmpsrv->queueslength + 1 < s->be->max_ka_queue))) &&
|
2018-12-01 08:40:40 -05:00
|
|
|
srv_currently_usable(tmpsrv)) {
|
2024-03-14 06:24:10 -04:00
|
|
|
list_for_each_entry(conn, &pconns->conn_list, sess_el) {
|
2020-01-23 10:27:54 -05:00
|
|
|
if (!(conn->flags & CO_FL_WAIT_XPRT)) {
|
2018-12-01 08:40:40 -05:00
|
|
|
srv = tmpsrv;
|
|
|
|
|
s->target = &srv->obj_type;
|
2020-07-01 12:56:30 -04:00
|
|
|
if (conn->flags & CO_FL_SESS_IDLE) {
|
|
|
|
|
conn->flags &= ~CO_FL_SESS_IDLE;
|
|
|
|
|
s->sess->idle_conns--;
|
|
|
|
|
}
|
2018-12-01 08:40:40 -05:00
|
|
|
goto out_ok;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-30 11:24:55 -05:00
|
|
|
}
|
|
|
|
|
}
|
2013-12-15 12:58:25 -05:00
|
|
|
}
|
2017-06-09 08:17:53 -04:00
|
|
|
|
OPTIM: backend: skip LB when we know the backend is full
For some algos (roundrobin, static-rr, leastconn, first) we know that
if there is any request queued in the backend, it's because a previous
attempt failed at finding a suitable server after trying all of them.
This alone is sufficient to decide that the next request will skip the
LB algo and directly reach the backend's queue. Doing this alone avoids
an O(N) lookup when load-balancing on a saturated farm of N servers,
which starts to be very expensive for hundreds of servers, especially
under the lbprm lock. This change alone has increased the request rate
from 110k to 148k RPS for 200 saturated servers on 8 threads, and
fwlc_reposition_srv() doesn't show up anymore in perf top. See github
issue #880 for more context.
It could have been the same for random, except that random is performed
using a consistent hash and it only considers a small set of servers (2
by default), so it may result in queueing at the backend despite having
some free slots on unknown servers. It's no big deal though since random()
only performs two attempts by default.
For hashing algorithms this is pointless since we don't queue at the
backend, except when there's no hash key found, which is the least of
our concerns here.
2020-09-29 11:07:21 -04:00
|
|
|
if (s->be->lbprm.algo & BE_LB_KIND) {
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
/* we must check if we have at least one server available */
|
|
|
|
|
if (!s->be->lbprm.tot_weight) {
|
|
|
|
|
err = SRV_STATUS_NOSRV;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
OPTIM: backend: skip LB when we know the backend is full
For some algos (roundrobin, static-rr, leastconn, first) we know that
if there is any request queued in the backend, it's because a previous
attempt failed at finding a suitable server after trying all of them.
This alone is sufficient to decide that the next request will skip the
LB algo and directly reach the backend's queue. Doing this alone avoids
an O(N) lookup when load-balancing on a saturated farm of N servers,
which starts to be very expensive for hundreds of servers, especially
under the lbprm lock. This change alone has increased the request rate
from 110k to 148k RPS for 200 saturated servers on 8 threads, and
fwlc_reposition_srv() doesn't show up anymore in perf top. See github
issue #880 for more context.
It could have been the same for random, except that random is performed
using a consistent hash and it only considers a small set of servers (2
by default), so it may result in queueing at the backend despite having
some free slots on unknown servers. It's no big deal though since random()
only performs two attempts by default.
For hashing algorithms this is pointless since we don't queue at the
backend, except when there's no hash key found, which is the least of
our concerns here.
2020-09-29 11:07:21 -04:00
|
|
|
/* if there's some queue on the backend, with certain algos we
|
|
|
|
|
* know it's because all servers are full.
|
|
|
|
|
*/
|
2025-01-15 10:37:35 -05:00
|
|
|
if (s->be->queueslength && s->be->served && s->be->queueslength != s->be->beconn &&
|
2020-10-22 11:19:07 -04:00
|
|
|
(((s->be->lbprm.algo & (BE_LB_KIND|BE_LB_NEED|BE_LB_PARM)) == BE_LB_ALGO_FAS)|| // first
|
OPTIM: backend: skip LB when we know the backend is full
For some algos (roundrobin, static-rr, leastconn, first) we know that
if there is any request queued in the backend, it's because a previous
attempt failed at finding a suitable server after trying all of them.
This alone is sufficient to decide that the next request will skip the
LB algo and directly reach the backend's queue. Doing this alone avoids
an O(N) lookup when load-balancing on a saturated farm of N servers,
which starts to be very expensive for hundreds of servers, especially
under the lbprm lock. This change alone has increased the request rate
from 110k to 148k RPS for 200 saturated servers on 8 threads, and
fwlc_reposition_srv() doesn't show up anymore in perf top. See github
issue #880 for more context.
It could have been the same for random, except that random is performed
using a consistent hash and it only considers a small set of servers (2
by default), so it may result in queueing at the backend despite having
some free slots on unknown servers. It's no big deal though since random()
only performs two attempts by default.
For hashing algorithms this is pointless since we don't queue at the
backend, except when there's no hash key found, which is the least of
our concerns here.
2020-09-29 11:07:21 -04:00
|
|
|
((s->be->lbprm.algo & (BE_LB_KIND|BE_LB_NEED|BE_LB_PARM)) == BE_LB_ALGO_RR) || // roundrobin
|
|
|
|
|
((s->be->lbprm.algo & (BE_LB_KIND|BE_LB_NEED|BE_LB_PARM)) == BE_LB_ALGO_SRR))) { // static-rr
|
|
|
|
|
err = SRV_STATUS_FULL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
/* First check whether we need to fetch some data or simply call
|
|
|
|
|
* the LB lookup function. Only the hashing functions will need
|
|
|
|
|
* some input data in fact, and will support multiple algorithms.
|
|
|
|
|
*/
|
|
|
|
|
switch (s->be->lbprm.algo & BE_LB_LKUP) {
|
|
|
|
|
case BE_LB_LKUP_RRTREE:
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = fwrr_get_next_server(s->be, prev_srv);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
break;
|
2009-10-03 06:36:05 -04:00
|
|
|
|
2012-02-13 11:12:08 -05:00
|
|
|
case BE_LB_LKUP_FSTREE:
|
|
|
|
|
srv = fas_get_next_server(s->be, prev_srv);
|
|
|
|
|
break;
|
|
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
case BE_LB_LKUP_LCTREE:
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = fwlc_get_next_server(s->be, prev_srv);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
break;
|
2009-10-03 06:36:05 -04:00
|
|
|
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
case BE_LB_LKUP_CHTREE:
|
2009-10-03 06:36:05 -04:00
|
|
|
case BE_LB_LKUP_MAP:
|
2009-10-03 06:56:50 -04:00
|
|
|
if ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_RR) {
|
2021-06-22 11:31:51 -04:00
|
|
|
/* static-rr (map) or random (chash) */
|
2018-05-03 01:20:40 -04:00
|
|
|
if ((s->be->lbprm.algo & BE_LB_PARM) == BE_LB_RR_RANDOM)
|
2019-01-02 08:48:31 -05:00
|
|
|
srv = get_server_rnd(s, prev_srv);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = map_get_server_rr(s->be, prev_srv);
|
2009-10-03 06:56:50 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if ((s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI) {
|
2009-10-03 06:36:05 -04:00
|
|
|
/* unknown balancing algorithm */
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = SRV_STATUS_INTERNAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
|
|
|
|
|
switch (s->be->lbprm.algo & BE_LB_PARM) {
|
2021-10-25 02:13:25 -04:00
|
|
|
const struct sockaddr_storage *src;
|
|
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
case BE_LB_HASH_SRC:
|
2022-05-27 02:57:21 -04:00
|
|
|
src = sc_src(s->scf);
|
2021-10-25 02:13:25 -04:00
|
|
|
if (src && src->ss_family == AF_INET) {
|
2012-03-31 13:53:37 -04:00
|
|
|
srv = get_server_sh(s->be,
|
2021-10-25 02:13:25 -04:00
|
|
|
(void *)&((struct sockaddr_in *)src)->sin_addr,
|
2019-01-02 08:48:31 -05:00
|
|
|
4, prev_srv);
|
2012-03-31 13:53:37 -04:00
|
|
|
}
|
2021-10-25 02:13:25 -04:00
|
|
|
else if (src && src->ss_family == AF_INET6) {
|
2012-03-31 13:53:37 -04:00
|
|
|
srv = get_server_sh(s->be,
|
2021-10-25 02:13:25 -04:00
|
|
|
(void *)&((struct sockaddr_in6 *)src)->sin6_addr,
|
2019-01-02 08:48:31 -05:00
|
|
|
16, prev_srv);
|
2012-03-31 13:53:37 -04:00
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BE_LB_HASH_URI:
|
|
|
|
|
/* URI hashing */
|
2019-07-15 09:37:57 -04:00
|
|
|
if (IS_HTX_STRM(s) && s->txn->req.msg_state >= HTTP_MSG_BODY) {
|
2019-02-04 06:02:18 -05:00
|
|
|
struct ist uri;
|
|
|
|
|
|
2019-05-13 08:41:27 -04:00
|
|
|
uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
|
2020-09-23 02:56:29 -04:00
|
|
|
if (s->be->lbprm.arg_opt1 & 2) {
|
2021-07-06 05:40:12 -04:00
|
|
|
struct http_uri_parser parser =
|
|
|
|
|
http_uri_parser_init(uri);
|
|
|
|
|
|
|
|
|
|
uri = http_parse_path(&parser);
|
2021-03-02 12:57:28 -05:00
|
|
|
if (!isttest(uri))
|
2020-09-23 02:56:29 -04:00
|
|
|
uri = ist("");
|
|
|
|
|
}
|
2019-02-04 06:02:18 -05:00
|
|
|
srv = get_server_uh(s->be, uri.ptr, uri.len, prev_srv);
|
|
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
break;
|
2008-04-14 14:47:37 -04:00
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
case BE_LB_HASH_PRM:
|
|
|
|
|
/* URL Parameter hashing */
|
2019-07-15 09:37:57 -04:00
|
|
|
if (IS_HTX_STRM(s) && s->txn->req.msg_state >= HTTP_MSG_BODY) {
|
2019-02-04 06:02:18 -05:00
|
|
|
struct ist uri;
|
|
|
|
|
|
2019-05-13 08:41:27 -04:00
|
|
|
uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
|
2019-02-04 06:02:18 -05:00
|
|
|
srv = get_server_ph(s->be, uri.ptr, uri.len, prev_srv);
|
2011-03-01 14:35:49 -05:00
|
|
|
|
2019-07-15 09:37:57 -04:00
|
|
|
if (!srv && s->txn->meth == HTTP_METH_POST)
|
|
|
|
|
srv = get_server_ph_post(s, prev_srv);
|
|
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
break;
|
2009-03-25 08:02:10 -04:00
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
case BE_LB_HASH_HDR:
|
|
|
|
|
/* Header Parameter hashing */
|
2019-07-15 09:37:57 -04:00
|
|
|
if (IS_HTX_STRM(s) && s->txn->req.msg_state >= HTTP_MSG_BODY)
|
|
|
|
|
srv = get_server_hh(s, prev_srv);
|
2009-10-03 06:36:05 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BE_LB_HASH_RDP:
|
|
|
|
|
/* RDP Cookie hashing */
|
2019-01-02 08:48:31 -05:00
|
|
|
srv = get_server_rch(s, prev_srv);
|
2009-10-03 06:36:05 -04:00
|
|
|
break;
|
|
|
|
|
|
2022-04-25 04:25:34 -04:00
|
|
|
case BE_LB_HASH_SMP:
|
|
|
|
|
/* sample expression hashing */
|
|
|
|
|
srv = get_server_expr(s, prev_srv);
|
|
|
|
|
break;
|
|
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
default:
|
|
|
|
|
/* unknown balancing algorithm */
|
|
|
|
|
err = SRV_STATUS_INTERNAL;
|
|
|
|
|
goto out;
|
2009-06-30 11:56:00 -04:00
|
|
|
}
|
|
|
|
|
|
2009-10-03 06:36:05 -04:00
|
|
|
/* If the hashing parameter was not found, let's fall
|
|
|
|
|
* back to round robin on the map.
|
|
|
|
|
*/
|
2011-03-10 10:55:02 -05:00
|
|
|
if (!srv) {
|
2019-01-14 11:07:39 -05:00
|
|
|
if ((s->be->lbprm.algo & BE_LB_LKUP) == BE_LB_LKUP_CHTREE)
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = chash_get_next_server(s->be, prev_srv);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
else
|
2011-03-10 10:55:02 -05:00
|
|
|
srv = map_get_server_rr(s->be, prev_srv);
|
[MEDIUM] backend: implement consistent hashing variation
Consistent hashing provides some interesting advantages over common
hashing. It avoids full redistribution in case of a server failure,
or when expanding the farm. This has a cost however, the hashing is
far from being perfect, as we associate a server to a request by
searching the server with the closest key in a tree. Since servers
appear multiple times based on their weights, it is recommended to
use weights larger than approximately 10-20 in order to smoothen
the distribution a bit.
In some cases, playing with weights will be the only solution to
make a server appear more often and increase chances of being picked,
so stats are very important with consistent hashing.
In order to indicate the type of hashing, use :
hash-type map-based (default, old one)
hash-type consistent (new one)
Consistent hashing can make sense in a cache farm, in order not
to redistribute everyone when a cache changes state. It could also
probably be used for long sessions such as terminal sessions, though
that has not be attempted yet.
More details on this method of hashing here :
http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/
2009-10-01 01:52:15 -04:00
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
|
|
|
|
|
/* end of map-based LB */
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
break;
|
2009-10-03 06:36:05 -04:00
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
default:
|
2024-03-28 12:24:53 -04:00
|
|
|
if ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_SA) {
|
|
|
|
|
/* some special algos that cannot be grouped together */
|
|
|
|
|
|
|
|
|
|
if ((s->be->lbprm.algo & BE_LB_PARM) == BE_LB_SA_SS)
|
|
|
|
|
srv = ss_get_server(s->be);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
/* unknown balancing algorithm */
|
|
|
|
|
err = SRV_STATUS_INTERNAL;
|
|
|
|
|
goto out;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2009-10-03 06:36:05 -04:00
|
|
|
|
2011-03-10 10:55:02 -05:00
|
|
|
if (!srv) {
|
2009-10-03 06:36:05 -04:00
|
|
|
err = SRV_STATUS_FULL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2011-03-10 10:55:02 -05:00
|
|
|
else if (srv != prev_srv) {
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.cum_lbconn);
|
|
|
|
|
_HA_ATOMIC_INC(&srv->counters.cum_lbconn);
|
2007-11-29 09:43:32 -05:00
|
|
|
}
|
2012-11-11 18:42:33 -05:00
|
|
|
s->target = &srv->obj_type;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
}
|
2011-08-06 11:05:02 -04:00
|
|
|
else if (s->be->options & (PR_O_DISPATCH | PR_O_TRANSP)) {
|
2012-11-11 18:42:33 -05:00
|
|
|
s->target = &s->be->obj_type;
|
2011-03-10 05:38:29 -05:00
|
|
|
}
|
|
|
|
|
else {
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = SRV_STATUS_NOSRV;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 11:24:55 -05:00
|
|
|
out_ok:
|
2015-04-02 19:14:29 -04:00
|
|
|
s->flags |= SF_ASSIGNED;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = SRV_STATUS_OK;
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
/* Either we take back our connection slot, or we offer it to someone
|
|
|
|
|
* else if we don't need it anymore.
|
|
|
|
|
*/
|
|
|
|
|
if (conn_slot) {
|
2011-03-10 10:55:02 -05:00
|
|
|
if (conn_slot == srv) {
|
|
|
|
|
sess_change_server(s, srv);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
} else {
|
|
|
|
|
if (may_dequeue_tasks(conn_slot, s->be))
|
2021-06-22 12:47:51 -04:00
|
|
|
process_srv_queue(conn_slot);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
|
return err;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2021-01-11 09:24:31 -05:00
|
|
|
/* Allocate an address for the destination endpoint
|
2006-06-25 20:48:02 -04:00
|
|
|
* The address is taken from the currently assigned server, or from the
|
|
|
|
|
* dispatch or transparent address.
|
|
|
|
|
*
|
2022-05-02 10:20:36 -04:00
|
|
|
* Returns SRV_STATUS_OK on success. Does nothing if the address was
|
|
|
|
|
* already set.
|
2021-01-21 03:40:19 -05:00
|
|
|
* On error, no address is allocated and SRV_STATUS_INTERNAL is returned.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
2021-01-11 09:24:31 -05:00
|
|
|
static int alloc_dst_address(struct sockaddr_storage **ss,
|
|
|
|
|
struct server *srv, struct stream *s)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2021-10-25 02:13:25 -04:00
|
|
|
const struct sockaddr_storage *dst;
|
2013-10-01 04:45:07 -04:00
|
|
|
|
2022-05-02 10:20:36 -04:00
|
|
|
if (*ss)
|
|
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
|
2015-04-02 19:14:29 -04:00
|
|
|
if ((s->flags & SF_DIRECT) || (s->be->lbprm.algo & BE_LB_KIND)) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* A server is necessarily known for this stream */
|
2015-04-02 19:14:29 -04:00
|
|
|
if (!(s->flags & SF_ASSIGNED))
|
2006-06-25 20:48:02 -04:00
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
2021-01-11 09:24:31 -05:00
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
|
|
|
|
**ss = srv->addr;
|
|
|
|
|
set_host_port(*ss, srv->svc_port);
|
2021-11-05 07:02:56 -04:00
|
|
|
if (!is_addr(*ss)) {
|
2010-07-13 08:49:50 -04:00
|
|
|
/* if the server has no address, we use the same address
|
|
|
|
|
* the client asked, which is handy for remapping ports
|
2014-05-09 16:56:10 -04:00
|
|
|
* locally on multiple addresses at once. Nothing is done
|
|
|
|
|
* for AF_UNIX addresses.
|
2010-07-13 08:49:50 -04:00
|
|
|
*/
|
2022-05-27 02:57:21 -04:00
|
|
|
dst = sc_dst(s->scf);
|
2021-11-05 07:02:56 -04:00
|
|
|
if (dst && dst->ss_family == AF_INET) {
|
2021-03-01 05:33:59 -05:00
|
|
|
((struct sockaddr_in *)*ss)->sin_family = AF_INET;
|
2021-01-11 09:24:31 -05:00
|
|
|
((struct sockaddr_in *)*ss)->sin_addr =
|
2021-10-25 02:13:25 -04:00
|
|
|
((struct sockaddr_in *)dst)->sin_addr;
|
2021-11-05 07:02:56 -04:00
|
|
|
} else if (dst && dst->ss_family == AF_INET6) {
|
2021-03-01 05:33:59 -05:00
|
|
|
((struct sockaddr_in6 *)*ss)->sin6_family = AF_INET6;
|
2021-01-11 09:24:31 -05:00
|
|
|
((struct sockaddr_in6 *)*ss)->sin6_addr =
|
2021-10-25 02:13:25 -04:00
|
|
|
((struct sockaddr_in6 *)dst)->sin6_addr;
|
2010-10-22 10:36:33 -04:00
|
|
|
}
|
2010-07-13 08:49:50 -04:00
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* if this server remaps proxied ports, we'll use
|
|
|
|
|
* the port the client connected to with an offset. */
|
2021-11-05 07:02:56 -04:00
|
|
|
if ((srv->flags & SRV_F_MAPPORTS)) {
|
2011-03-10 16:26:24 -05:00
|
|
|
int base_port;
|
|
|
|
|
|
2022-05-27 02:57:21 -04:00
|
|
|
dst = sc_dst(s->scf);
|
2021-11-05 07:02:56 -04:00
|
|
|
if (dst) {
|
|
|
|
|
/* First, retrieve the port from the incoming connection */
|
|
|
|
|
base_port = get_host_port(dst);
|
2011-03-10 16:26:24 -05:00
|
|
|
|
2021-11-05 07:02:56 -04:00
|
|
|
/* Second, assign the outgoing connection's port */
|
|
|
|
|
base_port += get_host_port(*ss);
|
|
|
|
|
set_host_port(*ss, base_port);
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
2011-08-06 11:05:02 -04:00
|
|
|
else if (s->be->options & PR_O_DISPATCH) {
|
2021-01-11 09:24:31 -05:00
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* connect to the defined dispatch addr */
|
2021-01-11 09:24:31 -05:00
|
|
|
**ss = s->be->dispatch_addr;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2021-10-25 02:13:25 -04:00
|
|
|
else if ((s->be->options & PR_O_TRANSP)) {
|
2021-11-05 07:02:56 -04:00
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
2021-01-11 09:24:31 -05:00
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/* in transparent mode, use the original dest addr if no dispatch specified */
|
2022-05-27 02:57:21 -04:00
|
|
|
dst = sc_dst(s->scf);
|
2021-11-05 07:02:56 -04:00
|
|
|
if (dst && (dst->ss_family == AF_INET || dst->ss_family == AF_INET6))
|
2021-10-25 02:13:25 -04:00
|
|
|
**ss = *dst;
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
2007-01-20 05:07:46 -05:00
|
|
|
else {
|
|
|
|
|
/* no server and no LB algorithm ! */
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* This function assigns a server to stream <s> if required, and can add the
|
2006-06-25 20:48:02 -04:00
|
|
|
* connection to either the assigned server's queue or to the proxy's queue.
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* If ->srv_conn is set, the stream is first released from the server.
|
2015-04-02 19:14:29 -04:00
|
|
|
* It may also be called with SF_DIRECT and/or SF_ASSIGNED though. It will
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* be called before any connection and after any retry or redispatch occurs.
|
|
|
|
|
*
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* It is not allowed to call this function with a stream in a queue.
|
2006-06-25 20:48:02 -04:00
|
|
|
*
|
|
|
|
|
* Returns :
|
|
|
|
|
*
|
|
|
|
|
* SRV_STATUS_OK if everything is OK.
|
2012-11-11 18:42:33 -05:00
|
|
|
* SRV_STATUS_NOSRV if no server is available. objt_server(s->target) = NULL.
|
2006-06-25 20:48:02 -04:00
|
|
|
* SRV_STATUS_QUEUED if the connection has been queued.
|
|
|
|
|
* SRV_STATUS_FULL if the server(s) is/are saturated and the
|
2011-03-10 10:55:02 -05:00
|
|
|
* connection could not be queued at the server's,
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* which may be NULL if we queue on the backend.
|
2006-06-25 20:48:02 -04:00
|
|
|
* SRV_STATUS_INTERNAL for other unrecoverable errors.
|
|
|
|
|
*
|
|
|
|
|
*/
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
int assign_server_and_queue(struct stream *s)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
|
|
|
|
struct pendconn *p;
|
2011-03-10 10:55:02 -05:00
|
|
|
struct server *srv;
|
2006-06-25 20:48:02 -04:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (s->pend_pos)
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = SRV_STATUS_OK;
|
2015-04-02 19:14:29 -04:00
|
|
|
if (!(s->flags & SF_ASSIGNED)) {
|
2012-11-11 18:42:33 -05:00
|
|
|
struct server *prev_srv = objt_server(s->target);
|
2011-03-10 05:42:13 -05:00
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
err = assign_server(s);
|
2011-03-10 05:42:13 -05:00
|
|
|
if (prev_srv) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* This stream was previously assigned to a server. We have to
|
|
|
|
|
* update the stream's and the server's stats :
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* - if the server changed :
|
|
|
|
|
* - set TX_CK_DOWN if txn.flags was TX_CK_VALID
|
2015-04-02 19:14:29 -04:00
|
|
|
* - set SF_REDISP if it was successfully redispatched
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* - increment srv->redispatches and be->redispatches
|
|
|
|
|
* - if the server remained the same : update retries.
|
[MEDIUM]: Prevent redispatcher from selecting the same server, version #3
When haproxy decides that session needs to be redispatched it chose a server,
but there is no guarantee for it to be a different one. So, it often
happens that selected server is exactly the same that it was previously, so
a client ends up with a 503 error anyway, especially when one sever has
much bigger weight than others.
Changes from the previous version:
- drop stupid and unnecessary SN_DIRECT changes
- assign_server(): use srvtoavoid to keep the old server and clear s->srv
so SRV_STATUS_NOSRV guarantees that t->srv == NULL (again)
and get_server_rr_with_conns has chances to work (previously
we were passing a NULL here)
- srv_redispatch_connect(): remove t->srv->cum_sess and t->srv->failed_conns
incrementing as t->srv was guaranteed to be NULL
- add avoididx to get_server_rr_with_conns. I hope I correctly understand this code.
- fix http_flush_cookie_flags() and move it to assign_server_and_queue()
directly. The code here was supposed to set CK_DOWN and clear CK_VALID,
but: (TX_CK_VALID | TX_CK_DOWN) == TX_CK_VALID == TX_CK_MASK so:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags ^= (TX_CK_VALID | TX_CK_DOWN);
was really a:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags &= TX_CK_VALID
Now haproxy logs "--DI" after redispatching connection.
- defer srv->redispatches++ and s->be->redispatches++ so there
are called only if a conenction was redispatched, not only
supposed to.
- don't increment lbconn if redispatcher selected the same sarver
- don't count unsuccessfully redispatched connections as redispatched
connections
- don't count redispatched connections as errors, so:
- the number of connections effectively served by a server is:
srv->cum_sess - srv->failed_conns - srv->retries - srv->redispatches
and
SUM(servers->failed_conns) == be->failed_conns
- requires the "Don't increment server connections too much + fix retries" patch
- needs little more testing and probably some discussion so reverting to the RFC state
Tests #1:
retries 4
redispatch
i) 1 server(s): b (wght=1, down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request failed
ii) server(s): b (wght=1, down), u (wght=1, down)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=1, retr=0, redis=0
-> request FAILED
iii) 2 server(s): b (wght=1, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
iv) 2 server(s): b (wght=100, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
v) 1 server(s): b (down for first 4 SYNS)
b) sessions=5, lbtot=1, err_conn=0, retr=4, redis=0
-> request OK
Tests #2:
retries 4
i) 1 server(s): b (down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request FAILED
2008-02-21 21:50:19 -05:00
|
|
|
*/
|
|
|
|
|
|
2012-11-11 18:42:33 -05:00
|
|
|
if (prev_srv != objt_server(s->target)) {
|
2015-04-03 17:46:31 -04:00
|
|
|
if (s->txn && (s->txn->flags & TX_CK_MASK) == TX_CK_VALID) {
|
|
|
|
|
s->txn->flags &= ~TX_CK_MASK;
|
|
|
|
|
s->txn->flags |= TX_CK_DOWN;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
}
|
2015-04-02 19:14:29 -04:00
|
|
|
s->flags |= SF_REDISP;
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&prev_srv->counters.redispatches);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.redispatches);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
} else {
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&prev_srv->counters.retries);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.retries);
|
[MEDIUM]: Prevent redispatcher from selecting the same server, version #3
When haproxy decides that session needs to be redispatched it chose a server,
but there is no guarantee for it to be a different one. So, it often
happens that selected server is exactly the same that it was previously, so
a client ends up with a 503 error anyway, especially when one sever has
much bigger weight than others.
Changes from the previous version:
- drop stupid and unnecessary SN_DIRECT changes
- assign_server(): use srvtoavoid to keep the old server and clear s->srv
so SRV_STATUS_NOSRV guarantees that t->srv == NULL (again)
and get_server_rr_with_conns has chances to work (previously
we were passing a NULL here)
- srv_redispatch_connect(): remove t->srv->cum_sess and t->srv->failed_conns
incrementing as t->srv was guaranteed to be NULL
- add avoididx to get_server_rr_with_conns. I hope I correctly understand this code.
- fix http_flush_cookie_flags() and move it to assign_server_and_queue()
directly. The code here was supposed to set CK_DOWN and clear CK_VALID,
but: (TX_CK_VALID | TX_CK_DOWN) == TX_CK_VALID == TX_CK_MASK so:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags ^= (TX_CK_VALID | TX_CK_DOWN);
was really a:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags &= TX_CK_VALID
Now haproxy logs "--DI" after redispatching connection.
- defer srv->redispatches++ and s->be->redispatches++ so there
are called only if a conenction was redispatched, not only
supposed to.
- don't increment lbconn if redispatcher selected the same sarver
- don't count unsuccessfully redispatched connections as redispatched
connections
- don't count redispatched connections as errors, so:
- the number of connections effectively served by a server is:
srv->cum_sess - srv->failed_conns - srv->retries - srv->redispatches
and
SUM(servers->failed_conns) == be->failed_conns
- requires the "Don't increment server connections too much + fix retries" patch
- needs little more testing and probably some discussion so reverting to the RFC state
Tests #1:
retries 4
redispatch
i) 1 server(s): b (wght=1, down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request failed
ii) server(s): b (wght=1, down), u (wght=1, down)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=1, retr=0, redis=0
-> request FAILED
iii) 2 server(s): b (wght=1, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
iv) 2 server(s): b (wght=100, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
v) 1 server(s): b (down for first 4 SYNS)
b) sessions=5, lbtot=1, err_conn=0, retr=4, redis=0
-> request OK
Tests #2:
retries 4
i) 1 server(s): b (down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request FAILED
2008-02-21 21:50:19 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
switch (err) {
|
|
|
|
|
case SRV_STATUS_OK:
|
2015-04-02 19:14:29 -04:00
|
|
|
/* we have SF_ASSIGNED set */
|
2012-11-11 18:42:33 -05:00
|
|
|
srv = objt_server(s->target);
|
2011-03-10 10:55:02 -05:00
|
|
|
if (!srv)
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
return SRV_STATUS_OK; /* dispatch or proxy mode */
|
|
|
|
|
|
|
|
|
|
/* If we already have a connection slot, no need to check any queue */
|
2011-03-10 10:55:02 -05:00
|
|
|
if (s->srv_conn == srv)
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* OK, this stream already has an assigned server, but no
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* connection slot yet. Either it is a redispatch, or it was
|
|
|
|
|
* assigned from persistence information (direct mode).
|
|
|
|
|
*/
|
2015-04-02 19:14:29 -04:00
|
|
|
if ((s->flags & SF_REDIRECTABLE) && srv->rdr_len) {
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
/* server scheduled for redirection, and already assigned. We
|
|
|
|
|
* don't want to go further nor check the queue.
|
2008-02-14 14:25:24 -05:00
|
|
|
*/
|
2011-03-10 10:55:02 -05:00
|
|
|
sess_change_server(s, srv); /* not really needed in fact */
|
2008-02-14 14:25:24 -05:00
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* We might have to queue this stream if the assigned server is full.
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
* We know we have to queue it into the server's queue, so if a maxqueue
|
|
|
|
|
* is set on the server, we must also check that the server's queue is
|
|
|
|
|
* not full, in which case we have to return FULL.
|
|
|
|
|
*/
|
2024-12-17 08:30:46 -05:00
|
|
|
if (srv->maxconn) {
|
MEDIUM: servers/proxies: Switch to using per-tgroup queues.
For both servers and proxies, use one connection queue per thread-group,
instead of only one. Having only one can lead to severe performance
issues on NUMA machines, it is actually trivial to get the watchdog to
trigger on an AMD machine, having a server with a maxconn of 96, and an
injector that uses 160 concurrent connections.
We now have one queue per thread-group, however when dequeueing, we're
dequeuing MAX_SELF_USE_QUEUE (currently 9) pendconns from our own queue,
before dequeueing one from another thread group, if available, to make
sure everybody is still running.
2025-01-15 10:44:05 -05:00
|
|
|
struct queue *queue = &srv->per_tgrp[tgid - 1].queue;
|
2024-12-17 08:30:46 -05:00
|
|
|
int served;
|
|
|
|
|
int got_it = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure that there's still a slot on the server.
|
|
|
|
|
* Try to increment its served, while making sure
|
|
|
|
|
* it is < maxconn.
|
|
|
|
|
*/
|
MEDIUM: servers/proxies: Switch to using per-tgroup queues.
For both servers and proxies, use one connection queue per thread-group,
instead of only one. Having only one can lead to severe performance
issues on NUMA machines, it is actually trivial to get the watchdog to
trigger on an AMD machine, having a server with a maxconn of 96, and an
injector that uses 160 concurrent connections.
We now have one queue per thread-group, however when dequeueing, we're
dequeuing MAX_SELF_USE_QUEUE (currently 9) pendconns from our own queue,
before dequeueing one from another thread group, if available, to make
sure everybody is still running.
2025-01-15 10:44:05 -05:00
|
|
|
if (!queue->length &&
|
2024-12-17 08:30:46 -05:00
|
|
|
(served = srv->served) < srv_dynamic_maxconn(srv)) {
|
|
|
|
|
/*
|
|
|
|
|
* Attempt to increment served, while
|
|
|
|
|
* making sure it is always below maxconn
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
*/
|
2024-12-17 08:30:46 -05:00
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
got_it = _HA_ATOMIC_CAS(&srv->served,
|
|
|
|
|
&served, served + 1);
|
|
|
|
|
} while (!got_it && served < srv_dynamic_maxconn(srv) &&
|
|
|
|
|
__ha_cpu_relax());
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
}
|
2024-12-17 08:30:46 -05:00
|
|
|
if (!got_it) {
|
2025-01-15 10:37:35 -05:00
|
|
|
if (srv->maxqueue > 0 && srv->queueslength >= srv->maxqueue)
|
2024-12-17 08:30:46 -05:00
|
|
|
return SRV_STATUS_FULL;
|
|
|
|
|
|
|
|
|
|
p = pendconn_add(s);
|
|
|
|
|
if (p) {
|
|
|
|
|
/* There's a TOCTOU here: it may happen that between the
|
|
|
|
|
* moment we decided to queue the request and the moment
|
|
|
|
|
* it was done, the last active request on the server
|
|
|
|
|
* ended and no new one will be able to dequeue that one.
|
|
|
|
|
* Since we already have our server we don't care, this
|
|
|
|
|
* will be handled by the caller which will check for
|
|
|
|
|
* this condition and will immediately dequeue it if
|
|
|
|
|
* possible.
|
|
|
|
|
*/
|
|
|
|
|
return SRV_STATUS_QUEUED;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
_HA_ATOMIC_INC(&srv->served);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
|
|
|
|
|
/* OK, we can use this server. Let's reserve our place */
|
2011-03-10 10:55:02 -05:00
|
|
|
sess_change_server(s, srv);
|
2006-06-25 20:48:02 -04:00
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
|
|
|
|
|
case SRV_STATUS_FULL:
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* queue this stream into the proxy's queue */
|
2006-06-25 20:48:02 -04:00
|
|
|
p = pendconn_add(s);
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
if (p) {
|
|
|
|
|
/* There's a TOCTOU here: it may happen that between the
|
|
|
|
|
* moment we decided to queue the request and the moment
|
|
|
|
|
* it was done, the last active request in the backend
|
|
|
|
|
* ended and no new one will be able to dequeue that one.
|
|
|
|
|
* This is more visible with maxconn 1 where it can
|
|
|
|
|
* happen 1/1000 times, though the vast majority are
|
MEDIUM: queue: Handle the race condition between queue and dequeue differently
There is a small race condition, where a server would check if there is
something left in the proxy queue, and adding something to the proxy
queue. If the server checks just before the stream is added to the queue,
and it no longer has any stream to deal with, then nothing will take
care of the stream, that may stay in the queue forever.
This was worked around with commit 5541d4995d, by checking for that exact
condition after adding the stream to the queue, and trying again to get
a server assigned if it is detected.
That fix lead to multiple infinite loops, that got fixed, but it is not
unlikely that it could happen again. So let's fix the initial problem
differently : a single server may mark itself as ready, and it removes
itself once used. The principle is that when we discover that the just
queued stream is alone with no active request anywhere ot dequeue it,
instead of rebalancing it, it will be assigned to that current "ready"
server that is available to handle it. The extra cost of the atomic ops
is negligible since the situation is super rare.
2024-12-05 09:30:06 -05:00
|
|
|
* correctly recovered from.
|
|
|
|
|
* To work around that, when a server is getting idle,
|
|
|
|
|
* it will set the ready_srv field of the proxy.
|
|
|
|
|
* Here, if ready_srv is non-NULL, we get that server,
|
|
|
|
|
* and we attempt to switch its served from 0 to 1.
|
|
|
|
|
* If it works, then we can just run, otherwise,
|
|
|
|
|
* it means another stream will be running, and will
|
|
|
|
|
* dequeue us eventually, so we can just do nothing.
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
*/
|
MEDIUM: queue: Handle the race condition between queue and dequeue differently
There is a small race condition, where a server would check if there is
something left in the proxy queue, and adding something to the proxy
queue. If the server checks just before the stream is added to the queue,
and it no longer has any stream to deal with, then nothing will take
care of the stream, that may stay in the queue forever.
This was worked around with commit 5541d4995d, by checking for that exact
condition after adding the stream to the queue, and trying again to get
a server assigned if it is detected.
That fix lead to multiple infinite loops, that got fixed, but it is not
unlikely that it could happen again. So let's fix the initial problem
differently : a single server may mark itself as ready, and it removes
itself once used. The principle is that when we discover that the just
queued stream is alone with no active request anywhere ot dequeue it,
instead of rebalancing it, it will be assigned to that current "ready"
server that is available to handle it. The extra cost of the atomic ops
is negligible since the situation is super rare.
2024-12-05 09:30:06 -05:00
|
|
|
if (unlikely(s->be->ready_srv != NULL)) {
|
|
|
|
|
struct server *newserv;
|
|
|
|
|
|
|
|
|
|
newserv = HA_ATOMIC_XCHG(&s->be->ready_srv, NULL);
|
|
|
|
|
if (newserv != NULL) {
|
|
|
|
|
int got_slot = 0;
|
|
|
|
|
|
|
|
|
|
while (_HA_ATOMIC_LOAD(&newserv->served) == 0) {
|
|
|
|
|
int served = 0;
|
|
|
|
|
|
|
|
|
|
if (_HA_ATOMIC_CAS(&newserv->served, &served, 1)) {
|
|
|
|
|
got_slot = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!got_slot) {
|
|
|
|
|
/*
|
|
|
|
|
* Somebody else can now
|
|
|
|
|
* wake up us, stop now.
|
|
|
|
|
*/
|
|
|
|
|
return SRV_STATUS_QUEUED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HA_SPIN_LOCK(QUEUE_LOCK, &p->queue->lock);
|
|
|
|
|
if (!p->node.node.leaf_p) {
|
|
|
|
|
/*
|
|
|
|
|
* Okay we've been queued and
|
|
|
|
|
* unqueued already, just leave
|
|
|
|
|
*/
|
|
|
|
|
_HA_ATOMIC_DEC(&newserv->served);
|
|
|
|
|
return SRV_STATUS_QUEUED;
|
|
|
|
|
}
|
|
|
|
|
eb32_delete(&p->node);
|
|
|
|
|
HA_SPIN_UNLOCK(QUEUE_LOCK, &p->queue->lock);
|
|
|
|
|
|
|
|
|
|
_HA_ATOMIC_DEC(&p->queue->length);
|
|
|
|
|
_HA_ATOMIC_INC(&p->queue->idx);
|
|
|
|
|
_HA_ATOMIC_DEC(&s->be->totpend);
|
|
|
|
|
|
|
|
|
|
pool_free(pool_head_pendconn, p);
|
|
|
|
|
|
|
|
|
|
s->flags |= SF_ASSIGNED;
|
|
|
|
|
s->target = &newserv->obj_type;
|
|
|
|
|
s->pend_pos = NULL;
|
|
|
|
|
sess_change_server(s, newserv);
|
|
|
|
|
return SRV_STATUS_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
return SRV_STATUS_QUEUED;
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
else
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
return SRV_STATUS_INTERNAL;
|
2006-06-25 20:48:02 -04:00
|
|
|
|
|
|
|
|
case SRV_STATUS_NOSRV:
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
return err;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
case SRV_STATUS_INTERNAL:
|
|
|
|
|
return err;
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
default:
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 11:17:15 -04:00
|
|
|
/* Allocate an address if an explicit source address must be used for a backend
|
|
|
|
|
* connection.
|
2021-01-21 03:40:19 -05:00
|
|
|
*
|
2023-10-02 11:17:15 -04:00
|
|
|
* Two parameters are taken into account to check if specific source address is
|
|
|
|
|
* configured. The first one is <srv> which is the server instance to connect
|
|
|
|
|
* to. It may be NULL when dispatching is used. The second one <be> is the
|
|
|
|
|
* backend instance which contains the target server or dispatch.
|
|
|
|
|
*
|
|
|
|
|
* A stream instance <s> can be used to set the stream owner of the backend
|
|
|
|
|
* connection. It is a required parameter if the source address is a dynamic
|
|
|
|
|
* parameter.
|
|
|
|
|
*
|
|
|
|
|
* Returns SRV_STATUS_OK if either no specific source address specified or its
|
|
|
|
|
* allocation is done correctly. On error returns SRV_STATUS_INTERNAL.
|
2010-03-29 13:36:59 -04:00
|
|
|
*/
|
2023-10-02 11:17:15 -04:00
|
|
|
int alloc_bind_address(struct sockaddr_storage **ss,
|
|
|
|
|
struct server *srv, struct proxy *be,
|
|
|
|
|
struct stream *s)
|
2010-03-29 13:36:59 -04:00
|
|
|
{
|
2015-08-20 13:35:14 -04:00
|
|
|
#if defined(CONFIG_HAP_TRANSPARENT)
|
2021-10-25 02:13:25 -04:00
|
|
|
const struct sockaddr_storage *addr;
|
2021-01-21 03:40:19 -05:00
|
|
|
struct conn_src *src = NULL;
|
|
|
|
|
struct sockaddr_in *sin;
|
|
|
|
|
char *vptr;
|
|
|
|
|
size_t vlen;
|
|
|
|
|
#endif
|
2019-01-17 09:59:13 -05:00
|
|
|
|
2023-10-02 11:17:15 -04:00
|
|
|
/* Ensure the function will not overwrite an allocated address. */
|
|
|
|
|
BUG_ON(*ss);
|
2011-03-10 10:55:02 -05:00
|
|
|
|
2021-01-21 03:40:19 -05:00
|
|
|
#if defined(CONFIG_HAP_TRANSPARENT)
|
2012-12-08 17:13:33 -05:00
|
|
|
if (srv && srv->conn_src.opts & CO_SRC_BIND)
|
|
|
|
|
src = &srv->conn_src;
|
2023-10-02 11:17:15 -04:00
|
|
|
else if (be->conn_src.opts & CO_SRC_BIND)
|
|
|
|
|
src = &be->conn_src;
|
2012-12-08 17:13:33 -05:00
|
|
|
|
2021-01-21 03:40:19 -05:00
|
|
|
/* no transparent mode, no need to allocate an address, returns OK */
|
|
|
|
|
if (!src)
|
|
|
|
|
return SRV_STATUS_OK;
|
2019-07-17 12:16:30 -04:00
|
|
|
|
2012-12-08 17:13:33 -05:00
|
|
|
switch (src->opts & CO_SRC_TPROXY_MASK) {
|
|
|
|
|
case CO_SRC_TPROXY_ADDR:
|
2021-01-21 03:40:19 -05:00
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
|
|
|
|
**ss = src->tproxy_addr;
|
2012-12-08 17:13:33 -05:00
|
|
|
break;
|
2021-01-21 03:40:19 -05:00
|
|
|
|
2012-12-08 17:13:33 -05:00
|
|
|
case CO_SRC_TPROXY_CLI:
|
|
|
|
|
case CO_SRC_TPROXY_CIP:
|
2023-10-02 11:17:15 -04:00
|
|
|
BUG_ON(!s); /* Dynamic source setting requires a stream instance. */
|
|
|
|
|
|
2012-12-08 17:13:33 -05:00
|
|
|
/* FIXME: what can we do if the client connects in IPv6 or unix socket ? */
|
2022-05-27 02:57:21 -04:00
|
|
|
addr = sc_src(s->scf);
|
2021-10-25 02:13:25 -04:00
|
|
|
if (!addr)
|
2021-01-21 03:40:19 -05:00
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
|
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
2021-10-25 02:13:25 -04:00
|
|
|
**ss = *addr;
|
2012-12-08 17:13:33 -05:00
|
|
|
break;
|
2021-01-21 03:40:19 -05:00
|
|
|
|
2012-12-08 17:13:33 -05:00
|
|
|
case CO_SRC_TPROXY_DYN:
|
2023-10-02 11:17:15 -04:00
|
|
|
BUG_ON(!s); /* Dynamic source setting requires a stream instance. */
|
|
|
|
|
|
2021-01-21 03:40:19 -05:00
|
|
|
if (!src->bind_hdr_occ || !IS_HTX_STRM(s))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
|
|
|
|
if (!sockaddr_alloc(ss, NULL, 0))
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
|
|
|
|
|
|
|
|
|
/* bind to the IP in a header */
|
|
|
|
|
sin = (struct sockaddr_in *)*ss;
|
|
|
|
|
sin->sin_family = AF_INET;
|
|
|
|
|
sin->sin_port = 0;
|
|
|
|
|
sin->sin_addr.s_addr = 0;
|
|
|
|
|
if (!http_get_htx_hdr(htxbuf(&s->req.buf),
|
|
|
|
|
ist2(src->bind_hdr_name, src->bind_hdr_len),
|
|
|
|
|
src->bind_hdr_occ, NULL, &vptr, &vlen)) {
|
|
|
|
|
sockaddr_free(ss);
|
|
|
|
|
return SRV_STATUS_INTERNAL;
|
2010-03-29 13:36:59 -04:00
|
|
|
}
|
2021-01-21 03:40:19 -05:00
|
|
|
|
|
|
|
|
sin->sin_addr.s_addr = htonl(inetaddr_host_lim(vptr, vptr + vlen));
|
2012-12-08 17:13:33 -05:00
|
|
|
break;
|
2021-01-21 03:40:19 -05:00
|
|
|
|
2012-12-08 17:13:33 -05:00
|
|
|
default:
|
2021-01-21 03:40:19 -05:00
|
|
|
;
|
2010-03-29 13:36:59 -04:00
|
|
|
}
|
|
|
|
|
#endif
|
2021-01-21 03:40:19 -05:00
|
|
|
|
|
|
|
|
return SRV_STATUS_OK;
|
2010-03-29 13:36:59 -04:00
|
|
|
}
|
|
|
|
|
|
2020-03-06 12:18:56 -05:00
|
|
|
/* Attempt to get a backend connection from the specified mt_list array
|
2020-07-01 09:04:38 -04:00
|
|
|
* (safe or idle connections). The <is_safe> argument means what type of
|
|
|
|
|
* connection the caller wants.
|
2020-03-06 12:18:56 -05:00
|
|
|
*/
|
DEBUG: unstatify a few functions that are often present in backtraces
It's useful to be able to recognize certain functions that are often
present in backtraces as they call lower level functions, and for this
they must not be static. Let's remove "static" in front of these
functions:
sc_notify, sc_conn_recv, sc_conn_send, sc_conn_process,
sc_applet_process, back_establish, stream_update_both_sc,
httpclient_applet_io_handler, httpclient_applet_init,
httpclient_applet_release
2023-11-30 11:04:16 -05:00
|
|
|
struct connection *conn_backend_get(struct stream *s, struct server *srv, int is_safe, int64_t hash)
|
2020-03-06 12:18:56 -05:00
|
|
|
{
|
2025-01-30 05:16:40 -05:00
|
|
|
const struct tgroup_info *curtg = tg;
|
2021-01-06 10:14:12 -05:00
|
|
|
struct connection *conn = NULL;
|
2025-01-30 05:16:40 -05:00
|
|
|
unsigned int curtgid = tgid;
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
int i; // thread number
|
2020-03-06 12:18:56 -05:00
|
|
|
int found = 0;
|
2020-07-01 09:55:30 -04:00
|
|
|
int stop;
|
2020-03-06 12:18:56 -05:00
|
|
|
|
|
|
|
|
/* We need to lock even if this is our own list, because another
|
|
|
|
|
* thread may be trying to migrate that connection, and we don't want
|
|
|
|
|
* to end up with two threads using the same connection.
|
|
|
|
|
*/
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
i = tid;
|
2021-01-11 03:21:52 -05:00
|
|
|
HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
2021-03-04 03:45:32 -05:00
|
|
|
conn = srv_lookup_conn(is_safe ? &srv->per_thr[tid].safe_conns : &srv->per_thr[tid].idle_conns, hash);
|
2021-01-06 10:14:12 -05:00
|
|
|
if (conn)
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(conn);
|
2020-07-01 09:04:38 -04:00
|
|
|
|
|
|
|
|
/* If we failed to pick a connection from the idle list, let's try again with
|
|
|
|
|
* the safe list.
|
|
|
|
|
*/
|
|
|
|
|
if (!conn && !is_safe && srv->curr_safe_nb > 0) {
|
2021-03-04 03:45:32 -05:00
|
|
|
conn = srv_lookup_conn(&srv->per_thr[tid].safe_conns, hash);
|
2020-07-01 09:04:38 -04:00
|
|
|
if (conn) {
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(conn);
|
2020-07-01 09:04:38 -04:00
|
|
|
is_safe = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-11 03:21:52 -05:00
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
2020-03-06 12:18:56 -05:00
|
|
|
|
|
|
|
|
/* If we found a connection in our own list, and we don't have to
|
|
|
|
|
* steal one from another thread, then we're done.
|
|
|
|
|
*/
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
if (conn)
|
|
|
|
|
goto done;
|
|
|
|
|
|
2020-07-01 12:49:24 -04:00
|
|
|
/* pool sharing globally disabled ? */
|
|
|
|
|
if (!(global.tune.options & GTUNE_IDLE_POOL_SHARED))
|
|
|
|
|
goto done;
|
|
|
|
|
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
/* Are we allowed to pick from another thread ? We'll still try
|
|
|
|
|
* it if we're running low on FDs as we don't want to create
|
|
|
|
|
* extra conns in this case, otherwise we can give up if we have
|
2023-11-17 04:53:36 -05:00
|
|
|
* too few idle conns and the server protocol supports establishing
|
|
|
|
|
* connections (i.e. not a reverse-http server for example).
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
*/
|
|
|
|
|
if (srv->curr_idle_conns < srv->low_idle_conns &&
|
2023-11-17 04:53:36 -05:00
|
|
|
ha_used_fds < global.tune.pool_low_count) {
|
|
|
|
|
const struct protocol *srv_proto = protocol_lookup(srv->addr.ss_family, PROTO_TYPE_STREAM, 0);
|
|
|
|
|
|
|
|
|
|
if (srv_proto && srv_proto->connect)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2020-03-06 12:18:56 -05:00
|
|
|
|
2020-07-01 09:55:30 -04:00
|
|
|
/* Lookup all other threads for an idle connection, starting from last
|
2022-07-07 03:12:45 -04:00
|
|
|
* unvisited thread, but always staying in the same group.
|
2020-07-01 09:55:30 -04:00
|
|
|
*/
|
2022-11-21 08:14:06 -05:00
|
|
|
stop = srv->per_tgrp[tgid - 1].next_takeover;
|
2025-01-30 05:16:40 -05:00
|
|
|
if (stop >= curtg->count)
|
|
|
|
|
stop %= curtg->count;
|
|
|
|
|
stop += curtg->base;
|
|
|
|
|
check_tgid:
|
2020-10-14 12:17:03 -04:00
|
|
|
i = stop;
|
|
|
|
|
do {
|
2020-07-01 09:55:30 -04:00
|
|
|
if (!srv->curr_idle_thr[i] || i == tid)
|
2020-07-01 02:24:44 -04:00
|
|
|
continue;
|
|
|
|
|
|
MEDIUM: backend: use a trylock when trying to grab an idle connection
In conn_backend_get() we can cause some extreme contention due to the
idle_conns_lock. Indeed, even though it's per-thread, it still causes
high contention when running with many threads. The reason is that all
threads which do not have any idle connections are quickly skipped,
till the point where there are still some, so the first reaching that
point will grab the lock and the other ones wait behind. From this
point, all threads are synchronized waiting on the same lock, and
will follow the leader in small jumps, all hindering each other.
Here instead of doing this we're using a trylock. This way when a thread
is already checking a list, other ones will continue to next thread. In
the worst case, a high contention will lead to a few new connections to
be set up, but this may actually be what is required to avoid contention
in the first place. With this change, the contention has mostly
disappeared on this lock (it's still present in muxes and transport
layers due to the takeover).
Surprisingly, checking for emptiness of the tree root before taking
the lock didn't address any contention.
A few improvements are still possible and desirable here. The first
one would be to avoid seeing all threads jump to the next one. We
could have each thread use a different prime number as the increment
so as to spread them across the entire table instead of keeping them
synchronized. The second one is that the lock in the muck layers
shouldn't be needed to check for the tasklet's context availability.
2021-03-01 01:22:17 -05:00
|
|
|
if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock) != 0)
|
|
|
|
|
continue;
|
2021-03-04 03:45:32 -05:00
|
|
|
conn = srv_lookup_conn(is_safe ? &srv->per_thr[i].safe_conns : &srv->per_thr[i].idle_conns, hash);
|
2021-01-06 10:14:12 -05:00
|
|
|
while (conn) {
|
2024-03-15 10:36:33 -04:00
|
|
|
if (conn->mux->takeover && conn->mux->takeover(conn, i, 0) == 0) {
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(conn);
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&activity[tid].fd_takeover);
|
2020-03-06 12:18:56 -05:00
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-01-06 10:14:12 -05:00
|
|
|
|
|
|
|
|
conn = srv_lookup_conn_next(conn);
|
2020-03-06 12:18:56 -05:00
|
|
|
}
|
2020-07-01 09:04:38 -04:00
|
|
|
|
|
|
|
|
if (!found && !is_safe && srv->curr_safe_nb > 0) {
|
2021-03-04 03:45:32 -05:00
|
|
|
conn = srv_lookup_conn(&srv->per_thr[i].safe_conns, hash);
|
2021-01-06 10:14:12 -05:00
|
|
|
while (conn) {
|
2024-03-15 10:36:33 -04:00
|
|
|
if (conn->mux->takeover && conn->mux->takeover(conn, i, 0) == 0) {
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(conn);
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&activity[tid].fd_takeover);
|
2020-07-01 09:04:38 -04:00
|
|
|
found = 1;
|
|
|
|
|
is_safe = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-01-06 10:14:12 -05:00
|
|
|
|
|
|
|
|
conn = srv_lookup_conn_next(conn);
|
2020-07-01 09:04:38 -04:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-11 03:21:52 -05:00
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
|
2025-01-30 05:16:40 -05:00
|
|
|
} while (!found && (i = (i + 1 == curtg->base + curtg->count) ? curtg->base : i + 1) != stop);
|
|
|
|
|
|
|
|
|
|
if (!found && (global.tune.tg_takeover == FULL_THREADGROUP_TAKEOVER ||
|
|
|
|
|
(global.tune.tg_takeover == RESTRICTED_THREADGROUP_TAKEOVER &&
|
2025-02-06 11:07:48 -05:00
|
|
|
srv->flags & (SRV_F_RHTTP | SRV_F_STRICT_MAXCONN)))) {
|
2025-01-30 05:16:40 -05:00
|
|
|
curtgid = curtgid + 1;
|
|
|
|
|
if (curtgid == global.nbtgroups + 1)
|
|
|
|
|
curtgid = 1;
|
|
|
|
|
/* If we haven't looped yet */
|
2025-03-12 13:16:14 -04:00
|
|
|
if (MAX_TGROUPS > 1 && curtgid != tgid) {
|
2025-01-30 05:16:40 -05:00
|
|
|
curtg = &ha_tgroup_info[curtgid - 1];
|
|
|
|
|
stop = curtg->base;
|
|
|
|
|
goto check_tgid;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-06 12:18:56 -05:00
|
|
|
if (!found)
|
|
|
|
|
conn = NULL;
|
MEDIUM: server: add a new pool-low-conn server setting
The problem with the way idle connections currently work is that it's
easy for a thread to steal all of its siblings' connections, then release
them, then it's done by another one, etc. This happens even more easily
due to scheduling latencies, or merged events inside the same pool loop,
which, when dealing with a fast server responding in sub-millisecond
delays, can really result in one thread being fully at work at a time.
In such a case, we perform a huge amount of takeover() which consumes
CPU and requires quite some locking, sometimes resulting in lower
performance than expected.
In order to fight against this problem, this patch introduces a new server
setting "pool-low-conn", whose purpose is to dictate when it is allowed to
steal connections from a sibling. As long as the number of idle connections
remains at least as high as this value, it is permitted to take over another
connection. When the idle connection count becomes lower, a thread may only
use its own connections or create a new one. By proceeding like this even
with a low number (typically 2*nbthreads), we quickly end up in a situation
where all active threads have a few connections. It then becomes possible
to connect to a server without bothering other threads the vast majority
of the time, while still being able to use these connections when the
number of available FDs becomes low.
We also use this threshold instead of global.nbthread in the connection
release logic, allowing to keep more extra connections if needed.
A test performed with 10000 concurrent HTTP/1 connections, 16 threads
and 210 servers with 1 millisecond of server response time showed the
following numbers:
haproxy 2.1.7: 185000 requests per second
haproxy 2.2: 314000 requests per second
haproxy 2.2 lowconn 32: 352000 requests per second
The takeover rate goes down from 300k/s to 13k/s. The difference is
further amplified as the response time shrinks.
2020-07-01 01:43:51 -04:00
|
|
|
done:
|
|
|
|
|
if (conn) {
|
2022-11-21 08:14:06 -05:00
|
|
|
_HA_ATOMIC_STORE(&srv->per_tgrp[tgid - 1].next_takeover, (i + 1 == tg->base + tg->count) ? tg->base : i + 1);
|
2020-10-14 12:17:04 -04:00
|
|
|
|
|
|
|
|
srv_use_conn(srv, conn);
|
|
|
|
|
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_DEC(&srv->curr_idle_conns);
|
|
|
|
|
_HA_ATOMIC_DEC(conn->flags & CO_FL_SAFE_LIST ? &srv->curr_safe_nb : &srv->curr_idle_nb);
|
|
|
|
|
_HA_ATOMIC_DEC(&srv->curr_idle_thr[i]);
|
2020-10-14 12:17:04 -04:00
|
|
|
conn->flags &= ~CO_FL_LIST_MASK;
|
|
|
|
|
__ha_barrier_atomic_store();
|
|
|
|
|
|
2020-10-14 12:17:09 -04:00
|
|
|
if ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_SAFE &&
|
|
|
|
|
conn->mux->flags & MX_FL_HOL_RISK) {
|
|
|
|
|
/* attach the connection to the session private list
|
|
|
|
|
*/
|
|
|
|
|
conn->owner = s->sess;
|
2020-11-20 11:08:15 -05:00
|
|
|
session_add_conn(s->sess, conn, conn->target);
|
2020-10-14 12:17:09 -04:00
|
|
|
}
|
|
|
|
|
else {
|
2023-10-25 04:10:14 -04:00
|
|
|
srv_add_to_avail_list(srv, conn);
|
2020-10-14 12:17:09 -04:00
|
|
|
}
|
2020-03-06 12:18:56 -05:00
|
|
|
}
|
|
|
|
|
return conn;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 03:53:38 -04:00
|
|
|
static int do_connect_server(struct stream *s, struct connection *conn)
|
|
|
|
|
{
|
|
|
|
|
int ret = SF_ERR_NONE;
|
|
|
|
|
int conn_flags = 0;
|
|
|
|
|
|
|
|
|
|
if (unlikely(!conn || !conn->ctrl || !conn->ctrl->connect))
|
|
|
|
|
return SF_ERR_INTERNAL;
|
|
|
|
|
|
2023-10-10 12:00:38 -04:00
|
|
|
if (co_data(&s->res))
|
2022-03-31 03:53:38 -04:00
|
|
|
conn_flags |= CONNECT_HAS_DATA;
|
2024-09-25 09:05:07 -04:00
|
|
|
if (s->conn_retries == s->max_retries)
|
2022-03-31 03:53:38 -04:00
|
|
|
conn_flags |= CONNECT_CAN_USE_TFO;
|
|
|
|
|
if (!conn_ctrl_ready(conn) || !conn_xprt_ready(conn)) {
|
|
|
|
|
ret = conn->ctrl->connect(conn, conn_flags);
|
|
|
|
|
if (ret != SF_ERR_NONE)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* we're in the process of establishing a connection */
|
2022-05-17 13:47:17 -04:00
|
|
|
s->scb->state = SC_ST_CON;
|
2022-03-31 03:53:38 -04:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* try to reuse the existing connection, it will be
|
|
|
|
|
* confirmed once we can send on it.
|
|
|
|
|
*/
|
|
|
|
|
/* Is the connection really ready ? */
|
2023-11-28 08:27:51 -05:00
|
|
|
if (conn->mux->ctl(conn, MUX_CTL_STATUS, NULL) & MUX_STATUS_READY)
|
2022-05-17 13:47:17 -04:00
|
|
|
s->scb->state = SC_ST_RDY;
|
2022-03-31 03:53:38 -04:00
|
|
|
else
|
2022-05-17 13:47:17 -04:00
|
|
|
s->scb->state = SC_ST_CON;
|
2022-03-31 03:53:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* needs src ip/port for logging */
|
|
|
|
|
if (s->flags & SF_SRC_ADDR)
|
|
|
|
|
conn_get_src(conn);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 11:07:48 -05:00
|
|
|
/*
|
|
|
|
|
* Returns the first connection from a tree we managed to take over,
|
|
|
|
|
* if any.
|
|
|
|
|
*/
|
|
|
|
|
static struct connection *
|
|
|
|
|
takeover_random_idle_conn(struct eb_root *root, int curtid)
|
|
|
|
|
{
|
|
|
|
|
struct conn_hash_node *hash_node;
|
|
|
|
|
struct connection *conn = NULL;
|
|
|
|
|
struct eb64_node *node = eb64_first(root);
|
|
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
|
hash_node = eb64_entry(node, struct conn_hash_node, node);
|
|
|
|
|
conn = hash_node->conn;
|
|
|
|
|
if (conn && conn->mux->takeover && conn->mux->takeover(conn, curtid, 0) == 0) {
|
|
|
|
|
conn_delete_from_tree(conn);
|
|
|
|
|
return conn;
|
|
|
|
|
}
|
|
|
|
|
node = eb64_next(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Kills an idle connection, any idle connection we can get a hold on.
|
|
|
|
|
* The goal is just to free a connection in case we reached the max and
|
|
|
|
|
* have to establish a new one.
|
|
|
|
|
* Returns -1 if there is no idle connection to kill, 0 if there are some
|
|
|
|
|
* available but we failed to get one, and 1 if we successfully killed one.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
kill_random_idle_conn(struct server *srv)
|
|
|
|
|
{
|
|
|
|
|
struct connection *conn = NULL;
|
|
|
|
|
int i;
|
|
|
|
|
int curtid;
|
|
|
|
|
/* No idle conn, then there is nothing we can do at this point */
|
|
|
|
|
|
|
|
|
|
if (srv->curr_idle_conns == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
for (i = 0; i < global.nbthread; i++) {
|
|
|
|
|
curtid = (i + tid) % global.nbthread;
|
|
|
|
|
|
|
|
|
|
if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].idle_conns_lock) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
conn = takeover_random_idle_conn(&srv->per_thr[curtid].idle_conns, curtid);
|
|
|
|
|
if (!conn)
|
|
|
|
|
conn = takeover_random_idle_conn(&srv->per_thr[curtid].safe_conns, curtid);
|
|
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].idle_conns_lock);
|
|
|
|
|
if (conn)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (conn) {
|
|
|
|
|
/*
|
|
|
|
|
* We have to manually decrement counters, as srv_release_conn()
|
|
|
|
|
* will attempt to access the current tid's counters, while
|
|
|
|
|
* we may have taken the connection from a different thread.
|
|
|
|
|
*/
|
|
|
|
|
if (conn->flags & CO_FL_LIST_MASK) {
|
|
|
|
|
_HA_ATOMIC_DEC(&srv->curr_idle_conns);
|
|
|
|
|
_HA_ATOMIC_DEC(conn->flags & CO_FL_SAFE_LIST ? &srv->curr_safe_nb : &srv->curr_idle_nb);
|
|
|
|
|
_HA_ATOMIC_DEC(&srv->curr_idle_thr[curtid]);
|
|
|
|
|
conn->flags &= ~CO_FL_LIST_MASK;
|
|
|
|
|
/*
|
|
|
|
|
* If we have no list flag then srv_release_conn()
|
|
|
|
|
* will consider the connection is used, so let's
|
|
|
|
|
* pretend it is.
|
|
|
|
|
*/
|
|
|
|
|
_HA_ATOMIC_INC(&srv->curr_used_conns);
|
|
|
|
|
}
|
|
|
|
|
conn->mux->destroy(conn->ctx);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* This function initiates a connection to the server assigned to this stream
|
2022-05-17 13:40:40 -04:00
|
|
|
* (s->target, (s->scb)->addr.to). It will assign a server if none
|
2011-03-10 05:38:29 -05:00
|
|
|
* is assigned yet.
|
2006-06-25 20:48:02 -04:00
|
|
|
* It can return one of :
|
2015-04-02 19:14:29 -04:00
|
|
|
* - SF_ERR_NONE if everything's OK
|
|
|
|
|
* - SF_ERR_SRVTO if there are no more servers
|
|
|
|
|
* - SF_ERR_SRVCL if the connection was refused by the server
|
|
|
|
|
* - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
|
|
|
|
|
* - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
|
|
|
|
|
* - SF_ERR_INTERNAL for any other purely internal errors
|
2016-11-28 20:15:19 -05:00
|
|
|
* Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
|
2022-05-17 13:07:51 -04:00
|
|
|
* The server-facing stream connector is expected to hold a pre-allocated connection.
|
2006-06-25 20:48:02 -04:00
|
|
|
*/
|
DEBUG: unstatify a few functions that are often present in backtraces
It's useful to be able to recognize certain functions that are often
present in backtraces as they call lower level functions, and for this
they must not be static. Let's remove "static" in front of these
functions:
sc_notify, sc_conn_recv, sc_conn_send, sc_conn_process,
sc_applet_process, back_establish, stream_update_both_sc,
httpclient_applet_io_handler, httpclient_applet_init,
httpclient_applet_release
2023-11-30 11:04:16 -05:00
|
|
|
int connect_server(struct stream *s)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2019-07-18 09:47:45 -04:00
|
|
|
struct connection *cli_conn = objt_conn(strm_orig(s));
|
2018-11-30 11:24:55 -05:00
|
|
|
struct connection *srv_conn = NULL;
|
2011-03-10 10:55:02 -05:00
|
|
|
struct server *srv;
|
2023-08-18 10:02:35 -04:00
|
|
|
int reuse_mode = s->be->options & PR_O_REUSE_MASK;
|
2013-12-15 10:33:46 -05:00
|
|
|
int reuse = 0;
|
2019-01-04 09:52:26 -05:00
|
|
|
int init_mux = 0;
|
2009-08-16 08:02:45 -04:00
|
|
|
int err;
|
2024-05-23 12:31:48 -04:00
|
|
|
struct sample *name_smp = NULL;
|
2022-05-02 10:20:36 -04:00
|
|
|
struct sockaddr_storage *bind_addr = NULL;
|
2021-01-14 04:15:29 -05:00
|
|
|
int proxy_line_ret;
|
2021-01-06 10:14:12 -05:00
|
|
|
int64_t hash = 0;
|
2021-01-18 08:57:50 -05:00
|
|
|
struct conn_hash_params hash_params;
|
|
|
|
|
|
MINOR: backend: handle reuse for conns with no server as target
If dispatch mode or transparent backend is used, the backend connection
target is a proxy instead of a server. In these cases, the reuse of
backend connections is not consistent.
With the default behavior, no reuse is done and every new request uses a
new connection. However, if http-reuse is set to never, the connection
are stored by the mux in the session and can be reused for future
requests in the same session.
As no server is used for these connections, no reuse can be made outside
of the session, similarly to http-reuse never mode. A different
http-reuse config value should not have an impact. To achieve this, mark
these connections as private to have a defined behavior.
For this feature to properly work, the connection hash has been slightly
adjusted. The server pointer as an input as been replaced by a generic
target pointer to refer to the server or proxy instance. The hash is
always calculated on connect_server even if the connection target is not
a server. This also requires to allocate the connection hash node for
every backend connections, not just the one with a server target.
2021-03-02 08:38:53 -05:00
|
|
|
/* in standard configuration, srv will be valid
|
|
|
|
|
* it can be NULL for dispatch mode or transparent backend */
|
|
|
|
|
srv = objt_server(s->target);
|
|
|
|
|
|
2023-08-18 10:02:35 -04:00
|
|
|
/* Override reuse-mode if reverse-connect is used. */
|
2023-11-21 05:10:34 -05:00
|
|
|
if (srv && srv->flags & SRV_F_RHTTP)
|
2023-08-18 10:02:35 -04:00
|
|
|
reuse_mode = PR_O_REUSE_ALWS;
|
|
|
|
|
|
2022-05-17 13:40:40 -04:00
|
|
|
err = alloc_dst_address(&s->scb->dst, srv, s);
|
MEDIUM: stream: remove the confusing SF_ADDR_SET flag
This flag is no longer needed now that it must always match the presence
of a destination address on the backend conn_stream. Worse, before previous
patch, if it were to be accidently removed while the address is present, it
could result in a leak of that address since alloc_dst_address() would first
be called to flush it.
Its usage has a long history where addresses were stored in an area shared
with the connection, but as this is no longer the case, there's no reason
for putting this burden onto application-level code that should not focus
on setting obscure flags.
The only place where that made a small difference is in the dequeuing code
in case of queue redistribution, because previously the code would first
clear the flag, and only later when trying to deal with the queue, would
release the address. It's not even certain whether there would exist a
code path going to connect_server() without calling pendconn_dequeue()
first (e.g. retries on queue timeout maybe?).
Now the pendconn_dequeue() code will rely on SF_ASSIGNED to decide to
clear and release the address, since that flag is always set while in
a server's queue, and its clearance implies that we don't want to keep
the address. At least it remains consistent and there's no more risk of
leaking it.
2022-05-02 10:36:47 -04:00
|
|
|
if (err != SRV_STATUS_OK)
|
|
|
|
|
return SF_ERR_INTERNAL;
|
2021-10-20 09:04:03 -04:00
|
|
|
|
2023-10-02 11:17:15 -04:00
|
|
|
err = alloc_bind_address(&bind_addr, srv, s->be, s);
|
2021-10-20 09:04:03 -04:00
|
|
|
if (err != SRV_STATUS_OK)
|
|
|
|
|
return SF_ERR_INTERNAL;
|
2021-01-22 10:47:46 -05:00
|
|
|
|
2024-05-23 12:31:48 -04:00
|
|
|
if (srv && srv->pool_conn_name_expr) {
|
|
|
|
|
name_smp = sample_fetch_as_type(s->be, s->sess, s,
|
|
|
|
|
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
|
|
|
|
|
srv->pool_conn_name_expr, SMP_T_STR);
|
2021-10-20 09:04:03 -04:00
|
|
|
}
|
2021-02-17 09:59:02 -05:00
|
|
|
|
2021-10-18 08:39:57 -04:00
|
|
|
/* disable reuse if websocket stream and the protocol to use is not the
|
|
|
|
|
* same as the main protocol of the server.
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(s->flags & SF_WEBSOCKET) && srv) {
|
|
|
|
|
if (!srv_check_reuse_ws(srv)) {
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("skip idle connections reuse: websocket stream", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2021-10-18 08:39:57 -04:00
|
|
|
goto skip_reuse;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-20 09:04:03 -04:00
|
|
|
/* first, set unique connection parameters and then calculate hash */
|
|
|
|
|
memset(&hash_params, 0, sizeof(hash_params));
|
2021-02-11 10:46:53 -05:00
|
|
|
|
2021-10-20 09:04:03 -04:00
|
|
|
/* 1. target */
|
|
|
|
|
hash_params.target = s->target;
|
|
|
|
|
|
2024-05-23 12:31:48 -04:00
|
|
|
/* 2. pool-conn-name */
|
|
|
|
|
if (name_smp) {
|
|
|
|
|
hash_params.name_prehash =
|
|
|
|
|
conn_hash_prehash(name_smp->data.u.str.area,
|
|
|
|
|
name_smp->data.u.str.data);
|
2021-02-11 10:46:53 -05:00
|
|
|
}
|
|
|
|
|
|
2021-10-20 09:04:03 -04:00
|
|
|
/* 3. destination address */
|
2023-08-18 10:02:35 -04:00
|
|
|
if (srv && srv_is_transparent(srv))
|
2022-05-17 13:40:40 -04:00
|
|
|
hash_params.dst_addr = s->scb->dst;
|
2021-02-11 10:46:53 -05:00
|
|
|
|
MINOR: backend: handle reuse for conns with no server as target
If dispatch mode or transparent backend is used, the backend connection
target is a proxy instead of a server. In these cases, the reuse of
backend connections is not consistent.
With the default behavior, no reuse is done and every new request uses a
new connection. However, if http-reuse is set to never, the connection
are stored by the mux in the session and can be reused for future
requests in the same session.
As no server is used for these connections, no reuse can be made outside
of the session, similarly to http-reuse never mode. A different
http-reuse config value should not have an impact. To achieve this, mark
these connections as private to have a defined behavior.
For this feature to properly work, the connection hash has been slightly
adjusted. The server pointer as an input as been replaced by a generic
target pointer to refer to the server or proxy instance. The hash is
always calculated on connect_server even if the connection target is not
a server. This also requires to allocate the connection hash node for
every backend connections, not just the one with a server target.
2021-03-02 08:38:53 -05:00
|
|
|
/* 4. source address */
|
2021-02-11 13:45:19 -05:00
|
|
|
hash_params.src_addr = bind_addr;
|
|
|
|
|
|
MINOR: backend: handle reuse for conns with no server as target
If dispatch mode or transparent backend is used, the backend connection
target is a proxy instead of a server. In these cases, the reuse of
backend connections is not consistent.
With the default behavior, no reuse is done and every new request uses a
new connection. However, if http-reuse is set to never, the connection
are stored by the mux in the session and can be reused for future
requests in the same session.
As no server is used for these connections, no reuse can be made outside
of the session, similarly to http-reuse never mode. A different
http-reuse config value should not have an impact. To achieve this, mark
these connections as private to have a defined behavior.
For this feature to properly work, the connection hash has been slightly
adjusted. The server pointer as an input as been replaced by a generic
target pointer to refer to the server or proxy instance. The hash is
always calculated on connect_server even if the connection target is not
a server. This also requires to allocate the connection hash node for
every backend connections, not just the one with a server target.
2021-03-02 08:38:53 -05:00
|
|
|
/* 5. proxy protocol */
|
2025-03-02 21:58:46 -05:00
|
|
|
if (srv && (srv->pp_opts & SRV_PP_ENABLED)) {
|
2024-05-16 11:46:36 -04:00
|
|
|
proxy_line_ret = make_proxy_line(trash.area, trash.size, srv, cli_conn, s, strm_sess(s));
|
2021-01-14 04:15:29 -05:00
|
|
|
if (proxy_line_ret) {
|
2021-02-17 10:25:31 -05:00
|
|
|
hash_params.proxy_prehash =
|
|
|
|
|
conn_hash_prehash(trash.area, proxy_line_ret);
|
2021-01-14 04:15:29 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 12:51:26 -05:00
|
|
|
/* 6. Custom mark, tos? */
|
|
|
|
|
if (s->flags & (SF_BC_MARK | SF_BC_TOS)) {
|
|
|
|
|
/* mark: 32bits, tos: 8bits = 40bits
|
|
|
|
|
* last 2 bits are there to indicate if mark and/or tos are set
|
|
|
|
|
* total: 42bits:
|
|
|
|
|
*
|
|
|
|
|
* 63==== (unused) ====42 39----32 31-----------------------------0
|
|
|
|
|
* 0000000000000000000000 11 00000111 00000000000000000000000000000011
|
|
|
|
|
* ^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
* || | |
|
|
|
|
|
* / \ \ \
|
|
|
|
|
* / \ \ \
|
|
|
|
|
* tos? mark? \ mark value (32bits)
|
|
|
|
|
* tos value (8bits)
|
|
|
|
|
* ie: in the above example:
|
|
|
|
|
* - mark is set, mark = 3
|
|
|
|
|
* - tos is set, tos = 7
|
|
|
|
|
*/
|
|
|
|
|
if (s->flags & SF_BC_MARK) {
|
|
|
|
|
hash_params.mark_tos_prehash |= s->bc_mark;
|
|
|
|
|
/* 41th bit: mark set */
|
|
|
|
|
hash_params.mark_tos_prehash |= 1ULL << 40;
|
|
|
|
|
}
|
|
|
|
|
if (s->flags & SF_BC_TOS) {
|
|
|
|
|
hash_params.mark_tos_prehash |= (uint64_t)s->bc_tos << 32;
|
|
|
|
|
/* 42th bit: tos set */
|
|
|
|
|
hash_params.mark_tos_prehash |= 1ULL << 41;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
MINOR: backend: handle reuse for conns with no server as target
If dispatch mode or transparent backend is used, the backend connection
target is a proxy instead of a server. In these cases, the reuse of
backend connections is not consistent.
With the default behavior, no reuse is done and every new request uses a
new connection. However, if http-reuse is set to never, the connection
are stored by the mux in the session and can be reused for future
requests in the same session.
As no server is used for these connections, no reuse can be made outside
of the session, similarly to http-reuse never mode. A different
http-reuse config value should not have an impact. To achieve this, mark
these connections as private to have a defined behavior.
For this feature to properly work, the connection hash has been slightly
adjusted. The server pointer as an input as been replaced by a generic
target pointer to refer to the server or proxy instance. The hash is
always calculated on connect_server even if the connection target is not
a server. This also requires to allocate the connection hash node for
every backend connections, not just the one with a server target.
2021-03-02 08:38:53 -05:00
|
|
|
hash = conn_calculate_hash(&hash_params);
|
2018-11-13 10:48:36 -05:00
|
|
|
|
2019-07-18 13:26:11 -04:00
|
|
|
/* first, search for a matching connection in the session's idle conns */
|
2021-01-25 04:29:35 -05:00
|
|
|
srv_conn = session_get_conn(s->sess, s->target, hash);
|
2021-10-20 09:22:01 -04:00
|
|
|
if (srv_conn) {
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("reuse connection from session", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-07-01 10:36:51 -04:00
|
|
|
reuse = 1;
|
2021-10-20 09:22:01 -04:00
|
|
|
}
|
2018-11-30 11:24:55 -05:00
|
|
|
|
2021-01-26 08:35:26 -05:00
|
|
|
if (srv && !reuse && reuse_mode != PR_O_REUSE_NEVR) {
|
2020-02-13 13:12:07 -05:00
|
|
|
/* Below we pick connections from the safe, idle or
|
|
|
|
|
* available (which are safe too) lists based
|
2015-08-05 11:16:33 -04:00
|
|
|
* on the strategy, the fact that this is a first or second
|
|
|
|
|
* (retryable) request, with the indicated priority (1 or 2) :
|
|
|
|
|
*
|
|
|
|
|
* SAFE AGGR ALWS
|
|
|
|
|
*
|
|
|
|
|
* +-----+-----+ +-----+-----+ +-----+-----+
|
|
|
|
|
* req| 1st | 2nd | req| 1st | 2nd | req| 1st | 2nd |
|
|
|
|
|
* ----+-----+-----+ ----+-----+-----+ ----+-----+-----+
|
|
|
|
|
* safe| - | 2 | safe| 1 | 2 | safe| 1 | 2 |
|
|
|
|
|
* ----+-----+-----+ ----+-----+-----+ ----+-----+-----+
|
|
|
|
|
* idle| - | 1 | idle| - | 1 | idle| 2 | 1 |
|
|
|
|
|
* ----+-----+-----+ ----+-----+-----+ ----+-----+-----+
|
2019-07-18 13:26:11 -04:00
|
|
|
*
|
|
|
|
|
* Idle conns are necessarily looked up on the same thread so
|
|
|
|
|
* that there is no concurrency issues.
|
2015-08-05 11:16:33 -04:00
|
|
|
*/
|
2021-03-04 03:45:32 -05:00
|
|
|
if (!eb_is_empty(&srv->per_thr[tid].avail_conns)) {
|
|
|
|
|
srv_conn = srv_lookup_conn(&srv->per_thr[tid].avail_conns, hash);
|
2021-10-20 09:22:01 -04:00
|
|
|
if (srv_conn) {
|
2023-10-24 12:31:28 -04:00
|
|
|
/* connection cannot be in idle list if used as an avail idle conn. */
|
|
|
|
|
BUG_ON(LIST_INLIST(&srv_conn->idle_list));
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("reuse connection from avail", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2021-01-06 10:14:12 -05:00
|
|
|
reuse = 1;
|
2021-10-20 09:22:01 -04:00
|
|
|
}
|
2015-08-05 11:16:33 -04:00
|
|
|
}
|
2021-01-22 13:37:44 -05:00
|
|
|
|
2021-01-26 08:35:26 -05:00
|
|
|
/* if no available connections found, search for an idle/safe */
|
2021-01-22 13:37:44 -05:00
|
|
|
if (!srv_conn && srv->max_idle_conns && srv->curr_idle_conns > 0) {
|
2021-01-26 08:35:26 -05:00
|
|
|
const int not_first_req = s->txn && s->txn->flags & TX_NOT_FIRST;
|
|
|
|
|
const int idle = srv->curr_idle_nb > 0;
|
|
|
|
|
const int safe = srv->curr_safe_nb > 0;
|
MINOR: backend: always satisfy the first req reuse rule with l7 retries
The "first req" rule consists in not delivering a connection's first
request to a connection that's not known for being safe so that we
don't deliver a broken page to a client if the server didn't intend to
keep it alive. That's what's used by "http-reuse safe" particularly.
But the reason this rule was created was precisely because haproxy was
not able to re-emit the request to the server in case of connection
breakage, which is precisely what l7 retries later brought. As such,
there's no reason for enforcing this rule when l7 retries are properly
enabled because such a blank page will trigger a retry and will not be
delivered to the client.
This patch simply checks that the l7 retries are enabled for the 3 cases
that can be triggered on a dead or dying connection (failure, empty, and
timeout), and if all 3 are enabled, then regular idle connections can be
reused.
This could almost be marked as a bug fix because a lot of users relying
on l7 retries do not necessarily think about using http-reuse always due
to the recommendation against it in the doc, while the protection that
the safe mode offers is never used in that mode, and it forces the http
client not to reuse existing persistent connections since it never sets
the "not first" flag.
It could also be decided that the protection is not used either when
the origin is an applet, as in this case this is internal code that
we can decide to let handle the retry by itself (all info are still
present). But at least the httpclient will be happy with this alone.
It would make sense to backport this at least to 2.6 in order to let
the httpclient reuse connections, maybe to older releases if some
users report low reuse counts.
2022-09-01 13:58:58 -04:00
|
|
|
const int retry_safe = (s->be->retry_type & (PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT)) ==
|
|
|
|
|
(PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT);
|
2021-01-26 08:35:26 -05:00
|
|
|
|
|
|
|
|
/* second column of the tables above,
|
|
|
|
|
* search for an idle then safe conn */
|
MINOR: backend: always satisfy the first req reuse rule with l7 retries
The "first req" rule consists in not delivering a connection's first
request to a connection that's not known for being safe so that we
don't deliver a broken page to a client if the server didn't intend to
keep it alive. That's what's used by "http-reuse safe" particularly.
But the reason this rule was created was precisely because haproxy was
not able to re-emit the request to the server in case of connection
breakage, which is precisely what l7 retries later brought. As such,
there's no reason for enforcing this rule when l7 retries are properly
enabled because such a blank page will trigger a retry and will not be
delivered to the client.
This patch simply checks that the l7 retries are enabled for the 3 cases
that can be triggered on a dead or dying connection (failure, empty, and
timeout), and if all 3 are enabled, then regular idle connections can be
reused.
This could almost be marked as a bug fix because a lot of users relying
on l7 retries do not necessarily think about using http-reuse always due
to the recommendation against it in the doc, while the protection that
the safe mode offers is never used in that mode, and it forces the http
client not to reuse existing persistent connections since it never sets
the "not first" flag.
It could also be decided that the protection is not used either when
the origin is an applet, as in this case this is internal code that
we can decide to let handle the retry by itself (all info are still
present). But at least the httpclient will be happy with this alone.
It would make sense to backport this at least to 2.6 in order to let
the httpclient reuse connections, maybe to older releases if some
users report low reuse counts.
2022-09-01 13:58:58 -04:00
|
|
|
if (not_first_req || retry_safe) {
|
2021-01-26 08:35:26 -05:00
|
|
|
if (idle || safe)
|
2021-01-06 10:14:12 -05:00
|
|
|
srv_conn = conn_backend_get(s, srv, 0, hash);
|
2020-03-06 12:18:56 -05:00
|
|
|
}
|
2021-01-26 08:35:26 -05:00
|
|
|
/* first column of the tables above */
|
|
|
|
|
else if (reuse_mode >= PR_O_REUSE_AGGR) {
|
|
|
|
|
/* search for a safe conn */
|
|
|
|
|
if (safe)
|
2021-01-06 10:14:12 -05:00
|
|
|
srv_conn = conn_backend_get(s, srv, 1, hash);
|
2021-01-25 08:43:17 -05:00
|
|
|
|
|
|
|
|
/* search for an idle conn if no safe conn found
|
|
|
|
|
* on always reuse mode */
|
|
|
|
|
if (!srv_conn &&
|
|
|
|
|
reuse_mode == PR_O_REUSE_ALWS && idle) {
|
|
|
|
|
/* TODO conn_backend_get should not check the
|
|
|
|
|
* safe list is this case */
|
2021-01-06 10:14:12 -05:00
|
|
|
srv_conn = conn_backend_get(s, srv, 0, hash);
|
2021-01-25 08:43:17 -05:00
|
|
|
}
|
2020-03-06 12:18:56 -05:00
|
|
|
}
|
2021-01-26 08:35:25 -05:00
|
|
|
|
2021-10-20 09:22:01 -04:00
|
|
|
if (srv_conn) {
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("reuse connection from idle/safe", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-03-06 12:18:56 -05:00
|
|
|
reuse = 1;
|
2021-10-20 09:22:01 -04:00
|
|
|
}
|
2020-02-13 13:12:07 -05:00
|
|
|
}
|
2015-08-04 14:45:52 -04:00
|
|
|
}
|
|
|
|
|
|
2019-07-18 13:26:11 -04:00
|
|
|
|
|
|
|
|
/* here reuse might have been set above, indicating srv_conn finally
|
|
|
|
|
* is OK.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-03-04 03:45:32 -05:00
|
|
|
if (ha_used_fds > global.tune.pool_high_count && srv) {
|
2021-01-06 10:14:12 -05:00
|
|
|
struct connection *tokill_conn = NULL;
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
/* We can't reuse a connection, and e have more FDs than deemd
|
|
|
|
|
* acceptable, attempt to kill an idling connection
|
|
|
|
|
*/
|
|
|
|
|
/* First, try from our own idle list */
|
2021-01-11 03:21:52 -05:00
|
|
|
HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
2023-08-21 12:32:29 -04:00
|
|
|
if (!LIST_ISEMPTY(&srv->per_thr[tid].idle_conn_list)) {
|
2023-10-24 12:31:55 -04:00
|
|
|
tokill_conn = LIST_ELEM(srv->per_thr[tid].idle_conn_list.n, struct connection *, idle_list);
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(tokill_conn);
|
2021-03-23 05:44:43 -04:00
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
|
|
|
|
|
|
|
|
|
/* Release the idle lock before calling mux->destroy.
|
|
|
|
|
* It will in turn call srv_release_conn through
|
|
|
|
|
* conn_free which also uses it.
|
|
|
|
|
*/
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
tokill_conn->mux->destroy(tokill_conn->ctx);
|
2021-01-06 10:14:12 -05:00
|
|
|
}
|
2021-03-23 05:44:43 -04:00
|
|
|
else {
|
|
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
|
|
|
|
}
|
2021-01-11 03:21:52 -05:00
|
|
|
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
/* If not, iterate over other thread's idling pool, and try to grab one */
|
2021-01-11 03:21:52 -05:00
|
|
|
if (!tokill_conn) {
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
int i;
|
|
|
|
|
|
2020-06-29 08:43:16 -04:00
|
|
|
for (i = tid; (i = ((i + 1 == global.nbthread) ? 0 : i + 1)) != tid;) {
|
2019-05-26 05:50:08 -04:00
|
|
|
// just silence stupid gcc which reports an absurd
|
|
|
|
|
// out-of-bounds warning for <i> which is always
|
|
|
|
|
// exactly zero without threads, but it seems to
|
|
|
|
|
// see it possibly larger.
|
|
|
|
|
ALREADY_CHECKED(i);
|
|
|
|
|
|
2021-03-26 15:52:10 -04:00
|
|
|
if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-08-21 12:32:29 -04:00
|
|
|
if (!LIST_ISEMPTY(&srv->per_thr[i].idle_conn_list)) {
|
2023-10-24 12:31:55 -04:00
|
|
|
tokill_conn = LIST_ELEM(srv->per_thr[i].idle_conn_list.n, struct connection *, idle_list);
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(tokill_conn);
|
2021-01-06 10:14:12 -05:00
|
|
|
}
|
2021-01-11 03:21:52 -05:00
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
|
2021-01-28 04:16:29 -05:00
|
|
|
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
if (tokill_conn) {
|
|
|
|
|
/* We got one, put it into the concerned thread's to kill list, and wake it's kill task */
|
|
|
|
|
|
2021-04-21 01:32:39 -04:00
|
|
|
MT_LIST_APPEND(&idle_conns[i].toremove_conns,
|
2023-08-21 12:32:29 -04:00
|
|
|
&tokill_conn->toremove_list);
|
2020-06-27 18:19:17 -04:00
|
|
|
task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER);
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2025-02-25 02:42:45 -05:00
|
|
|
|
|
|
|
|
if (!(global.tune.options & GTUNE_IDLE_POOL_SHARED))
|
|
|
|
|
break;
|
MEDIUM: connections: Add a way to control the number of idling connections.
As by default we add all keepalive connections to the idle pool, if we run
into a pathological case, where all client don't do keepalive, but the server
does, and haproxy is configured to only reuse "safe" connections, we will
soon find ourself having lots of idling, unusable for new sessions, connections,
while we won't have any file descriptors available to create new connections.
To fix this, add 2 new global settings, "pool_low_ratio" and "pool_high_ratio".
pool-low-fd-ratio is the % of fds we're allowed to use (against the maximum
number of fds available to haproxy) before we stop adding connections to the
idle pool, and destroy them instead. The default is 20. pool-high-fd-ratio is
the % of fds we're allowed to use (against the maximum number of fds available
to haproxy) before we start killing idling connection in the event we have to
create a new outgoing connection, and no reuse is possible. The default is 25.
2019-04-16 13:07:22 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2013-12-15 10:33:46 -05:00
|
|
|
|
2019-01-24 12:22:19 -05:00
|
|
|
if (reuse) {
|
2019-07-18 13:26:11 -04:00
|
|
|
if (srv_conn->mux) {
|
2019-01-24 12:22:19 -05:00
|
|
|
int avail = srv_conn->mux->avail_streams(srv_conn);
|
|
|
|
|
|
|
|
|
|
if (avail <= 1) {
|
2018-12-01 11:03:38 -05:00
|
|
|
/* No more streams available, remove it from the list */
|
2022-04-22 11:56:24 -04:00
|
|
|
HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
2023-08-21 08:24:17 -04:00
|
|
|
conn_delete_from_tree(srv_conn);
|
2022-04-22 11:56:24 -04:00
|
|
|
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
|
2018-12-01 11:03:38 -05:00
|
|
|
}
|
2019-01-24 12:22:19 -05:00
|
|
|
|
|
|
|
|
if (avail >= 1) {
|
2022-05-17 13:40:40 -04:00
|
|
|
if (srv_conn->mux->attach(srv_conn, s->scb->sedesc, s->sess) == -1) {
|
2019-01-24 12:22:19 -05:00
|
|
|
srv_conn = NULL;
|
2022-05-27 02:49:24 -04:00
|
|
|
if (sc_reset_endp(s->scb) < 0)
|
2022-03-23 10:15:29 -04:00
|
|
|
return SF_ERR_INTERNAL;
|
2022-05-17 13:40:40 -04:00
|
|
|
sc_ep_clr(s->scb, ~SE_FL_DETACHED);
|
2021-12-20 09:34:16 -05:00
|
|
|
}
|
2019-01-24 12:22:19 -05:00
|
|
|
}
|
2018-12-28 08:45:47 -05:00
|
|
|
else
|
|
|
|
|
srv_conn = NULL;
|
2018-11-13 10:48:36 -05:00
|
|
|
}
|
2019-01-24 12:22:19 -05:00
|
|
|
/* otherwise srv_conn is left intact */
|
2018-11-13 10:48:36 -05:00
|
|
|
}
|
2019-01-24 12:22:19 -05:00
|
|
|
else
|
|
|
|
|
srv_conn = NULL;
|
|
|
|
|
|
2021-01-26 11:35:46 -05:00
|
|
|
skip_reuse:
|
2019-01-24 12:22:19 -05:00
|
|
|
/* no reuse or failed to reuse the connection above, pick a new one */
|
|
|
|
|
if (!srv_conn) {
|
2025-02-06 11:07:48 -05:00
|
|
|
unsigned int total_conns;
|
|
|
|
|
|
2023-11-21 05:10:34 -05:00
|
|
|
if (srv && (srv->flags & SRV_F_RHTTP)) {
|
2023-08-18 10:02:35 -04:00
|
|
|
DBG_TRACE_USER("cannot open a new connection for reverse server", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
|
|
|
|
return SF_ERR_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 11:07:48 -05:00
|
|
|
if (srv && (srv->flags & SRV_F_STRICT_MAXCONN)) {
|
|
|
|
|
int kill_tries = 0;
|
|
|
|
|
/*
|
|
|
|
|
* Before creating a new connection, make sure we still
|
|
|
|
|
* have a slot for that
|
|
|
|
|
*/
|
|
|
|
|
total_conns = srv->curr_total_conns;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
if (total_conns < srv->maxconn) {
|
|
|
|
|
if (_HA_ATOMIC_CAS(&srv->curr_total_conns,
|
|
|
|
|
&total_conns, total_conns + 1))
|
|
|
|
|
break;
|
|
|
|
|
__ha_cpu_relax();
|
|
|
|
|
} else {
|
|
|
|
|
int ret = kill_random_idle_conn(srv);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* There is no idle connection to kill
|
|
|
|
|
* so there is nothing we can do at
|
|
|
|
|
* that point but to report an
|
|
|
|
|
* error.
|
|
|
|
|
*/
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
return SF_ERR_RESOURCE;
|
|
|
|
|
kill_tries++;
|
|
|
|
|
/*
|
|
|
|
|
* We tried 3 times to kill an idle
|
|
|
|
|
* connection, we failed, give up now.
|
|
|
|
|
*/
|
|
|
|
|
if (ret == 0 && kill_tries == 3)
|
|
|
|
|
return SF_ERR_RESOURCE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-02 03:19:54 -04:00
|
|
|
srv_conn = conn_new(s->target);
|
2020-07-15 11:46:32 -04:00
|
|
|
if (srv_conn) {
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("alloc new be connection", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-07-15 11:46:32 -04:00
|
|
|
srv_conn->owner = s->sess;
|
MINOR: backend: handle reuse for conns with no server as target
If dispatch mode or transparent backend is used, the backend connection
target is a proxy instead of a server. In these cases, the reuse of
backend connections is not consistent.
With the default behavior, no reuse is done and every new request uses a
new connection. However, if http-reuse is set to never, the connection
are stored by the mux in the session and can be reused for future
requests in the same session.
As no server is used for these connections, no reuse can be made outside
of the session, similarly to http-reuse never mode. A different
http-reuse config value should not have an impact. To achieve this, mark
these connections as private to have a defined behavior.
For this feature to properly work, the connection hash has been slightly
adjusted. The server pointer as an input as been replaced by a generic
target pointer to refer to the server or proxy instance. The hash is
always calculated on connect_server even if the connection target is not
a server. This also requires to allocate the connection hash node for
every backend connections, not just the one with a server target.
2021-03-02 08:38:53 -05:00
|
|
|
|
|
|
|
|
/* connection will be attached to the session if
|
|
|
|
|
* http-reuse mode is never or it is not targeted to a
|
|
|
|
|
* server */
|
|
|
|
|
if (reuse_mode == PR_O_REUSE_NEVR || !srv)
|
2020-07-15 11:46:32 -04:00
|
|
|
conn_set_private(srv_conn);
|
2013-10-11 13:34:20 -04:00
|
|
|
|
2021-03-02 06:01:06 -05:00
|
|
|
/* assign bind_addr to srv_conn */
|
2021-02-11 13:45:19 -05:00
|
|
|
srv_conn->src = bind_addr;
|
2021-03-02 06:01:06 -05:00
|
|
|
bind_addr = NULL;
|
2021-01-21 03:40:19 -05:00
|
|
|
|
2024-01-18 12:51:26 -05:00
|
|
|
/* mark? */
|
|
|
|
|
if (s->flags & SF_BC_MARK) {
|
|
|
|
|
srv_conn->mark = s->bc_mark;
|
|
|
|
|
srv_conn->flags |= CO_FL_OPT_MARK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* tos? */
|
|
|
|
|
if (s->flags & SF_BC_TOS) {
|
|
|
|
|
srv_conn->tos = s->bc_tos;
|
|
|
|
|
srv_conn->flags |= CO_FL_OPT_TOS;
|
|
|
|
|
}
|
|
|
|
|
|
BUG/MAJOR: conn-idle: fix hash indexing issues on idle conns
Idle connections do not work on 32-bit machines due to an alignment issue
causing the connection nodes to be indexed with their lower 32-bits set to
zero and the higher 32 ones containing the 32 lower bitss of the hash. The
cause is the use of ebmb_node with an aligned data, as on this platform
ebmb_node is only 32-bit aligned, leaving a hole before the following hash
which is a uint64_t:
$ pahole -C conn_hash_node ./haproxy
struct conn_hash_node {
struct ebmb_node node; /* 0 20 */
/* XXX 4 bytes hole, try to pack */
int64_t hash; /* 24 8 */
struct connection * conn; /* 32 4 */
/* size: 40, cachelines: 1, members: 3 */
/* sum members: 32, holes: 1, sum holes: 4 */
/* padding: 4 */
/* last cacheline: 40 bytes */
};
Instead, eb64 nodes should be used when it comes to simply storing a
64-bit key, and that is what this patch does.
For backports, a variant consisting in simply marking the "hash" member
with a "packed" attribute on the struct also does the job (tested), and
might be preferable if the fix is difficult to adapt. Only 2.6 and 2.5
are affected by this.
2022-09-29 14:32:43 -04:00
|
|
|
srv_conn->hash_node->node.key = hash;
|
2025-02-06 11:07:48 -05:00
|
|
|
} else if (srv && (srv->flags & SRV_F_STRICT_MAXCONN))
|
|
|
|
|
_HA_ATOMIC_DEC(&srv->curr_total_conns);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 06:01:06 -05:00
|
|
|
/* if bind_addr is non NULL free it */
|
|
|
|
|
sockaddr_free(&bind_addr);
|
|
|
|
|
|
|
|
|
|
/* srv_conn is still NULL only on allocation failure */
|
|
|
|
|
if (!srv_conn)
|
|
|
|
|
return SF_ERR_RESOURCE;
|
|
|
|
|
|
2019-07-18 09:47:45 -04:00
|
|
|
/* copy the target address into the connection */
|
2022-05-17 13:40:40 -04:00
|
|
|
*srv_conn->dst = *s->scb->dst;
|
2019-07-18 09:47:45 -04:00
|
|
|
|
|
|
|
|
/* Copy network namespace from client connection */
|
|
|
|
|
srv_conn->proxy_netns = cli_conn ? cli_conn->proxy_netns : NULL;
|
|
|
|
|
|
2021-03-05 17:37:48 -05:00
|
|
|
if (!srv_conn->xprt) {
|
2022-05-17 13:07:51 -04:00
|
|
|
/* set the correct protocol on the output stream connector */
|
MEDIUM: protocol: add MPTCP per address support
Multipath TCP (MPTCP), standardized in RFC8684 [1], is a TCP extension
that enables a TCP connection to use different paths.
Multipath TCP has been used for several use cases. On smartphones, MPTCP
enables seamless handovers between cellular and Wi-Fi networks while
preserving established connections. This use-case is what pushed Apple
to use MPTCP since 2013 in multiple applications [2]. On dual-stack
hosts, Multipath TCP enables the TCP connection to automatically use the
best performing path, either IPv4 or IPv6. If one path fails, MPTCP
automatically uses the other path.
To benefit from MPTCP, both the client and the server have to support
it. Multipath TCP is a backward-compatible TCP extension that is enabled
by default on recent Linux distributions (Debian, Ubuntu, Redhat, ...).
Multipath TCP is included in the Linux kernel since version 5.6 [3]. To
use it on Linux, an application must explicitly enable it when creating
the socket. No need to change anything else in the application.
This attached patch adds MPTCP per address support, to be used with:
mptcp{,4,6}@<address>[:port1[-port2]]
MPTCP v4 and v6 protocols have been added: they are mainly a copy of the
TCP ones, with small differences: names, proto, and receivers lists.
These protocols are stored in __protocol_by_family, as an alternative to
TCP, similar to what has been done with QUIC. By doing that, the size of
__protocol_by_family has not been increased, and it behaves like TCP.
MPTCP is both supported for the frontend and backend sides.
Also added an example of configuration using mptcp along with a backend
allowing to experiment with it.
Note that this is a re-implementation of Bjrn's work from 3 years ago
[4], when haproxy's internals were probably less ready to deal with
this, causing his work to be left pending for a while.
Currently, the TCP_MAXSEG socket option doesn't seem to be supported
with MPTCP [5]. This results in a warning when trying to set the MSS of
sockets in proto_tcp:tcp_bind_listener.
This can be resolved by adding two new variables:
sock_inet(6)_mptcp_maxseg_default that will hold the default
value of the TCP_MAXSEG option. Note that for the moment, this
will always be -1 as the option isn't supported. However, in the
future, when the support for this option will be added, it should
contain the correct value for the MSS, allowing to correctly
set the TCP_MAXSEG option.
Link: https://www.rfc-editor.org/rfc/rfc8684.html [1]
Link: https://www.tessares.net/apples-mptcp-story-so-far/ [2]
Link: https://www.mptcp.dev [3]
Link: https://github.com/haproxy/haproxy/issues/1028 [4]
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/515 [5]
Co-authored-by: Dorian Craps <dorian.craps@student.vinci.be>
Co-authored-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
2024-08-26 05:50:27 -04:00
|
|
|
|
2021-03-05 17:37:48 -05:00
|
|
|
if (srv) {
|
MEDIUM: protocol: add MPTCP per address support
Multipath TCP (MPTCP), standardized in RFC8684 [1], is a TCP extension
that enables a TCP connection to use different paths.
Multipath TCP has been used for several use cases. On smartphones, MPTCP
enables seamless handovers between cellular and Wi-Fi networks while
preserving established connections. This use-case is what pushed Apple
to use MPTCP since 2013 in multiple applications [2]. On dual-stack
hosts, Multipath TCP enables the TCP connection to automatically use the
best performing path, either IPv4 or IPv6. If one path fails, MPTCP
automatically uses the other path.
To benefit from MPTCP, both the client and the server have to support
it. Multipath TCP is a backward-compatible TCP extension that is enabled
by default on recent Linux distributions (Debian, Ubuntu, Redhat, ...).
Multipath TCP is included in the Linux kernel since version 5.6 [3]. To
use it on Linux, an application must explicitly enable it when creating
the socket. No need to change anything else in the application.
This attached patch adds MPTCP per address support, to be used with:
mptcp{,4,6}@<address>[:port1[-port2]]
MPTCP v4 and v6 protocols have been added: they are mainly a copy of the
TCP ones, with small differences: names, proto, and receivers lists.
These protocols are stored in __protocol_by_family, as an alternative to
TCP, similar to what has been done with QUIC. By doing that, the size of
__protocol_by_family has not been increased, and it behaves like TCP.
MPTCP is both supported for the frontend and backend sides.
Also added an example of configuration using mptcp along with a backend
allowing to experiment with it.
Note that this is a re-implementation of Bjrn's work from 3 years ago
[4], when haproxy's internals were probably less ready to deal with
this, causing his work to be left pending for a while.
Currently, the TCP_MAXSEG socket option doesn't seem to be supported
with MPTCP [5]. This results in a warning when trying to set the MSS of
sockets in proto_tcp:tcp_bind_listener.
This can be resolved by adding two new variables:
sock_inet(6)_mptcp_maxseg_default that will hold the default
value of the TCP_MAXSEG option. Note that for the moment, this
will always be -1 as the option isn't supported. However, in the
future, when the support for this option will be added, it should
contain the correct value for the MSS, allowing to correctly
set the TCP_MAXSEG option.
Link: https://www.rfc-editor.org/rfc/rfc8684.html [1]
Link: https://www.tessares.net/apples-mptcp-story-so-far/ [2]
Link: https://www.mptcp.dev [3]
Link: https://github.com/haproxy/haproxy/issues/1028 [4]
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/515 [5]
Co-authored-by: Dorian Craps <dorian.craps@student.vinci.be>
Co-authored-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
2024-08-26 05:50:27 -04:00
|
|
|
if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, srv->alt_proto), srv->xprt)) {
|
2021-03-05 17:37:48 -05:00
|
|
|
conn_free(srv_conn);
|
|
|
|
|
return SF_ERR_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
} else if (obj_type(s->target) == OBJ_TYPE_PROXY) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
2013-12-20 05:09:51 -05:00
|
|
|
/* proxies exclusively run on raw_sock right now */
|
2021-10-27 11:41:07 -04:00
|
|
|
ret = conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, 0), xprt_get(XPRT_RAW));
|
2021-03-05 17:37:48 -05:00
|
|
|
if (ret < 0 || !(srv_conn->ctrl)) {
|
2020-03-20 09:26:32 -04:00
|
|
|
conn_free(srv_conn);
|
2015-04-02 19:14:29 -04:00
|
|
|
return SF_ERR_INTERNAL;
|
2020-03-20 09:26:32 -04:00
|
|
|
}
|
2013-12-20 05:09:51 -05:00
|
|
|
}
|
2020-03-20 09:26:32 -04:00
|
|
|
else {
|
|
|
|
|
conn_free(srv_conn);
|
2015-04-02 19:14:29 -04:00
|
|
|
return SF_ERR_INTERNAL; /* how did we get there ? */
|
2020-03-20 09:26:32 -04:00
|
|
|
}
|
2013-12-20 05:09:51 -05:00
|
|
|
|
2022-05-27 02:49:24 -04:00
|
|
|
if (sc_attach_mux(s->scb, NULL, srv_conn) < 0) {
|
2022-03-31 13:27:18 -04:00
|
|
|
conn_free(srv_conn);
|
|
|
|
|
return SF_ERR_INTERNAL; /* how did we get there ? */
|
|
|
|
|
}
|
2022-05-17 13:40:40 -04:00
|
|
|
srv_conn->ctx = s->scb;
|
2022-03-23 06:01:09 -04:00
|
|
|
|
2020-01-24 08:10:55 -05:00
|
|
|
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
2018-11-23 08:32:08 -05:00
|
|
|
if (!srv ||
|
2020-07-30 03:10:36 -04:00
|
|
|
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
|
2021-03-08 11:57:53 -05:00
|
|
|
srv->mux_proto || !IS_HTX_STRM(s)))
|
2018-11-20 18:16:29 -05:00
|
|
|
#endif
|
2019-01-04 09:52:26 -05:00
|
|
|
init_mux = 1;
|
MEDIUM: connection: Add private connections synchronously in session server list
When a connection is marked as private, it is now added in the session server
list. We don't wait a stream is detached from the mux to do so. When the
connection is created, this happens after the mux creation. Otherwise, it is
performed when the connection is marked as private.
To allow that, when a connection is created, the session is systematically set
as the connectin owner. Thus, a backend connection has always a owner during its
creation. And a private connection has always a owner until its death.
Note that outside the detach() callback, if the call to session_add_conn()
failed, the error is ignored. In this situation, we retry to add the connection
into the session server list in the detach() callback. If this fails at this
step, the multiplexer is destroyed and the connection is closed.
2020-07-01 10:10:06 -04:00
|
|
|
|
2013-12-20 05:09:51 -05:00
|
|
|
/* process the case where the server requires the PROXY protocol to be sent */
|
|
|
|
|
srv_conn->send_proxy_ofs = 0;
|
2017-11-03 11:27:47 -04:00
|
|
|
|
2025-03-02 21:58:46 -05:00
|
|
|
if (srv && (srv->pp_opts & SRV_PP_ENABLED)) {
|
2019-05-22 07:44:48 -04:00
|
|
|
srv_conn->flags |= CO_FL_SEND_PROXY;
|
2013-12-20 05:09:51 -05:00
|
|
|
srv_conn->send_proxy_ofs = 1; /* must compute size */
|
|
|
|
|
}
|
2012-05-11 12:32:18 -04:00
|
|
|
|
2019-05-22 07:44:48 -04:00
|
|
|
if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
|
|
|
|
|
srv_conn->send_proxy_ofs = 1;
|
|
|
|
|
srv_conn->flags |= CO_FL_SOCKS4;
|
|
|
|
|
}
|
2021-10-18 08:39:57 -04:00
|
|
|
|
|
|
|
|
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
|
|
|
|
/* if websocket stream, try to update connection ALPN. */
|
|
|
|
|
if (unlikely(s->flags & SF_WEBSOCKET) &&
|
|
|
|
|
srv && srv->use_ssl && srv->ssl_ctx.alpn_str) {
|
|
|
|
|
char *alpn = "";
|
|
|
|
|
int force = 0;
|
|
|
|
|
|
|
|
|
|
switch (srv->ws) {
|
|
|
|
|
case SRV_WS_AUTO:
|
|
|
|
|
alpn = "\x08http/1.1";
|
|
|
|
|
force = 0;
|
|
|
|
|
break;
|
|
|
|
|
case SRV_WS_H1:
|
|
|
|
|
alpn = "\x08http/1.1";
|
|
|
|
|
force = 1;
|
|
|
|
|
break;
|
|
|
|
|
case SRV_WS_H2:
|
|
|
|
|
alpn = "\x02h2";
|
|
|
|
|
force = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!conn_update_alpn(srv_conn, ist(alpn), force))
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("update alpn for websocket", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2021-10-18 08:39:57 -04:00
|
|
|
}
|
|
|
|
|
#endif
|
2013-12-20 05:09:51 -05:00
|
|
|
}
|
2019-10-25 11:00:54 -04:00
|
|
|
else {
|
2021-06-17 09:14:49 -04:00
|
|
|
s->flags |= SF_SRV_REUSED;
|
|
|
|
|
|
2021-03-05 09:27:41 -05:00
|
|
|
/* Currently there seems to be no known cases of xprt ready
|
|
|
|
|
* without the mux installed here.
|
|
|
|
|
*/
|
|
|
|
|
BUG_ON(!srv_conn->mux);
|
|
|
|
|
|
2023-11-28 08:27:51 -05:00
|
|
|
if (!(srv_conn->mux->ctl(srv_conn, MUX_CTL_STATUS, NULL) & MUX_STATUS_READY))
|
2021-06-17 09:14:49 -04:00
|
|
|
s->flags |= SF_SRV_REUSED_ANTICIPATED;
|
2019-10-25 11:00:54 -04:00
|
|
|
}
|
2010-03-29 13:36:59 -04:00
|
|
|
|
2012-03-02 08:35:21 -05:00
|
|
|
/* flag for logging source ip/port */
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_fe(s)->options2 & PR_O2_SRC_ADDR)
|
2022-03-30 10:26:39 -04:00
|
|
|
s->flags |= SF_SRC_ADDR;
|
2012-03-02 08:35:21 -05:00
|
|
|
|
2012-08-30 16:23:13 -04:00
|
|
|
/* disable lingering */
|
|
|
|
|
if (s->be->options & PR_O_TCP_NOLING)
|
2022-05-17 13:44:42 -04:00
|
|
|
s->scb->flags |= SC_FL_NOLINGER;
|
2012-08-30 16:23:13 -04:00
|
|
|
|
2018-12-14 05:35:36 -05:00
|
|
|
if (s->flags & SF_SRV_REUSED) {
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.reuse);
|
2018-12-15 09:11:36 -05:00
|
|
|
if (srv)
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.reuse);
|
2018-12-14 05:35:36 -05:00
|
|
|
} else {
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.connect);
|
2018-12-15 09:11:36 -05:00
|
|
|
if (srv)
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.connect);
|
2018-12-14 05:35:36 -05:00
|
|
|
}
|
|
|
|
|
|
2022-03-31 03:53:38 -04:00
|
|
|
err = do_connect_server(s, srv_conn);
|
2019-07-18 10:18:20 -04:00
|
|
|
if (err != SF_ERR_NONE)
|
|
|
|
|
return err;
|
|
|
|
|
|
2020-07-01 05:00:18 -04:00
|
|
|
#ifdef USE_OPENSSL
|
2024-05-23 12:31:48 -04:00
|
|
|
/* Set socket SNI unless connection is reused. */
|
|
|
|
|
if (srv && srv->ssl_ctx.sni && !(s->flags & SF_SRV_REUSED)) {
|
|
|
|
|
struct sample *sni_smp = NULL;
|
|
|
|
|
|
|
|
|
|
sni_smp = sample_fetch_as_type(s->be, s->sess, s,
|
|
|
|
|
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
|
|
|
|
|
srv->ssl_ctx.sni, SMP_T_STR);
|
2021-06-01 11:04:10 -04:00
|
|
|
if (smp_make_safe(sni_smp))
|
|
|
|
|
ssl_sock_set_servername(srv_conn, sni_smp->data.u.str.area);
|
|
|
|
|
}
|
2020-07-01 05:00:18 -04:00
|
|
|
#endif /* USE_OPENSSL */
|
|
|
|
|
|
2020-07-31 02:39:31 -04:00
|
|
|
/* The CO_FL_SEND_PROXY flag may have been set by the connect method,
|
|
|
|
|
* if so, add our handshake pseudo-XPRT now.
|
|
|
|
|
*/
|
|
|
|
|
if ((srv_conn->flags & CO_FL_HANDSHAKE)) {
|
|
|
|
|
if (xprt_add_hs(srv_conn) < 0) {
|
|
|
|
|
conn_full_close(srv_conn);
|
|
|
|
|
return SF_ERR_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-05 17:37:48 -05:00
|
|
|
conn_xprt_start(srv_conn);
|
2020-07-31 02:39:31 -04:00
|
|
|
|
2019-01-04 09:52:26 -05:00
|
|
|
/* We have to defer the mux initialization until after si_connect()
|
|
|
|
|
* has been called, as we need the xprt to have been properly
|
|
|
|
|
* initialized, or any attempt to recv during the mux init may
|
|
|
|
|
* fail, and flag the connection as CO_FL_ERROR.
|
|
|
|
|
*/
|
|
|
|
|
if (init_mux) {
|
2021-10-18 08:39:57 -04:00
|
|
|
const struct mux_ops *alt_mux =
|
|
|
|
|
likely(!(s->flags & SF_WEBSOCKET)) ? NULL : srv_get_ws_proto(srv);
|
2022-05-17 13:40:40 -04:00
|
|
|
if (conn_install_mux_be(srv_conn, s->scb, s->sess, alt_mux) < 0) {
|
2019-01-29 13:11:16 -05:00
|
|
|
conn_full_close(srv_conn);
|
2019-01-04 09:52:26 -05:00
|
|
|
return SF_ERR_INTERNAL;
|
2019-01-29 13:11:16 -05:00
|
|
|
}
|
2021-03-08 11:57:53 -05:00
|
|
|
if (IS_HTX_STRM(s)) {
|
2021-01-26 11:35:46 -05:00
|
|
|
/* If we're doing http-reuse always, and the connection
|
|
|
|
|
* is not private with available streams (an http2
|
|
|
|
|
* connection), add it to the available list, so that
|
|
|
|
|
* others can use it right away. If the connection is
|
|
|
|
|
* private or we're doing http-reuse safe and the mux
|
|
|
|
|
* protocol supports multiplexing, add it in the
|
|
|
|
|
* session server list.
|
|
|
|
|
*/
|
|
|
|
|
if (srv && reuse_mode == PR_O_REUSE_ALWS &&
|
|
|
|
|
!(srv_conn->flags & CO_FL_PRIVATE) &&
|
|
|
|
|
srv_conn->mux->avail_streams(srv_conn) > 0) {
|
2023-10-25 04:10:14 -04:00
|
|
|
srv_add_to_avail_list(srv, srv_conn);
|
2021-01-26 11:35:46 -05:00
|
|
|
}
|
|
|
|
|
else if (srv_conn->flags & CO_FL_PRIVATE ||
|
|
|
|
|
(reuse_mode == PR_O_REUSE_SAFE &&
|
|
|
|
|
srv_conn->mux->flags & MX_FL_HOL_RISK)) {
|
|
|
|
|
/* If it fail now, the same will be done in mux->detach() callback */
|
|
|
|
|
session_add_conn(s->sess, srv_conn, srv_conn->target);
|
|
|
|
|
}
|
MEDIUM: connection: Add private connections synchronously in session server list
When a connection is marked as private, it is now added in the session server
list. We don't wait a stream is detached from the mux to do so. When the
connection is created, this happens after the mux creation. Otherwise, it is
performed when the connection is marked as private.
To allow that, when a connection is created, the session is systematically set
as the connectin owner. Thus, a backend connection has always a owner during its
creation. And a private connection has always a owner until its death.
Note that outside the detach() callback, if the call to session_add_conn()
failed, the error is ignored. In this situation, we retry to add the connection
into the session server list in the detach() callback. If this fails at this
step, the multiplexer is destroyed and the connection is closed.
2020-07-01 10:10:06 -04:00
|
|
|
}
|
2019-01-04 09:52:26 -05:00
|
|
|
}
|
2009-06-10 05:09:37 -04:00
|
|
|
|
2021-08-30 03:35:18 -04:00
|
|
|
#if defined(USE_OPENSSL) && (defined(OPENSSL_IS_BORINGSSL) || (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L))
|
2019-05-06 09:18:27 -04:00
|
|
|
|
2019-06-14 18:14:05 -04:00
|
|
|
if (!reuse && cli_conn && srv && srv_conn->mux &&
|
2017-11-03 11:27:47 -04:00
|
|
|
(srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA) &&
|
2019-05-03 16:46:27 -04:00
|
|
|
/* Only attempt to use early data if either the client sent
|
|
|
|
|
* early data, so that we know it can handle a 425, or if
|
2023-11-21 13:54:16 -05:00
|
|
|
* we are allowed to retry requests on early data failure, and
|
2019-05-03 16:46:27 -04:00
|
|
|
* it's our first try
|
|
|
|
|
*/
|
|
|
|
|
((cli_conn->flags & CO_FL_EARLY_DATA) ||
|
2022-03-29 10:08:44 -04:00
|
|
|
((s->be->retry_type & PR_RE_EARLY_ERROR) && !s->conn_retries)) &&
|
2023-10-10 12:00:38 -04:00
|
|
|
co_data(sc_oc(s->scb)) &&
|
2019-05-03 16:46:27 -04:00
|
|
|
srv_conn->flags & CO_FL_SSL_WAIT_HS)
|
2017-11-03 11:27:47 -04:00
|
|
|
srv_conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
|
2017-11-08 08:25:59 -05:00
|
|
|
#endif
|
2017-11-03 11:27:47 -04:00
|
|
|
|
2012-08-30 16:23:13 -04:00
|
|
|
/* set connect timeout */
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = tick_add_ifset(now_ms, s->be->timeout.connect);
|
2012-08-30 16:23:13 -04:00
|
|
|
|
2011-03-10 10:55:02 -05:00
|
|
|
if (srv) {
|
2017-06-08 08:04:45 -04:00
|
|
|
int count;
|
|
|
|
|
|
2015-04-02 19:14:29 -04:00
|
|
|
s->flags |= SF_CURR_SESS;
|
2021-04-06 05:44:07 -04:00
|
|
|
count = _HA_ATOMIC_ADD_FETCH(&srv->cur_sess, 1);
|
2017-06-08 08:04:45 -04:00
|
|
|
HA_ATOMIC_UPDATE_MAX(&srv->counters.cur_sess_max, count);
|
2021-02-17 10:01:37 -05:00
|
|
|
if (s->be->lbprm.server_take_conn)
|
2021-06-18 12:29:25 -04:00
|
|
|
s->be->lbprm.server_take_conn(srv);
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
2022-05-17 13:07:51 -04:00
|
|
|
/* Now handle synchronously connected sockets. We know the stream connector
|
2022-05-17 13:47:17 -04:00
|
|
|
* is at least in state SC_ST_CON. These ones typically are UNIX
|
2022-03-30 04:06:11 -04:00
|
|
|
* sockets, socket pairs, andoccasionally TCP connections on the
|
2020-03-04 10:42:03 -05:00
|
|
|
* loopback on a heavily loaded system.
|
|
|
|
|
*/
|
2023-04-14 06:03:50 -04:00
|
|
|
if (srv_conn->flags & CO_FL_ERROR)
|
2023-04-14 05:35:07 -04:00
|
|
|
s->scb->flags |= SC_FL_ERROR;
|
2020-03-04 10:42:03 -05:00
|
|
|
|
|
|
|
|
/* If we had early data, and the handshake ended, then
|
|
|
|
|
* we can remove the flag, and attempt to wake the task up,
|
|
|
|
|
* in the event there's an analyser waiting for the end of
|
|
|
|
|
* the handshake.
|
|
|
|
|
*/
|
|
|
|
|
if (!(srv_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS)))
|
2022-05-17 13:40:40 -04:00
|
|
|
sc_ep_clr(s->scb, SE_FL_WAIT_FOR_HS);
|
2020-03-04 10:42:03 -05:00
|
|
|
|
2022-05-27 03:03:30 -04:00
|
|
|
if (!sc_state_in(s->scb->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO) &&
|
2020-03-04 10:42:03 -05:00
|
|
|
(srv_conn->flags & CO_FL_WAIT_XPRT) == 0) {
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = TICK_ETERNITY;
|
2022-12-12 02:11:36 -05:00
|
|
|
sc_oc(s->scb)->flags |= CF_WRITE_EVENT;
|
2022-05-17 13:47:17 -04:00
|
|
|
if (s->scb->state == SC_ST_CON)
|
|
|
|
|
s->scb->state = SC_ST_RDY;
|
2020-03-04 10:42:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Report EOI on the channel if it was reached from the mux point of
|
|
|
|
|
* view.
|
|
|
|
|
*
|
|
|
|
|
* Note: This test is only required because si_cs_process is also the SI
|
|
|
|
|
* wake callback. Otherwise si_cs_recv()/si_cs_send() already take
|
|
|
|
|
* care of it.
|
|
|
|
|
*/
|
2023-03-22 09:53:11 -04:00
|
|
|
if (sc_ep_test(s->scb, SE_FL_EOI) && !(s->scb->flags & SC_FL_EOI)) {
|
|
|
|
|
s->scb->flags |= SC_FL_EOI;
|
|
|
|
|
sc_ic(s->scb)->flags |= CF_READ_EVENT;
|
|
|
|
|
}
|
2020-03-04 10:42:03 -05:00
|
|
|
|
2020-07-29 16:42:27 -04:00
|
|
|
/* catch all sync connect while the mux is not already installed */
|
|
|
|
|
if (!srv_conn->mux && !(srv_conn->flags & CO_FL_WAIT_XPRT)) {
|
|
|
|
|
if (conn_create_mux(srv_conn) < 0) {
|
|
|
|
|
conn_full_close(srv_conn);
|
|
|
|
|
return SF_ERR_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 19:14:29 -04:00
|
|
|
return SF_ERR_NONE; /* connection is OK */
|
2006-06-25 20:48:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function performs the "redispatch" part of a connection attempt. It
|
|
|
|
|
* will assign a server if required, queue the connection if required, and
|
|
|
|
|
* handle errors that might arise at this level. It can change the server
|
|
|
|
|
* state. It will return 1 if it encounters an error, switches the server
|
|
|
|
|
* state, or has to queue a connection. Otherwise, it will return 0 indicating
|
|
|
|
|
* that the connection is ready to use.
|
|
|
|
|
*/
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
int srv_redispatch_connect(struct stream *s)
|
2006-06-25 20:48:02 -04:00
|
|
|
{
|
2011-03-10 10:55:02 -05:00
|
|
|
struct server *srv;
|
2006-06-25 20:48:02 -04:00
|
|
|
int conn_err;
|
|
|
|
|
|
|
|
|
|
/* We know that we don't have any connection pending, so we will
|
|
|
|
|
* try to get a new one, and wait in this state if it's queued
|
|
|
|
|
*/
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
redispatch:
|
2014-04-24 14:47:57 -04:00
|
|
|
conn_err = assign_server_and_queue(s);
|
|
|
|
|
srv = objt_server(s->target);
|
2011-03-10 10:55:02 -05:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
switch (conn_err) {
|
|
|
|
|
case SRV_STATUS_OK:
|
|
|
|
|
break;
|
|
|
|
|
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
case SRV_STATUS_FULL:
|
|
|
|
|
/* The server has reached its maxqueue limit. Either PR_O_REDISP is set
|
|
|
|
|
* and we can redispatch to another server, or it is not and we return
|
|
|
|
|
* 503. This only makes sense in DIRECT mode however, because normal LB
|
|
|
|
|
* algorithms would never select such a server, and hash algorithms
|
2014-04-24 14:47:57 -04:00
|
|
|
* would bring us on the same server again. Note that s->target is set
|
2011-03-10 10:55:02 -05:00
|
|
|
* in this case.
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
*/
|
2015-04-02 19:14:29 -04:00
|
|
|
if (((s->flags & (SF_DIRECT|SF_FORCE_PRST)) == SF_DIRECT) &&
|
2014-04-24 14:47:57 -04:00
|
|
|
(s->be->options & PR_O_REDISP)) {
|
MEDIUM: stream: remove the confusing SF_ADDR_SET flag
This flag is no longer needed now that it must always match the presence
of a destination address on the backend conn_stream. Worse, before previous
patch, if it were to be accidently removed while the address is present, it
could result in a leak of that address since alloc_dst_address() would first
be called to flush it.
Its usage has a long history where addresses were stored in an area shared
with the connection, but as this is no longer the case, there's no reason
for putting this burden onto application-level code that should not focus
on setting obscure flags.
The only place where that made a small difference is in the dequeuing code
in case of queue redistribution, because previously the code would first
clear the flag, and only later when trying to deal with the queue, would
release the address. It's not even certain whether there would exist a
code path going to connect_server() without calling pendconn_dequeue()
first (e.g. retries on queue timeout maybe?).
Now the pendconn_dequeue() code will rely on SF_ASSIGNED to decide to
clear and release the address, since that flag is always set while in
a server's queue, and its clearance implies that we don't want to keep
the address. At least it remains consistent and there's no more risk of
leaking it.
2022-05-02 10:36:47 -04:00
|
|
|
s->flags &= ~(SF_DIRECT | SF_ASSIGNED);
|
2022-05-17 13:40:40 -04:00
|
|
|
sockaddr_free(&s->scb->dst);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
goto redispatch;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
|
|
|
|
s->conn_err_type = STRM_ET_QUEUE_ERR;
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
}
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.failed_conns);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
[BUG] fix the dequeuing logic to ensure that all requests get served
The dequeuing logic was completely wrong. First, a task was assigned
to all servers to process the queue, but this task was never scheduled
and was only woken up on session free. Second, there was no reservation
of server entries when a task was assigned a server. This means that
as long as the task was not connected to the server, its presence was
not accounted for. This was causing trouble when detecting whether or
not a server had reached maxconn. Third, during a redispatch, a session
could lose its place at the server's and get blocked because another
session at the same moment would have stolen the entry. Fourth, the
redispatch option did not work when maxqueue was reached for a server,
and it was not possible to do so without indefinitely hanging a session.
The root cause of all those problems was the lack of pre-reservation of
connections at the server's, and the lack of tracking of servers during
a redispatch. Everything relied on combinations of flags which could
appear similarly in quite distinct situations.
This patch is a major rework but there was no other solution, as the
internal logic was deeply flawed. The resulting code is cleaner, more
understandable, uses less magics and is overall more robust.
As an added bonus, "option redispatch" now works when maxqueue has
been reached on a server.
2008-06-20 09:04:11 -04:00
|
|
|
return 1;
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
case SRV_STATUS_NOSRV:
|
2011-03-10 10:55:02 -05:00
|
|
|
/* note: it is guaranteed that srv == NULL here */
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
}
|
[MEDIUM]: Prevent redispatcher from selecting the same server, version #3
When haproxy decides that session needs to be redispatched it chose a server,
but there is no guarantee for it to be a different one. So, it often
happens that selected server is exactly the same that it was previously, so
a client ends up with a 503 error anyway, especially when one sever has
much bigger weight than others.
Changes from the previous version:
- drop stupid and unnecessary SN_DIRECT changes
- assign_server(): use srvtoavoid to keep the old server and clear s->srv
so SRV_STATUS_NOSRV guarantees that t->srv == NULL (again)
and get_server_rr_with_conns has chances to work (previously
we were passing a NULL here)
- srv_redispatch_connect(): remove t->srv->cum_sess and t->srv->failed_conns
incrementing as t->srv was guaranteed to be NULL
- add avoididx to get_server_rr_with_conns. I hope I correctly understand this code.
- fix http_flush_cookie_flags() and move it to assign_server_and_queue()
directly. The code here was supposed to set CK_DOWN and clear CK_VALID,
but: (TX_CK_VALID | TX_CK_DOWN) == TX_CK_VALID == TX_CK_MASK so:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags ^= (TX_CK_VALID | TX_CK_DOWN);
was really a:
if ((txn->flags & TX_CK_MASK) == TX_CK_VALID)
txn->flags &= TX_CK_VALID
Now haproxy logs "--DI" after redispatching connection.
- defer srv->redispatches++ and s->be->redispatches++ so there
are called only if a conenction was redispatched, not only
supposed to.
- don't increment lbconn if redispatcher selected the same sarver
- don't count unsuccessfully redispatched connections as redispatched
connections
- don't count redispatched connections as errors, so:
- the number of connections effectively served by a server is:
srv->cum_sess - srv->failed_conns - srv->retries - srv->redispatches
and
SUM(servers->failed_conns) == be->failed_conns
- requires the "Don't increment server connections too much + fix retries" patch
- needs little more testing and probably some discussion so reverting to the RFC state
Tests #1:
retries 4
redispatch
i) 1 server(s): b (wght=1, down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request failed
ii) server(s): b (wght=1, down), u (wght=1, down)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=1, retr=0, redis=0
-> request FAILED
iii) 2 server(s): b (wght=1, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
iv) 2 server(s): b (wght=100, down), u (wght=1, up)
b) sessions=4, lbtot=1, err_conn=0, retr=3, redis=1
u) sessions=1, lbtot=1, err_conn=0, retr=0, redis=0
-> request OK
v) 1 server(s): b (down for first 4 SYNS)
b) sessions=5, lbtot=1, err_conn=0, retr=4, redis=0
-> request OK
Tests #2:
retries 4
i) 1 server(s): b (down)
b) sessions=5, lbtot=1, err_conn=1, retr=4, redis=0
-> request FAILED
2008-02-21 21:50:19 -05:00
|
|
|
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
2006-06-25 20:48:02 -04:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case SRV_STATUS_QUEUED:
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = tick_add_ifset(now_ms, s->be->timeout.queue);
|
2022-05-17 13:47:17 -04:00
|
|
|
s->scb->state = SC_ST_QUE;
|
BUG/MEDIUM: queue: deal with a rare TOCTOU in assign_server_and_queue()
After checking that a server or backend is full, it remains possible
to call pendconn_add() just after the last pending requests finishes, so
that there's no more connection on the server for very low maxconn (typ 1),
leaving new ones in queue till the timeout.
The approach depends on where the request was queued, though:
- when queued on a server, we can simply detect that we may dequeue
pending requests and wake them up, it will wake our request and
that's fine. This needs to be done in srv_redispatch_connect() when
the server is set.
- when queued on a backend, it means that all servers are done with
their requests. It means that all servers were full before the
check and all were empty after. In practice this will only concern
configs with less servers than threads. It's where the issue was
first spotted, and it's very hard to reproduce with more than one
server. In this case we need to load-balance again in order to find
a spare server (or even to fail). For this, we call the newly added
dedicated function pendconn_must_try_again() that tells whether or
not a blocked pending request was dequeued and needs to be retried.
This should be backported along with pendconn_must_try_again() to all
stable versions, but with extreme care because over time the queue's
locking evolved.
2024-07-22 02:29:28 -04:00
|
|
|
|
|
|
|
|
/* handle the unlikely event where we added to the server's
|
|
|
|
|
* queue just after checking the server was full and before
|
|
|
|
|
* it released its last entry (with extremely low maxconn).
|
|
|
|
|
* Not needed for backend queues, already handled in
|
|
|
|
|
* assign_server_and_queue().
|
|
|
|
|
*/
|
|
|
|
|
if (unlikely(srv && may_dequeue_tasks(srv, s->be)))
|
|
|
|
|
process_srv_queue(srv);
|
|
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
case SRV_STATUS_INTERNAL:
|
|
|
|
|
default:
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_OTHER;
|
[MAJOR] rework of the server FSM
srv_state has been removed from HTTP state machines, and states
have been split in either TCP states or analyzers. For instance,
the TARPIT state has just become a simple analyzer.
New flags have been added to the struct buffer to compensate this.
The high-level stream processors sometimes need to force a disconnection
without touching a file-descriptor (eg: report an error). But if
they touched BF_SHUTW or BF_SHUTR, the file descriptor would not
be closed. Thus, the two SHUT?_NOW flags have been added so that
an application can request a forced close which the stream interface
will be forced to obey.
During this change, a new BF_HIJACK flag was added. It will
be used for data generation, eg during a stats dump. It
prevents the producer on a buffer from sending data into it.
BF_SHUTR_NOW /* the producer must shut down for reads ASAP */
BF_SHUTW_NOW /* the consumer must shut down for writes ASAP */
BF_HIJACK /* the producer is temporarily replaced */
BF_SHUTW_NOW has precedence over BF_HIJACK. BF_HIJACK has
precedence over BF_MAY_FORWARD (so that it does not need it).
New functions buffer_shutr_now(), buffer_shutw_now(), buffer_abort()
are provided to manipulate BF_SHUT* flags.
A new type "stream_interface" has been added to describe both
sides of a buffer. A stream interface has states and error
reporting. The session now has two stream interfaces (one per
side). Each buffer has stream_interface pointers to both
consumer and producer sides.
The server-side file descriptor has moved to its stream interface,
so that even the buffer has access to it.
process_srv() has been split into three parts :
- tcp_get_connection() obtains a connection to the server
- tcp_connection_failed() tests if a previously attempted
connection has succeeded or not.
- process_srv_data() only manages the data phase, and in
this sense should be roughly equivalent to process_cli.
Little code has been removed, and a lot of old code has been
left in comments for now.
2008-10-19 01:30:41 -04:00
|
|
|
}
|
|
|
|
|
|
2011-03-10 10:55:02 -05:00
|
|
|
if (srv)
|
|
|
|
|
srv_inc_sess_ctr(srv);
|
2014-02-03 16:26:46 -05:00
|
|
|
if (srv)
|
|
|
|
|
srv_set_sess_last(srv);
|
2011-03-10 10:55:02 -05:00
|
|
|
if (srv)
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.failed_conns);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
2006-06-25 20:48:02 -04:00
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* release other streams waiting for this server */
|
2014-04-24 14:47:57 -04:00
|
|
|
if (may_dequeue_tasks(srv, s->be))
|
2021-06-22 12:47:51 -04:00
|
|
|
process_srv_queue(srv);
|
2006-06-25 20:48:02 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
/* if we get here, it's because we got SRV_STATUS_OK, which also
|
|
|
|
|
* means that the connection has not been queued.
|
|
|
|
|
*/
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 12:43:15 -05:00
|
|
|
/* Check if the connection request is in such a state that it can be aborted. */
|
|
|
|
|
static int back_may_abort_req(struct channel *req, struct stream *s)
|
|
|
|
|
{
|
2023-04-14 06:05:25 -04:00
|
|
|
return ((s->scf->flags & SC_FL_ERROR) ||
|
2023-04-13 10:37:37 -04:00
|
|
|
((s->scb->flags & (SC_FL_SHUT_WANTED|SC_FL_SHUT_DONE)) && /* empty and client aborted */
|
2023-10-10 12:00:38 -04:00
|
|
|
(!co_data(req) || (s->be->options & PR_O_ABRT_CLOSE))));
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-17 13:47:17 -04:00
|
|
|
/* Update back stream connector status for input states SC_ST_ASS, SC_ST_QUE,
|
|
|
|
|
* SC_ST_TAR. Other input states are simply ignored.
|
|
|
|
|
* Possible output states are SC_ST_CLO, SC_ST_TAR, SC_ST_ASS, SC_ST_REQ, SC_ST_CON
|
|
|
|
|
* and SC_ST_EST. Flags must have previously been updated for timeouts and other
|
2020-01-09 12:43:15 -05:00
|
|
|
* conditions.
|
|
|
|
|
*/
|
|
|
|
|
void back_try_conn_req(struct stream *s)
|
|
|
|
|
{
|
|
|
|
|
struct server *srv = objt_server(s->target);
|
2022-05-27 04:13:37 -04:00
|
|
|
struct stconn *sc = s->scb;
|
2020-01-09 12:43:15 -05:00
|
|
|
struct channel *req = &s->req;
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
if (sc->state == SC_ST_ASS) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* Server assigned to connection request, we have to try to connect now */
|
|
|
|
|
int conn_err;
|
|
|
|
|
|
|
|
|
|
/* Before we try to initiate the connection, see if the
|
|
|
|
|
* request may be aborted instead.
|
|
|
|
|
*/
|
|
|
|
|
if (back_may_abort_req(req, s)) {
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type |= STRM_ET_CONN_ABRT;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection aborted", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto abort_connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conn_err = connect_server(s);
|
|
|
|
|
srv = objt_server(s->target);
|
|
|
|
|
|
|
|
|
|
if (conn_err == SF_ERR_NONE) {
|
2022-05-17 13:47:17 -04:00
|
|
|
/* state = SC_ST_CON or SC_ST_EST now */
|
2020-01-09 12:43:15 -05:00
|
|
|
if (srv)
|
|
|
|
|
srv_inc_sess_ctr(srv);
|
|
|
|
|
if (srv)
|
|
|
|
|
srv_set_sess_last(srv);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection attempt", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We have received a synchronous error. We might have to
|
|
|
|
|
* abort, retry immediately or redispatch.
|
|
|
|
|
*/
|
|
|
|
|
if (conn_err == SF_ERR_INTERNAL) {
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_OTHER;
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (srv)
|
|
|
|
|
srv_inc_sess_ctr(srv);
|
|
|
|
|
if (srv)
|
|
|
|
|
srv_set_sess_last(srv);
|
|
|
|
|
if (srv)
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.failed_conns);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* release other streams waiting for this server */
|
|
|
|
|
sess_change_server(s, NULL);
|
|
|
|
|
if (may_dequeue_tasks(srv, s->be))
|
2021-06-22 12:47:51 -04:00
|
|
|
process_srv_queue(srv);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* Failed and not retryable. */
|
2023-04-13 10:10:23 -04:00
|
|
|
sc_abort(sc);
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags |= SC_FL_ERROR;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* we may need to know the position in the queue for logging */
|
|
|
|
|
pendconn_cond_unlink(s->pend_pos);
|
|
|
|
|
|
|
|
|
|
/* no stream was ever accounted for this server */
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("internal error during connection", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We are facing a retryable error, but we don't want to run a
|
|
|
|
|
* turn-around now, as the problem is likely a source port
|
|
|
|
|
* allocation problem, so we want to retry now.
|
|
|
|
|
*/
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CER;
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags &= ~SC_FL_ERROR;
|
2020-01-09 12:43:15 -05:00
|
|
|
back_handle_st_cer(s);
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection error, retry", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2022-05-27 04:13:37 -04:00
|
|
|
/* now sc->state is one of SC_ST_CLO, SC_ST_TAR, SC_ST_ASS, SC_ST_REQ */
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
2022-05-27 04:13:37 -04:00
|
|
|
else if (sc->state == SC_ST_QUE) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* connection request was queued, check for any update */
|
|
|
|
|
if (!pendconn_dequeue(s)) {
|
|
|
|
|
/* The connection is not in the queue anymore. Either
|
|
|
|
|
* we have a server connection slot available and we
|
|
|
|
|
* go directly to the assigned state, or we need to
|
|
|
|
|
* load-balance first and go to the INI state.
|
|
|
|
|
*/
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = TICK_ETERNITY;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (unlikely(!(s->flags & SF_ASSIGNED)))
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_REQ;
|
2020-01-09 12:43:15 -05:00
|
|
|
else {
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_ASS;
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("dequeue connection request", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Connection request still in queue... */
|
2022-03-29 13:02:31 -04:00
|
|
|
if (s->flags & SF_CONN_EXP) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* ... and timeout expired */
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = TICK_ETERNITY;
|
|
|
|
|
s->flags &= ~SF_CONN_EXP;
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* we may need to know the position in the queue for logging */
|
|
|
|
|
pendconn_cond_unlink(s->pend_pos);
|
|
|
|
|
|
|
|
|
|
if (srv)
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&srv->counters.failed_conns);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
2023-04-13 10:10:23 -04:00
|
|
|
sc_abort(sc);
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2020-01-09 12:43:15 -05:00
|
|
|
req->flags |= CF_WRITE_TIMEOUT;
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type)
|
|
|
|
|
s->conn_err_type = STRM_ET_QUEUE_TO;
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection request still queued", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Connection remains in queue, check if we have to abort it */
|
|
|
|
|
if (back_may_abort_req(req, s)) {
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* we may need to know the position in the queue for logging */
|
|
|
|
|
pendconn_cond_unlink(s->pend_pos);
|
|
|
|
|
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type |= STRM_ET_QUEUE_ABRT;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("abort queued connection request", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto abort_connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Nothing changed */
|
|
|
|
|
}
|
2022-05-27 04:13:37 -04:00
|
|
|
else if (sc->state == SC_ST_TAR) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* Connection request might be aborted */
|
|
|
|
|
if (back_may_abort_req(req, s)) {
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type |= STRM_ET_CONN_ABRT;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection aborted", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto abort_connection;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 13:02:31 -04:00
|
|
|
if (!(s->flags & SF_CONN_EXP))
|
2020-01-09 12:43:15 -05:00
|
|
|
return; /* still in turn-around */
|
|
|
|
|
|
2022-03-29 13:02:31 -04:00
|
|
|
s->flags &= ~SF_CONN_EXP;
|
|
|
|
|
s->conn_exp = TICK_ETERNITY;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* we keep trying on the same server as long as the stream is
|
|
|
|
|
* marked "assigned".
|
|
|
|
|
* FIXME: Should we force a redispatch attempt when the server is down ?
|
|
|
|
|
*/
|
|
|
|
|
if (s->flags & SF_ASSIGNED)
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_ASS;
|
2020-01-09 12:43:15 -05:00
|
|
|
else
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_REQ;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("retry connection now", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
abort_connection:
|
|
|
|
|
/* give up */
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = TICK_ETERNITY;
|
|
|
|
|
s->flags &= ~SF_CONN_EXP;
|
2023-04-13 10:10:23 -04:00
|
|
|
sc_abort(sc);
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 13:07:51 -04:00
|
|
|
/* This function initiates a server connection request on a stream connector
|
2022-05-17 13:47:17 -04:00
|
|
|
* already in SC_ST_REQ state. Upon success, the state goes to SC_ST_ASS for
|
2020-01-09 12:43:15 -05:00
|
|
|
* a real connection to a server, indicating that a server has been assigned,
|
2022-05-17 13:47:17 -04:00
|
|
|
* or SC_ST_RDY for a successful connection to an applet. It may also return
|
|
|
|
|
* SC_ST_QUE, or SC_ST_CLO upon error.
|
2020-01-09 12:43:15 -05:00
|
|
|
*/
|
|
|
|
|
void back_handle_st_req(struct stream *s)
|
|
|
|
|
{
|
2022-05-27 04:13:37 -04:00
|
|
|
struct stconn *sc = s->scb;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
if (sc->state != SC_ST_REQ)
|
2020-01-09 12:43:15 -05:00
|
|
|
return;
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
if (unlikely(obj_type(s->target) == OBJ_TYPE_APPLET)) {
|
2022-04-21 05:52:07 -04:00
|
|
|
struct appctx *appctx;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-05-27 05:23:05 -04:00
|
|
|
/* The target is an applet but the SC is in SC_ST_REQ. Thus it
|
|
|
|
|
* means no appctx are attached to the SC. Otherwise, it will be
|
2022-05-17 13:47:17 -04:00
|
|
|
* in SC_ST_RDY state. So, try to create the appctx now.
|
2022-04-21 05:52:07 -04:00
|
|
|
*/
|
2022-05-27 04:13:37 -04:00
|
|
|
BUG_ON(sc_appctx(sc));
|
|
|
|
|
appctx = sc_applet_create(sc, objt_applet(s->target));
|
2020-01-09 12:43:15 -05:00
|
|
|
if (!appctx) {
|
|
|
|
|
/* No more memory, let's immediately abort. Force the
|
|
|
|
|
* error code to ignore the ERR_LOCAL which is not a
|
|
|
|
|
* real error.
|
|
|
|
|
*/
|
|
|
|
|
s->flags &= ~(SF_ERR_MASK | SF_FINST_MASK);
|
|
|
|
|
|
2023-04-13 10:10:23 -04:00
|
|
|
sc_abort(sc);
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags |= SC_FL_ERROR;
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type = STRM_ET_CONN_RES;
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("failed to register applet", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("applet registered", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Try to assign a server */
|
|
|
|
|
if (srv_redispatch_connect(s) != 0) {
|
|
|
|
|
/* We did not get a server. Either we queued the
|
|
|
|
|
* connection request, or we encountered an error.
|
|
|
|
|
*/
|
2022-05-27 04:13:37 -04:00
|
|
|
if (sc->state == SC_ST_QUE) {
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection request queued", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we did not get any server, let's check the cause */
|
2023-04-13 10:10:23 -04:00
|
|
|
sc_abort(sc);
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags |= SC_FL_ERROR;
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type)
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_OTHER;
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection request failed", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The server is assigned */
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_ASS;
|
2020-01-09 12:43:15 -05:00
|
|
|
be_set_sess_last(s->be);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection request assigned to a server", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
end:
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
/* This function is called with (sc->state == SC_ST_CON) meaning that a
|
2020-01-09 12:43:15 -05:00
|
|
|
* connection was attempted and that the file descriptor is already allocated.
|
|
|
|
|
* We must check for timeout, error and abort. Possible output states are
|
2022-05-17 13:47:17 -04:00
|
|
|
* SC_ST_CER (error), SC_ST_DIS (abort), and SC_ST_CON (no change). This only
|
2020-01-09 12:43:15 -05:00
|
|
|
* works with connection-based streams. We know that there were no I/O event
|
|
|
|
|
* when reaching this function. Timeouts and errors are *not* cleared.
|
|
|
|
|
*/
|
|
|
|
|
void back_handle_st_con(struct stream *s)
|
|
|
|
|
{
|
2022-05-27 04:13:37 -04:00
|
|
|
struct stconn *sc = s->scb;
|
2020-01-09 12:43:15 -05:00
|
|
|
struct channel *req = &s->req;
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* the client might want to abort */
|
2023-04-13 10:37:37 -04:00
|
|
|
if ((s->scf->flags & SC_FL_SHUT_DONE) ||
|
|
|
|
|
((s->scb->flags & SC_FL_SHUT_WANTED) &&
|
2023-10-10 12:00:38 -04:00
|
|
|
(!co_data(req) || (s->be->options & PR_O_ABRT_CLOSE)))) {
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->flags |= SC_FL_NOLINGER;
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type |= STRM_ET_CONN_ABRT;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-05-17 13:47:17 -04:00
|
|
|
/* Note: state = SC_ST_DIS now */
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("client abort during connection attempt", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-10 00:17:03 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
2020-01-09 12:43:15 -05:00
|
|
|
/* retryable error ? */
|
2023-04-14 06:05:25 -04:00
|
|
|
if ((s->flags & SF_CONN_EXP) || (sc->flags & SC_FL_ERROR)) {
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
2023-04-14 06:05:25 -04:00
|
|
|
if ((sc->flags & SC_FL_ERROR))
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
2020-01-09 12:43:15 -05:00
|
|
|
else
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type = STRM_ET_CONN_TO;
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CER;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection failed, retry", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2020-01-10 00:17:03 -05:00
|
|
|
end:
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
/* This function is called with (sc->state == SC_ST_CER) meaning that a
|
2020-01-09 12:43:15 -05:00
|
|
|
* previous connection attempt has failed and that the file descriptor
|
|
|
|
|
* has already been released. Possible causes include asynchronous error
|
2022-05-17 13:47:17 -04:00
|
|
|
* notification and time out. Possible output states are SC_ST_CLO when
|
|
|
|
|
* retries are exhausted, SC_ST_TAR when a delay is wanted before a new
|
|
|
|
|
* connection attempt, SC_ST_ASS when it's wise to retry on the same server,
|
|
|
|
|
* and SC_ST_REQ when an immediate redispatch is wanted. The buffers are
|
2020-01-09 12:43:15 -05:00
|
|
|
* marked as in error state. Timeouts and errors are cleared before retrying.
|
|
|
|
|
*/
|
|
|
|
|
void back_handle_st_cer(struct stream *s)
|
|
|
|
|
{
|
2022-05-27 04:13:37 -04:00
|
|
|
struct stconn *sc = s->scb;
|
2023-04-14 06:05:25 -04:00
|
|
|
int must_tar = !!(sc->flags & SC_FL_ERROR);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = TICK_ETERNITY;
|
|
|
|
|
s->flags &= ~SF_CONN_EXP;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* we probably have to release last stream from the server */
|
|
|
|
|
if (objt_server(s->target)) {
|
2022-05-27 04:13:37 -04:00
|
|
|
struct connection *conn = sc_conn(sc);
|
2021-12-15 03:50:17 -05:00
|
|
|
|
2021-12-06 02:01:02 -05:00
|
|
|
health_adjust(__objt_server(s->target), HANA_STATUS_L4_ERR);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
if (s->flags & SF_CURR_SESS) {
|
|
|
|
|
s->flags &= ~SF_CURR_SESS;
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_DEC(&__objt_server(s->target)->cur_sess);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2023-04-14 06:05:25 -04:00
|
|
|
if ((sc->flags & SC_FL_ERROR) &&
|
2020-01-09 12:43:15 -05:00
|
|
|
conn && conn->err_code == CO_ER_SSL_MISMATCH_SNI) {
|
|
|
|
|
/* We tried to connect to a server which is configured
|
|
|
|
|
* with "verify required" and which doesn't have the
|
|
|
|
|
* "verifyhost" directive. The server presented a wrong
|
|
|
|
|
* certificate (a certificate for an unexpected name),
|
|
|
|
|
* which implies that we have used SNI in the handshake,
|
|
|
|
|
* and that the server doesn't have the associated cert
|
|
|
|
|
* and presented a default one.
|
|
|
|
|
*
|
|
|
|
|
* This is a serious enough issue not to retry. It's
|
|
|
|
|
* especially important because this wrong name might
|
|
|
|
|
* either be the result of a configuration error, and
|
|
|
|
|
* retrying will only hammer the server, or is caused
|
|
|
|
|
* by the use of a wrong SNI value, most likely
|
|
|
|
|
* provided by the client and we don't want to let the
|
|
|
|
|
* client provoke retries.
|
|
|
|
|
*/
|
2024-09-25 09:05:07 -04:00
|
|
|
s->conn_retries = s->max_retries;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_DEVEL("Bad SSL cert, disable connection retries", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ensure that we have enough retries left */
|
2024-09-25 09:05:07 -04:00
|
|
|
if (s->conn_retries >= s->max_retries || !(s->be->retry_type & PR_RE_CONN_FAILED)) {
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type) {
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (objt_server(s->target))
|
2021-04-06 07:53:36 -04:00
|
|
|
_HA_ATOMIC_INC(&objt_server(s->target)->counters.failed_conns);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.failed_conns);
|
2020-01-09 12:43:15 -05:00
|
|
|
sess_change_server(s, NULL);
|
|
|
|
|
if (may_dequeue_tasks(objt_server(s->target), s->be))
|
2021-06-22 12:47:51 -04:00
|
|
|
process_srv_queue(objt_server(s->target));
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-03-31 11:44:45 -04:00
|
|
|
/* shutw is enough to stop a connecting socket */
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags |= SC_FL_ERROR;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection failed", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
BUG/MAJOR: stream-int: Release SI endpoint on server side ASAP on retry
When a connection attempt failed, if a retry is possible, the SI endpoint on
the server side is immediately released, instead of waiting to establish a
new connection to a server. Thus, when the backend SI is switched from
SI_ST_CER state to SI_ST_REQ, SI_ST_ASS or SI_ST_TAR, its endpoint is
released. It is expected because the SI is moved to a state prior to the
connection stage ( < SI_ST_CONN). So it seems logical to not have any server
connection.
It is especially important if the retry is delayed (SI_ST_TAR or
SI_ST_QUE). Because, if the server connection is preserved, any error at the
connection level is unexpectedly relayed to the stream, via the
stream-interface, leading to an infinite loop in process_stream(). if
SI_FL_ERR flag is set on the backend SI in another state than SI_ST_CLO, an
internal goto is performed to resync the stream-interfaces. In addtition,
some ressources are not released ASAP.
This bug is quite old and was reported 1 or 2 times per years since the 2.2
(at least) with not enough information to catch it. It must be backported as
far as 2.2 with a special care because this part has moved several times and
after some observation period and feedback from users to be sure. For info,
in 2.0 and prior, the connection is released when an error is encountered in
SI_ST_CON or SI_ST_RDY states.
2021-06-01 08:06:05 -04:00
|
|
|
/* At this stage, we will trigger a connection retry (with or without
|
2021-12-16 08:44:31 -05:00
|
|
|
* redispatch). Thus we must reset the SI endpoint on the server side
|
BUG/MAJOR: stream-int: Release SI endpoint on server side ASAP on retry
When a connection attempt failed, if a retry is possible, the SI endpoint on
the server side is immediately released, instead of waiting to establish a
new connection to a server. Thus, when the backend SI is switched from
SI_ST_CER state to SI_ST_REQ, SI_ST_ASS or SI_ST_TAR, its endpoint is
released. It is expected because the SI is moved to a state prior to the
connection stage ( < SI_ST_CONN). So it seems logical to not have any server
connection.
It is especially important if the retry is delayed (SI_ST_TAR or
SI_ST_QUE). Because, if the server connection is preserved, any error at the
connection level is unexpectedly relayed to the stream, via the
stream-interface, leading to an infinite loop in process_stream(). if
SI_FL_ERR flag is set on the backend SI in another state than SI_ST_CLO, an
internal goto is performed to resync the stream-interfaces. In addtition,
some ressources are not released ASAP.
This bug is quite old and was reported 1 or 2 times per years since the 2.2
(at least) with not enough information to catch it. It must be backported as
far as 2.2 with a special care because this part has moved several times and
after some observation period and feedback from users to be sure. For info,
in 2.0 and prior, the connection is released when an error is encountered in
SI_ST_CON or SI_ST_RDY states.
2021-06-01 08:06:05 -04:00
|
|
|
* an close the attached connection. It is especially important to do it
|
|
|
|
|
* now if the retry is not immediately performed, to be sure to release
|
2021-06-12 06:55:27 -04:00
|
|
|
* resources as soon as possible and to not catch errors from the lower
|
BUG/MAJOR: stream-int: Release SI endpoint on server side ASAP on retry
When a connection attempt failed, if a retry is possible, the SI endpoint on
the server side is immediately released, instead of waiting to establish a
new connection to a server. Thus, when the backend SI is switched from
SI_ST_CER state to SI_ST_REQ, SI_ST_ASS or SI_ST_TAR, its endpoint is
released. It is expected because the SI is moved to a state prior to the
connection stage ( < SI_ST_CONN). So it seems logical to not have any server
connection.
It is especially important if the retry is delayed (SI_ST_TAR or
SI_ST_QUE). Because, if the server connection is preserved, any error at the
connection level is unexpectedly relayed to the stream, via the
stream-interface, leading to an infinite loop in process_stream(). if
SI_FL_ERR flag is set on the backend SI in another state than SI_ST_CLO, an
internal goto is performed to resync the stream-interfaces. In addtition,
some ressources are not released ASAP.
This bug is quite old and was reported 1 or 2 times per years since the 2.2
(at least) with not enough information to catch it. It must be backported as
far as 2.2 with a special care because this part has moved several times and
after some observation period and feedback from users to be sure. For info,
in 2.0 and prior, the connection is released when an error is encountered in
SI_ST_CON or SI_ST_RDY states.
2021-06-01 08:06:05 -04:00
|
|
|
* layers in an unexpected state (i.e < ST_CONN).
|
|
|
|
|
*
|
2022-05-17 13:07:51 -04:00
|
|
|
* Note: the stream connector will be switched to ST_REQ, ST_ASS or
|
2023-04-14 06:03:50 -04:00
|
|
|
* ST_TAR and SC_FL_ERROR and SF_CONN_EXP flags will be unset.
|
BUG/MAJOR: stream-int: Release SI endpoint on server side ASAP on retry
When a connection attempt failed, if a retry is possible, the SI endpoint on
the server side is immediately released, instead of waiting to establish a
new connection to a server. Thus, when the backend SI is switched from
SI_ST_CER state to SI_ST_REQ, SI_ST_ASS or SI_ST_TAR, its endpoint is
released. It is expected because the SI is moved to a state prior to the
connection stage ( < SI_ST_CONN). So it seems logical to not have any server
connection.
It is especially important if the retry is delayed (SI_ST_TAR or
SI_ST_QUE). Because, if the server connection is preserved, any error at the
connection level is unexpectedly relayed to the stream, via the
stream-interface, leading to an infinite loop in process_stream(). if
SI_FL_ERR flag is set on the backend SI in another state than SI_ST_CLO, an
internal goto is performed to resync the stream-interfaces. In addtition,
some ressources are not released ASAP.
This bug is quite old and was reported 1 or 2 times per years since the 2.2
(at least) with not enough information to catch it. It must be backported as
far as 2.2 with a special care because this part has moved several times and
after some observation period and feedback from users to be sure. For info,
in 2.0 and prior, the connection is released when an error is encountered in
SI_ST_CON or SI_ST_RDY states.
2021-06-01 08:06:05 -04:00
|
|
|
*/
|
2022-05-27 04:13:37 -04:00
|
|
|
if (sc_reset_endp(sc) < 0) {
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type)
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_OTHER;
|
2022-03-23 10:15:29 -04:00
|
|
|
|
|
|
|
|
if (objt_server(s->target))
|
|
|
|
|
_HA_ATOMIC_INC(&objt_server(s->target)->counters.internal_errors);
|
|
|
|
|
_HA_ATOMIC_INC(&s->be->be_counters.internal_errors);
|
|
|
|
|
sess_change_server(s, NULL);
|
|
|
|
|
if (may_dequeue_tasks(objt_server(s->target), s->be))
|
|
|
|
|
process_srv_queue(objt_server(s->target));
|
|
|
|
|
|
2022-03-31 11:44:45 -04:00
|
|
|
/* shutw is enough to stop a connecting socket */
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2023-04-14 05:35:07 -04:00
|
|
|
sc->flags |= SC_FL_ERROR;
|
2022-03-23 10:15:29 -04:00
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CLO;
|
2022-03-23 10:15:29 -04:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-23 10:15:29 -04:00
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("error resetting endpoint", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2022-03-23 10:15:29 -04:00
|
|
|
goto end;
|
|
|
|
|
}
|
BUG/MAJOR: stream-int: Release SI endpoint on server side ASAP on retry
When a connection attempt failed, if a retry is possible, the SI endpoint on
the server side is immediately released, instead of waiting to establish a
new connection to a server. Thus, when the backend SI is switched from
SI_ST_CER state to SI_ST_REQ, SI_ST_ASS or SI_ST_TAR, its endpoint is
released. It is expected because the SI is moved to a state prior to the
connection stage ( < SI_ST_CONN). So it seems logical to not have any server
connection.
It is especially important if the retry is delayed (SI_ST_TAR or
SI_ST_QUE). Because, if the server connection is preserved, any error at the
connection level is unexpectedly relayed to the stream, via the
stream-interface, leading to an infinite loop in process_stream(). if
SI_FL_ERR flag is set on the backend SI in another state than SI_ST_CLO, an
internal goto is performed to resync the stream-interfaces. In addtition,
some ressources are not released ASAP.
This bug is quite old and was reported 1 or 2 times per years since the 2.2
(at least) with not enough information to catch it. It must be backported as
far as 2.2 with a special care because this part has moved several times and
after some observation period and feedback from users to be sure. For info,
in 2.0 and prior, the connection is released when an error is encountered in
SI_ST_CON or SI_ST_RDY states.
2021-06-01 08:06:05 -04:00
|
|
|
|
2022-08-03 04:47:48 -04:00
|
|
|
s->conn_retries++;
|
2020-01-09 12:43:15 -05:00
|
|
|
stream_choose_redispatch(s);
|
|
|
|
|
|
2022-03-30 04:47:32 -04:00
|
|
|
if (must_tar) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* The error was an asynchronous connection error, and we will
|
|
|
|
|
* likely have to retry connecting to the same server, most
|
|
|
|
|
* likely leading to the same result. To avoid this, we wait
|
|
|
|
|
* MIN(one second, connect timeout) before retrying. We don't
|
|
|
|
|
* do it when the failure happened on a reused connection
|
|
|
|
|
* though.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int delay = 1000;
|
2021-06-17 09:14:49 -04:00
|
|
|
const int reused = (s->flags & SF_SRV_REUSED) &&
|
|
|
|
|
!(s->flags & SF_SRV_REUSED_ANTICIPATED);
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
if (s->be->timeout.connect && s->be->timeout.connect < delay)
|
|
|
|
|
delay = s->be->timeout.connect;
|
|
|
|
|
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type)
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
/* only wait when we're retrying on the same server */
|
2022-05-27 04:13:37 -04:00
|
|
|
if ((sc->state == SC_ST_ASS ||
|
2021-06-17 09:14:49 -04:00
|
|
|
(s->be->srv_act <= 1)) && !reused) {
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_TAR;
|
2022-03-29 13:02:31 -04:00
|
|
|
s->conn_exp = tick_add(now_ms, MS_TO_TICKS(delay));
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("retry a new connection", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-27 04:13:37 -04:00
|
|
|
/* This function is called with (sc->state == SC_ST_RDY) meaning that a
|
2020-01-09 12:43:15 -05:00
|
|
|
* connection was attempted, that the file descriptor is already allocated,
|
|
|
|
|
* and that it has succeeded. We must still check for errors and aborts.
|
2022-05-17 13:47:17 -04:00
|
|
|
* Possible output states are SC_ST_EST (established), SC_ST_CER (error),
|
|
|
|
|
* and SC_ST_DIS (abort). This only works with connection-based streams.
|
2020-01-09 12:43:15 -05:00
|
|
|
* Timeouts and errors are *not* cleared.
|
|
|
|
|
*/
|
|
|
|
|
void back_handle_st_rdy(struct stream *s)
|
|
|
|
|
{
|
2022-05-27 04:13:37 -04:00
|
|
|
struct stconn *sc = s->scb;
|
2020-01-09 12:43:15 -05:00
|
|
|
struct channel *req = &s->req;
|
|
|
|
|
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2022-04-21 05:52:07 -04:00
|
|
|
|
|
|
|
|
if (unlikely(obj_type(s->target) == OBJ_TYPE_APPLET)) {
|
2022-05-27 05:23:05 -04:00
|
|
|
/* Here the appctx must exists because the SC was set to
|
2022-05-17 13:47:17 -04:00
|
|
|
* SC_ST_RDY state when the appctx was created.
|
2022-04-21 05:52:07 -04:00
|
|
|
*/
|
2022-05-18 11:58:02 -04:00
|
|
|
BUG_ON(!sc_appctx(s->scb));
|
2022-04-21 05:52:07 -04:00
|
|
|
|
2023-04-27 03:46:02 -04:00
|
|
|
if (!s->logs.request_ts)
|
2023-04-28 03:16:15 -04:00
|
|
|
s->logs.request_ts = now_ns;
|
|
|
|
|
s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts);
|
2022-04-21 05:52:07 -04:00
|
|
|
be_set_sess_last(s->be);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 12:43:15 -05:00
|
|
|
/* We know the connection at least succeeded, though it could have
|
|
|
|
|
* since met an error for any other reason. At least it didn't time out
|
2021-01-07 23:35:52 -05:00
|
|
|
* even though the timeout might have been reported right after success.
|
2020-01-09 12:43:15 -05:00
|
|
|
* We need to take care of various situations here :
|
|
|
|
|
* - everything might be OK. We have to switch to established.
|
|
|
|
|
* - an I/O error might have been reported after a successful transfer,
|
|
|
|
|
* which is not retryable and needs to be logged correctly, and needs
|
|
|
|
|
* established as well
|
2022-05-17 13:47:17 -04:00
|
|
|
* - SC_ST_CON implies !CF_WROTE_DATA but not conversely as we could
|
2020-01-09 12:43:15 -05:00
|
|
|
* have validated a connection with incoming data (e.g. TCP with a
|
|
|
|
|
* banner protocol), or just a successful connect() probe.
|
|
|
|
|
* - the client might have requested a connection abort, this needs to
|
|
|
|
|
* be checked before we decide to retry anything.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* it's still possible to handle client aborts or connection retries
|
|
|
|
|
* before any data were sent.
|
|
|
|
|
*/
|
|
|
|
|
if (!(req->flags & CF_WROTE_DATA)) {
|
|
|
|
|
/* client abort ? */
|
2023-04-13 10:37:37 -04:00
|
|
|
if ((s->scf->flags & SC_FL_SHUT_DONE) ||
|
|
|
|
|
((s->scb->flags & SC_FL_SHUT_WANTED) &&
|
2023-10-10 12:00:38 -04:00
|
|
|
(!co_data(req) || (s->be->options & PR_O_ABRT_CLOSE)))) {
|
2020-01-09 12:43:15 -05:00
|
|
|
/* give up */
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->flags |= SC_FL_NOLINGER;
|
2023-04-13 10:23:48 -04:00
|
|
|
sc_shutdown(sc);
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type |= STRM_ET_CONN_ABRT;
|
2020-01-09 12:43:15 -05:00
|
|
|
if (s->srv_error)
|
2022-05-27 04:13:37 -04:00
|
|
|
s->srv_error(s, sc);
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("client abort during connection attempt", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* retryable error ? */
|
2023-04-14 06:05:25 -04:00
|
|
|
if (sc->flags & SC_FL_ERROR) {
|
2022-03-30 13:39:30 -04:00
|
|
|
if (!s->conn_err_type)
|
|
|
|
|
s->conn_err_type = STRM_ET_CONN_ERR;
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_CER;
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection failed, retry", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* data were sent and/or we had no error, back_establish() will
|
|
|
|
|
* now take over.
|
|
|
|
|
*/
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_STATE("connection established", STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2022-03-30 13:39:30 -04:00
|
|
|
s->conn_err_type = STRM_ET_NONE;
|
2022-05-27 04:13:37 -04:00
|
|
|
sc->state = SC_ST_EST;
|
2020-01-09 12:43:15 -05:00
|
|
|
|
|
|
|
|
end:
|
2022-03-31 03:16:34 -04:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
|
2020-01-09 12:43:15 -05:00
|
|
|
}
|
|
|
|
|
|
2014-05-16 05:48:10 -04:00
|
|
|
/* sends a log message when a backend goes down, and also sets last
|
|
|
|
|
* change date.
|
|
|
|
|
*/
|
|
|
|
|
void set_backend_down(struct proxy *be)
|
|
|
|
|
{
|
2024-04-30 06:04:57 -04:00
|
|
|
be->be_counters.last_change = ns_to_sec(now_ns);
|
2024-03-28 12:37:07 -04:00
|
|
|
_HA_ATOMIC_INC(&be->be_counters.down_trans);
|
2014-05-16 05:48:10 -04:00
|
|
|
|
2016-11-03 14:42:36 -04:00
|
|
|
if (!(global.mode & MODE_STARTING)) {
|
2017-11-24 10:50:31 -05:00
|
|
|
ha_alert("%s '%s' has no server available!\n", proxy_type_str(be), be->id);
|
2016-11-03 14:42:36 -04:00
|
|
|
send_log(be, LOG_EMERG, "%s %s has no server available!\n", proxy_type_str(be), be->id);
|
|
|
|
|
}
|
2014-05-16 05:48:10 -04:00
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* Apply RDP cookie persistence to the current stream. For this, the function
|
2010-05-24 14:27:29 -04:00
|
|
|
* tries to extract an RDP cookie from the request buffer, and look for the
|
|
|
|
|
* matching server in the list. If the server is found, it is assigned to the
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* stream. This always returns 1, and the analyser removes itself from the
|
2010-05-24 14:27:29 -04:00
|
|
|
* list. Nothing is performed if a server was already assigned.
|
|
|
|
|
*/
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit)
|
2010-05-24 14:27:29 -04:00
|
|
|
{
|
|
|
|
|
struct proxy *px = s->be;
|
|
|
|
|
int ret;
|
2012-04-23 10:16:37 -04:00
|
|
|
struct sample smp;
|
2010-05-24 14:27:29 -04:00
|
|
|
struct server *srv = px->srv;
|
2017-01-06 11:41:29 -05:00
|
|
|
uint16_t port;
|
|
|
|
|
uint32_t addr;
|
2010-05-24 14:27:29 -04:00
|
|
|
char *p;
|
|
|
|
|
|
2019-11-05 10:18:10 -05:00
|
|
|
DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
|
2010-05-24 14:27:29 -04:00
|
|
|
|
2015-04-02 19:14:29 -04:00
|
|
|
if (s->flags & SF_ASSIGNED)
|
2010-05-24 14:27:29 -04:00
|
|
|
goto no_cookie;
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
memset(&smp, 0, sizeof(smp));
|
2010-05-24 14:27:29 -04:00
|
|
|
|
2013-07-22 12:09:52 -04:00
|
|
|
ret = fetch_rdp_cookie_name(s, &smp, s->be->rdp_cookie_name, s->be->rdp_cookie_len);
|
2018-07-13 04:54:26 -04:00
|
|
|
if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || smp.data.u.str.data == 0)
|
2010-05-24 14:27:29 -04:00
|
|
|
goto no_cookie;
|
|
|
|
|
|
2017-01-06 11:41:29 -05:00
|
|
|
/* Considering an rdp cookie detected using acl, str ended with <cr><lf> and should return.
|
|
|
|
|
* The cookie format is <ip> "." <port> where "ip" is the integer corresponding to the
|
|
|
|
|
* server's IP address in network order, and "port" is the integer corresponding to the
|
|
|
|
|
* server's port in network order. Comments please Emeric.
|
|
|
|
|
*/
|
2018-07-13 04:54:26 -04:00
|
|
|
addr = strtoul(smp.data.u.str.area, &p, 10);
|
2010-05-24 14:27:29 -04:00
|
|
|
if (*p != '.')
|
|
|
|
|
goto no_cookie;
|
|
|
|
|
p++;
|
2017-01-06 11:41:29 -05:00
|
|
|
|
|
|
|
|
port = ntohs(strtoul(p, &p, 10));
|
2010-05-24 14:27:29 -04:00
|
|
|
if (*p != '.')
|
|
|
|
|
goto no_cookie;
|
|
|
|
|
|
2012-11-11 18:42:33 -05:00
|
|
|
s->target = NULL;
|
2010-05-24 14:27:29 -04:00
|
|
|
while (srv) {
|
2014-05-09 16:47:50 -04:00
|
|
|
if (srv->addr.ss_family == AF_INET &&
|
2017-01-06 11:41:29 -05:00
|
|
|
port == srv->svc_port &&
|
|
|
|
|
addr == ((struct sockaddr_in *)&srv->addr)->sin_addr.s_addr) {
|
2017-08-31 08:41:55 -04:00
|
|
|
if ((srv->cur_state != SRV_ST_STOPPED) || (px->options & PR_O_PERSIST)) {
|
2010-05-24 14:27:29 -04:00
|
|
|
/* we found the server and it is usable */
|
2015-04-02 19:14:29 -04:00
|
|
|
s->flags |= SF_DIRECT | SF_ASSIGNED;
|
2012-11-11 18:42:33 -05:00
|
|
|
s->target = &srv->obj_type;
|
2010-05-24 14:27:29 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
srv = srv->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
no_cookie:
|
|
|
|
|
req->analysers &= ~an_bit;
|
|
|
|
|
req->analyse_exp = TICK_ETERNITY;
|
2019-11-05 10:18:10 -05:00
|
|
|
DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
|
2010-05-24 14:27:29 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
[MEDIUM] stats: report server and backend cumulated downtime
Hello,
This patch implements new statistics for SLA calculation by adding new
field 'Dwntime' with total down time since restart (both HTTP/CSV) and
extending status field (HTTP) or inserting a new one (CSV) with time
showing how long each server/backend is in a current state. Additionaly,
down transations are also calculated and displayed for backends, so it is
possible to know how many times selected backend was down, generating "No
server is available to handle this request." error.
New information are presentetd in two different ways:
- for HTTP: a "human redable form", one of "100000d 23h", "23h 59m" or
"59m 59s"
- for CSV: seconds
I believe that seconds resolution is enough.
As there are more columns in the status page I decided to shrink some
names to make more space:
- Weight -> Wght
- Check -> Chk
- Down -> Dwn
Making described changes I also made some improvements and fixed some
small bugs:
- don't increment s->health above 's->rise + s->fall - 1'. Previously it
was incremented an then (re)set to 's->rise + s->fall - 1'.
- do not set server down if it is down already
- do not set server up if it is up already
- fix colspan in multiple places (mostly introduced by my previous patch)
- add missing "status" header to CSV
- fix order of retries/redispatches in server (CSV)
- s/Tthen/Then/
- s/server/backend/ in DATA_ST_PX_BE (dumpstats.c)
Changes from previous version:
- deal with negative time intervales
- don't relay on s->state (SRV_RUNNING)
- little reworked human_time + compacted format (no spaces). If needed it
can be used in the future for other purposes by optionally making "cnt"
as an argument
- leave set_server_down mostly unchanged
- only little reworked "process_chk: 9"
- additional fields in CSV are appended to the rigth
- fix "SEC" macro
- named arguments (human_time, be_downtime, srv_downtime)
Hope it is OK. If there are only cosmetic changes needed please fill free
to correct it, however if there are some bigger changes required I would
like to discuss it first or at last to know what exactly was changed
especially since I already put this patch into my production server. :)
Thank you,
Best regards,
Krzysztof Oledzki
2007-10-22 10:21:10 -04:00
|
|
|
int be_downtime(struct proxy *px) {
|
2024-04-30 06:04:57 -04:00
|
|
|
if (px->lbprm.tot_weight && px->be_counters.last_change < ns_to_sec(now_ns)) // ignore negative time
|
[MEDIUM] stats: report server and backend cumulated downtime
Hello,
This patch implements new statistics for SLA calculation by adding new
field 'Dwntime' with total down time since restart (both HTTP/CSV) and
extending status field (HTTP) or inserting a new one (CSV) with time
showing how long each server/backend is in a current state. Additionaly,
down transations are also calculated and displayed for backends, so it is
possible to know how many times selected backend was down, generating "No
server is available to handle this request." error.
New information are presentetd in two different ways:
- for HTTP: a "human redable form", one of "100000d 23h", "23h 59m" or
"59m 59s"
- for CSV: seconds
I believe that seconds resolution is enough.
As there are more columns in the status page I decided to shrink some
names to make more space:
- Weight -> Wght
- Check -> Chk
- Down -> Dwn
Making described changes I also made some improvements and fixed some
small bugs:
- don't increment s->health above 's->rise + s->fall - 1'. Previously it
was incremented an then (re)set to 's->rise + s->fall - 1'.
- do not set server down if it is down already
- do not set server up if it is up already
- fix colspan in multiple places (mostly introduced by my previous patch)
- add missing "status" header to CSV
- fix order of retries/redispatches in server (CSV)
- s/Tthen/Then/
- s/server/backend/ in DATA_ST_PX_BE (dumpstats.c)
Changes from previous version:
- deal with negative time intervales
- don't relay on s->state (SRV_RUNNING)
- little reworked human_time + compacted format (no spaces). If needed it
can be used in the future for other purposes by optionally making "cnt"
as an argument
- leave set_server_down mostly unchanged
- only little reworked "process_chk: 9"
- additional fields in CSV are appended to the rigth
- fix "SEC" macro
- named arguments (human_time, be_downtime, srv_downtime)
Hope it is OK. If there are only cosmetic changes needed please fill free
to correct it, however if there are some bigger changes required I would
like to discuss it first or at last to know what exactly was changed
especially since I already put this patch into my production server. :)
Thank you,
Best regards,
Krzysztof Oledzki
2007-10-22 10:21:10 -04:00
|
|
|
return px->down_time;
|
|
|
|
|
|
2024-04-30 06:04:57 -04:00
|
|
|
return ns_to_sec(now_ns) - px->be_counters.last_change + px->down_time;
|
[MEDIUM] stats: report server and backend cumulated downtime
Hello,
This patch implements new statistics for SLA calculation by adding new
field 'Dwntime' with total down time since restart (both HTTP/CSV) and
extending status field (HTTP) or inserting a new one (CSV) with time
showing how long each server/backend is in a current state. Additionaly,
down transations are also calculated and displayed for backends, so it is
possible to know how many times selected backend was down, generating "No
server is available to handle this request." error.
New information are presentetd in two different ways:
- for HTTP: a "human redable form", one of "100000d 23h", "23h 59m" or
"59m 59s"
- for CSV: seconds
I believe that seconds resolution is enough.
As there are more columns in the status page I decided to shrink some
names to make more space:
- Weight -> Wght
- Check -> Chk
- Down -> Dwn
Making described changes I also made some improvements and fixed some
small bugs:
- don't increment s->health above 's->rise + s->fall - 1'. Previously it
was incremented an then (re)set to 's->rise + s->fall - 1'.
- do not set server down if it is down already
- do not set server up if it is up already
- fix colspan in multiple places (mostly introduced by my previous patch)
- add missing "status" header to CSV
- fix order of retries/redispatches in server (CSV)
- s/Tthen/Then/
- s/server/backend/ in DATA_ST_PX_BE (dumpstats.c)
Changes from previous version:
- deal with negative time intervales
- don't relay on s->state (SRV_RUNNING)
- little reworked human_time + compacted format (no spaces). If needed it
can be used in the future for other purposes by optionally making "cnt"
as an argument
- leave set_server_down mostly unchanged
- only little reworked "process_chk: 9"
- additional fields in CSV are appended to the rigth
- fix "SEC" macro
- named arguments (human_time, be_downtime, srv_downtime)
Hope it is OK. If there are only cosmetic changes needed please fill free
to correct it, however if there are some bigger changes required I would
like to discuss it first or at last to know what exactly was changed
especially since I already put this patch into my production server. :)
Thank you,
Best regards,
Krzysztof Oledzki
2007-10-22 10:21:10 -04:00
|
|
|
}
|
2006-06-25 20:48:02 -04:00
|
|
|
|
2010-01-04 10:03:09 -05:00
|
|
|
/*
|
|
|
|
|
* This function returns a string containing the balancing
|
|
|
|
|
* mode of the proxy in a format suitable for stats.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const char *backend_lb_algo_str(int algo) {
|
|
|
|
|
|
|
|
|
|
if (algo == BE_LB_ALGO_RR)
|
|
|
|
|
return "roundrobin";
|
|
|
|
|
else if (algo == BE_LB_ALGO_SRR)
|
|
|
|
|
return "static-rr";
|
2012-02-13 11:12:08 -05:00
|
|
|
else if (algo == BE_LB_ALGO_FAS)
|
|
|
|
|
return "first";
|
2010-01-04 10:03:09 -05:00
|
|
|
else if (algo == BE_LB_ALGO_LC)
|
|
|
|
|
return "leastconn";
|
|
|
|
|
else if (algo == BE_LB_ALGO_SH)
|
|
|
|
|
return "source";
|
|
|
|
|
else if (algo == BE_LB_ALGO_UH)
|
|
|
|
|
return "uri";
|
|
|
|
|
else if (algo == BE_LB_ALGO_PH)
|
|
|
|
|
return "url_param";
|
|
|
|
|
else if (algo == BE_LB_ALGO_HH)
|
|
|
|
|
return "hdr";
|
|
|
|
|
else if (algo == BE_LB_ALGO_RCH)
|
|
|
|
|
return "rdp-cookie";
|
2022-04-25 04:25:34 -04:00
|
|
|
else if (algo == BE_LB_ALGO_SMP)
|
|
|
|
|
return "hash";
|
2016-11-26 09:52:04 -05:00
|
|
|
else if (algo == BE_LB_ALGO_NONE)
|
|
|
|
|
return "none";
|
2010-01-04 10:03:09 -05:00
|
|
|
else
|
2016-11-26 09:52:04 -05:00
|
|
|
return "unknown";
|
2010-01-04 10:03:09 -05:00
|
|
|
}
|
|
|
|
|
|
2007-11-01 16:39:54 -04:00
|
|
|
/* This function parses a "balance" statement in a backend section describing
|
|
|
|
|
* <curproxy>. It returns -1 if there is any error, otherwise zero. If it
|
2012-05-08 12:14:39 -04:00
|
|
|
* returns -1, it will write an error message into the <err> buffer which will
|
|
|
|
|
* automatically be allocated and must be passed as NULL. The trailing '\n'
|
|
|
|
|
* will not be written. The function must be called with <args> pointing to the
|
|
|
|
|
* first word after "balance".
|
2007-11-01 16:39:54 -04:00
|
|
|
*/
|
2012-05-08 12:14:39 -04:00
|
|
|
int backend_parse_balance(const char **args, char **err, struct proxy *curproxy)
|
2007-11-01 16:39:54 -04:00
|
|
|
{
|
|
|
|
|
if (!*(args[0])) {
|
|
|
|
|
/* if no option is set, use round-robin by default */
|
2007-11-29 09:38:04 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_RR;
|
2007-11-01 16:39:54 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
if (strcmp(args[0], "roundrobin") == 0) {
|
2007-11-29 09:38:04 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_RR;
|
2007-11-01 16:39:54 -04:00
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "static-rr") == 0) {
|
2009-10-03 06:56:50 -04:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_SRR;
|
|
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "first") == 0) {
|
2012-02-13 11:12:08 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_FAS;
|
|
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "leastconn") == 0) {
|
2008-03-10 17:04:20 -04:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_LC;
|
|
|
|
|
}
|
2019-01-14 12:14:27 -05:00
|
|
|
else if (!strncmp(args[0], "random", 6)) {
|
2018-05-03 01:20:40 -04:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_RND;
|
2019-01-14 12:14:27 -05:00
|
|
|
curproxy->lbprm.arg_opt1 = 2;
|
|
|
|
|
|
|
|
|
|
if (*(args[0] + 6) == '(' && *(args[0] + 7) != ')') { /* number of draws */
|
|
|
|
|
const char *beg;
|
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
|
|
beg = args[0] + 7;
|
|
|
|
|
curproxy->lbprm.arg_opt1 = strtol(beg, &end, 0);
|
|
|
|
|
|
|
|
|
|
if (*end != ')') {
|
|
|
|
|
if (!*end)
|
|
|
|
|
memprintf(err, "random : missing closing parenthesis.");
|
|
|
|
|
else
|
|
|
|
|
memprintf(err, "random : unexpected character '%c' after argument.", *end);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (curproxy->lbprm.arg_opt1 < 1) {
|
|
|
|
|
memprintf(err, "random : number of draws must be at least 1.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-03 01:20:40 -04:00
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "source") == 0) {
|
2007-11-29 09:38:04 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_SH;
|
2007-11-01 16:39:54 -04:00
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "uri") == 0) {
|
2008-04-27 17:25:55 -04:00
|
|
|
int arg = 1;
|
|
|
|
|
|
2007-11-29 09:38:04 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_UH;
|
2020-09-23 02:56:29 -04:00
|
|
|
curproxy->lbprm.arg_opt1 = 0; // "whole", "path-only"
|
2019-01-14 10:14:15 -05:00
|
|
|
curproxy->lbprm.arg_opt2 = 0; // "len"
|
|
|
|
|
curproxy->lbprm.arg_opt3 = 0; // "depth"
|
2012-05-19 05:19:54 -04:00
|
|
|
|
2008-04-27 17:25:55 -04:00
|
|
|
while (*args[arg]) {
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
if (strcmp(args[arg], "len") == 0) {
|
2008-04-27 17:25:55 -04:00
|
|
|
if (!*args[arg+1] || (atoi(args[arg+1]) <= 0)) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "%s : '%s' expects a positive integer (got '%s').", args[0], args[arg], args[arg+1]);
|
2008-04-27 17:25:55 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2019-01-14 10:14:15 -05:00
|
|
|
curproxy->lbprm.arg_opt2 = atoi(args[arg+1]);
|
2008-04-27 17:25:55 -04:00
|
|
|
arg += 2;
|
|
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[arg], "depth") == 0) {
|
2008-04-27 17:25:55 -04:00
|
|
|
if (!*args[arg+1] || (atoi(args[arg+1]) <= 0)) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "%s : '%s' expects a positive integer (got '%s').", args[0], args[arg], args[arg+1]);
|
2008-04-27 17:25:55 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
/* hint: we store the position of the ending '/' (depth+1) so
|
|
|
|
|
* that we avoid a comparison while computing the hash.
|
|
|
|
|
*/
|
2019-01-14 10:14:15 -05:00
|
|
|
curproxy->lbprm.arg_opt3 = atoi(args[arg+1]) + 1;
|
2008-04-27 17:25:55 -04:00
|
|
|
arg += 2;
|
|
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[arg], "whole") == 0) {
|
2020-09-23 02:05:47 -04:00
|
|
|
curproxy->lbprm.arg_opt1 |= 1;
|
2012-05-19 05:19:54 -04:00
|
|
|
arg += 1;
|
|
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[arg], "path-only") == 0) {
|
2020-09-23 02:56:29 -04:00
|
|
|
curproxy->lbprm.arg_opt1 |= 2;
|
|
|
|
|
arg += 1;
|
|
|
|
|
}
|
2008-04-27 17:25:55 -04:00
|
|
|
else {
|
2020-09-23 02:56:29 -04:00
|
|
|
memprintf(err, "%s only accepts parameters 'len', 'depth', 'path-only', and 'whole' (got '%s').", args[0], args[arg]);
|
2008-04-27 17:25:55 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-11-01 16:39:54 -04:00
|
|
|
}
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
else if (strcmp(args[0], "url_param") == 0) {
|
2007-11-01 17:48:15 -04:00
|
|
|
if (!*args[1]) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "%s requires an URL parameter name.", args[0]);
|
2007-11-01 17:48:15 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2007-11-29 09:38:04 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_PH;
|
2008-08-03 06:19:50 -04:00
|
|
|
|
2019-01-14 09:23:54 -05:00
|
|
|
free(curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_str = strdup(args[1]);
|
|
|
|
|
curproxy->lbprm.arg_len = strlen(args[1]);
|
2008-04-27 17:25:55 -04:00
|
|
|
if (*args[2]) {
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
if (strcmp(args[2], "check_post") != 0) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "%s only accepts 'check_post' modifier (got '%s').", args[0], args[2]);
|
2008-04-14 14:47:37 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-11-01 17:48:15 -04:00
|
|
|
}
|
2022-04-25 04:25:34 -04:00
|
|
|
else if (strcmp(args[0], "hash") == 0) {
|
|
|
|
|
if (!*args[1]) {
|
|
|
|
|
memprintf(err, "%s requires a sample expression.", args[0]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_SMP;
|
|
|
|
|
|
|
|
|
|
ha_free(&curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_str = strdup(args[1]);
|
|
|
|
|
curproxy->lbprm.arg_len = strlen(args[1]);
|
|
|
|
|
|
|
|
|
|
if (*args[2]) {
|
|
|
|
|
memprintf(err, "%s takes no other argument (got '%s').", args[0], args[2]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-03-25 08:02:10 -04:00
|
|
|
else if (!strncmp(args[0], "hdr(", 4)) {
|
|
|
|
|
const char *beg, *end;
|
|
|
|
|
|
|
|
|
|
beg = args[0] + 4;
|
|
|
|
|
end = strchr(beg, ')');
|
|
|
|
|
|
|
|
|
|
if (!end || end == beg) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "hdr requires an http header field name.");
|
2009-03-25 08:02:10 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_HH;
|
|
|
|
|
|
2019-01-14 09:28:53 -05:00
|
|
|
free(curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_len = end - beg;
|
|
|
|
|
curproxy->lbprm.arg_str = my_strndup(beg, end - beg);
|
2019-01-14 10:04:54 -05:00
|
|
|
curproxy->lbprm.arg_opt1 = 0;
|
2009-03-25 08:02:10 -04:00
|
|
|
|
|
|
|
|
if (*args[1]) {
|
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
|
|
|
if (strcmp(args[1], "use_domain_only") != 0) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "%s only accepts 'use_domain_only' modifier (got '%s').", args[0], args[1]);
|
2009-03-25 08:02:10 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2019-01-14 10:04:54 -05:00
|
|
|
curproxy->lbprm.arg_opt1 = 1;
|
2009-03-25 08:02:10 -04:00
|
|
|
}
|
|
|
|
|
}
|
2009-06-30 11:56:00 -04:00
|
|
|
else if (!strncmp(args[0], "rdp-cookie", 10)) {
|
|
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
|
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_RCH;
|
|
|
|
|
|
|
|
|
|
if ( *(args[0] + 10 ) == '(' ) { /* cookie name */
|
|
|
|
|
const char *beg, *end;
|
|
|
|
|
|
|
|
|
|
beg = args[0] + 11;
|
|
|
|
|
end = strchr(beg, ')');
|
|
|
|
|
|
|
|
|
|
if (!end || end == beg) {
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "rdp-cookie : missing cookie name.");
|
2009-06-30 11:56:00 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 09:28:53 -05:00
|
|
|
free(curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_str = my_strndup(beg, end - beg);
|
|
|
|
|
curproxy->lbprm.arg_len = end - beg;
|
2009-06-30 11:56:00 -04:00
|
|
|
}
|
|
|
|
|
else if ( *(args[0] + 10 ) == '\0' ) { /* default cookie name 'mstshash' */
|
2019-01-14 09:28:53 -05:00
|
|
|
free(curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_str = strdup("mstshash");
|
|
|
|
|
curproxy->lbprm.arg_len = strlen(curproxy->lbprm.arg_str);
|
2009-06-30 11:56:00 -04:00
|
|
|
}
|
|
|
|
|
else { /* syntax */
|
2012-05-08 12:14:39 -04:00
|
|
|
memprintf(err, "rdp-cookie : missing cookie name.");
|
2009-06-30 11:56:00 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-15 05:15:50 -05:00
|
|
|
else if (strcmp(args[0], "log-hash") == 0) {
|
2023-09-19 04:51:53 -04:00
|
|
|
if (!*args[1]) {
|
|
|
|
|
memprintf(err, "%s requires a converter list.", args[0]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
2023-11-15 05:15:50 -05:00
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_LH;
|
2023-09-19 04:51:53 -04:00
|
|
|
|
|
|
|
|
ha_free(&curproxy->lbprm.arg_str);
|
|
|
|
|
curproxy->lbprm.arg_str = strdup(args[1]);
|
|
|
|
|
}
|
2023-11-23 12:19:41 -05:00
|
|
|
else if (strcmp(args[0], "sticky") == 0) {
|
2023-11-15 05:15:50 -05:00
|
|
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
2024-03-28 12:24:53 -04:00
|
|
|
curproxy->lbprm.algo |= BE_LB_ALGO_SS;
|
2023-11-15 05:15:50 -05:00
|
|
|
}
|
2023-09-13 05:52:31 -04:00
|
|
|
else {
|
2023-11-23 12:19:41 -05:00
|
|
|
memprintf(err, "only supports 'roundrobin', 'static-rr', 'leastconn', 'source', 'uri', 'url_param', 'hash', 'hdr(name)', 'rdp-cookie(name)', 'log-hash' and 'sticky' options.");
|
2023-09-13 05:52:31 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-30 14:48:53 -05:00
|
|
|
|
|
|
|
|
/************************************************************************/
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
/* All supported sample and ACL keywords must be declared here. */
|
2007-11-30 14:48:53 -05:00
|
|
|
/************************************************************************/
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the number of enabled servers on the proxy.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2007-11-30 14:48:53 -05:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_nbsrv(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2007-11-30 14:48:53 -05:00
|
|
|
{
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
|
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
2015-05-11 09:20:49 -04:00
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2007-11-30 14:48:53 -05:00
|
|
|
|
2017-03-12 16:56:56 -04:00
|
|
|
smp->data.u.sint = be_usable_srv(px);
|
2007-11-30 14:48:53 -05:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
/* report in smp->flags a success or failure depending on the designated
|
2010-05-16 16:18:27 -04:00
|
|
|
* server's state. There is no match function involved since there's no pattern.
|
2012-04-19 11:16:54 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
2010-05-16 16:18:27 -04:00
|
|
|
*/
|
|
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_srv_is_up(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2010-05-16 16:18:27 -04:00
|
|
|
{
|
2012-04-23 17:55:44 -04:00
|
|
|
struct server *srv = args->data.srv;
|
2010-05-16 16:18:27 -04:00
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_BOOL;
|
2017-08-31 08:41:55 -04:00
|
|
|
if (!(srv->cur_admin & SRV_ADMF_MAINT) &&
|
|
|
|
|
(!(srv->check.state & CHK_ST_CONFIGURED) || (srv->cur_state != SRV_ST_STOPPED)))
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = 1;
|
2010-05-16 16:18:27 -04:00
|
|
|
else
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = 0;
|
2010-05-16 16:18:27 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the number of enabled servers on the proxy.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2008-09-03 13:03:03 -04:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_connslots(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2008-09-03 13:03:03 -04:00
|
|
|
{
|
|
|
|
|
struct server *iterator;
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
|
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
2012-04-19 13:28:33 -04:00
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = 0;
|
2012-04-20 05:37:56 -04:00
|
|
|
|
2021-10-12 12:48:05 -04:00
|
|
|
for (iterator = px->srv; iterator; iterator = iterator->next) {
|
2017-08-31 08:41:55 -04:00
|
|
|
if (iterator->cur_state == SRV_ST_STOPPED)
|
2008-09-03 13:03:03 -04:00
|
|
|
continue;
|
2012-04-20 05:37:56 -04:00
|
|
|
|
2008-09-03 13:03:03 -04:00
|
|
|
if (iterator->maxconn == 0 || iterator->maxqueue == 0) {
|
2011-12-16 11:06:15 -05:00
|
|
|
/* configuration is stupid */
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = -1; /* FIXME: stupid value! */
|
2008-09-03 13:03:03 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint += (iterator->maxconn - iterator->cur_sess)
|
2025-01-15 10:37:35 -05:00
|
|
|
+ (iterator->maxqueue - iterator->queueslength);
|
2008-09-03 13:03:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-16 11:06:15 -05:00
|
|
|
/* set temp integer to the id of the backend */
|
2010-12-15 08:04:51 -05:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_be_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2012-04-23 10:16:37 -04:00
|
|
|
{
|
2020-04-30 03:51:15 -04:00
|
|
|
struct proxy *px = NULL;
|
|
|
|
|
|
|
|
|
|
if (smp->strm)
|
|
|
|
|
px = smp->strm->be;
|
2020-05-06 03:42:04 -04:00
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
2020-04-30 03:51:15 -04:00
|
|
|
px = __objt_check(smp->sess->origin)->proxy;
|
|
|
|
|
if (!px)
|
2016-03-10 05:47:01 -05:00
|
|
|
return 0;
|
|
|
|
|
|
2012-04-23 12:53:56 -04:00
|
|
|
smp->flags = SMP_F_VOL_TXN;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2020-04-30 03:51:15 -04:00
|
|
|
smp->data.u.sint = px->uuid;
|
2010-12-15 08:04:51 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-12 08:08:05 -05:00
|
|
|
/* set string to the name of the backend */
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_be_name(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
2020-04-30 03:51:15 -04:00
|
|
|
struct proxy *px = NULL;
|
|
|
|
|
|
|
|
|
|
if (smp->strm)
|
|
|
|
|
px = smp->strm->be;
|
2020-05-06 03:42:04 -04:00
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
2020-04-30 03:51:15 -04:00
|
|
|
px = __objt_check(smp->sess->origin)->proxy;
|
|
|
|
|
if (!px)
|
2016-12-12 08:08:05 -05:00
|
|
|
return 0;
|
|
|
|
|
|
2020-04-30 03:51:15 -04:00
|
|
|
smp->data.u.str.area = (char *)px->id;
|
2018-07-13 04:54:26 -04:00
|
|
|
if (!smp->data.u.str.area)
|
2016-12-12 08:08:05 -05:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->data.type = SMP_T_STR;
|
|
|
|
|
smp->flags = SMP_F_CONST;
|
2018-07-13 04:54:26 -04:00
|
|
|
smp->data.u.str.data = strlen(smp->data.u.str.area);
|
2016-12-12 08:08:05 -05:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-16 11:06:15 -05:00
|
|
|
/* set temp integer to the id of the server */
|
2010-12-15 08:04:51 -05:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_srv_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2012-04-23 10:16:37 -04:00
|
|
|
{
|
2020-04-30 03:51:15 -04:00
|
|
|
struct server *srv = NULL;
|
2016-03-10 05:47:01 -05:00
|
|
|
|
2020-04-30 03:51:15 -04:00
|
|
|
if (smp->strm)
|
|
|
|
|
srv = objt_server(smp->strm->target);
|
2020-05-06 03:42:04 -04:00
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
2020-04-30 03:51:15 -04:00
|
|
|
srv = __objt_check(smp->sess->origin)->server;
|
|
|
|
|
if (!srv)
|
2011-02-23 08:27:06 -05:00
|
|
|
return 0;
|
|
|
|
|
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2020-04-30 03:51:15 -04:00
|
|
|
smp->data.u.sint = srv->puid;
|
2010-12-15 08:04:51 -05:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-30 04:58:14 -04:00
|
|
|
/* set string to the name of the server */
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_name(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
2020-04-30 03:51:15 -04:00
|
|
|
struct server *srv = NULL;
|
2019-10-30 04:58:14 -04:00
|
|
|
|
2020-04-30 03:51:15 -04:00
|
|
|
if (smp->strm)
|
|
|
|
|
srv = objt_server(smp->strm->target);
|
2020-05-06 03:42:04 -04:00
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
2020-04-30 03:51:15 -04:00
|
|
|
srv = __objt_check(smp->sess->origin)->server;
|
|
|
|
|
if (!srv)
|
2019-10-30 04:58:14 -04:00
|
|
|
return 0;
|
|
|
|
|
|
2020-04-30 03:51:15 -04:00
|
|
|
smp->data.u.str.area = srv->id;
|
2019-10-30 04:58:14 -04:00
|
|
|
if (!smp->data.u.str.area)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->data.type = SMP_T_STR;
|
|
|
|
|
smp->data.u.str.data = strlen(smp->data.u.str.area);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the number of connections per second reaching the backend.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2009-03-05 15:34:28 -05:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_be_sess_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2009-03-05 15:34:28 -05:00
|
|
|
{
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
|
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2024-04-29 10:05:27 -04:00
|
|
|
smp->data.u.sint = read_freq_ctr(&px->be_counters.sess_per_sec);
|
2009-03-05 15:34:28 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the number of concurrent connections on the backend.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2009-10-10 06:02:45 -04:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_be_conn(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2009-10-10 06:02:45 -04:00
|
|
|
{
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
|
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2021-10-12 12:48:05 -04:00
|
|
|
smp->data.u.sint = px->beconn;
|
2009-10-10 06:02:45 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 17:10:27 -04:00
|
|
|
/* set temp integer to the number of available connections across available
|
|
|
|
|
* servers on the backend.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_be_conn_free(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct server *iterator;
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
2018-06-14 17:10:27 -04:00
|
|
|
unsigned int maxconn;
|
|
|
|
|
|
2021-10-12 12:48:05 -04:00
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
|
|
|
|
|
2018-06-14 17:10:27 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = 0;
|
|
|
|
|
|
2021-10-12 12:48:05 -04:00
|
|
|
for (iterator = px->srv; iterator; iterator = iterator->next) {
|
2018-06-14 17:10:27 -04:00
|
|
|
if (iterator->cur_state == SRV_ST_STOPPED)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
px = iterator->proxy;
|
|
|
|
|
if (!srv_currently_usable(iterator) ||
|
|
|
|
|
((iterator->flags & SRV_F_BACKUP) &&
|
|
|
|
|
(px->srv_act || (iterator != px->lbprm.fbck && !(px->options & PR_O_USE_ALL_BK)))))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (iterator->maxconn == 0) {
|
|
|
|
|
/* one active server is unlimited, return -1 */
|
|
|
|
|
smp->data.u.sint = -1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxconn = srv_dynamic_maxconn(iterator);
|
|
|
|
|
if (maxconn > iterator->cur_sess)
|
|
|
|
|
smp->data.u.sint += maxconn - iterator->cur_sess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the total number of queued connections on the backend.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2009-10-10 06:02:45 -04:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_queue_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2009-10-10 06:02:45 -04:00
|
|
|
{
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
|
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
|
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2021-10-12 12:48:05 -04:00
|
|
|
smp->data.u.sint = px->totpend;
|
2009-10-10 06:02:45 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-16 11:06:15 -05:00
|
|
|
/* set temp integer to the total number of queued connections on the backend divided
|
2009-10-10 06:02:45 -04:00
|
|
|
* by the number of running servers and rounded up. If there is no running
|
|
|
|
|
* server, we return twice the total, just as if we had half a running server.
|
|
|
|
|
* This is more or less correct anyway, since we expect the last server to come
|
|
|
|
|
* back soon.
|
2012-04-20 05:37:56 -04:00
|
|
|
* Accepts exactly 1 argument. Argument is a backend, other types will lead to
|
2012-04-19 11:16:54 -04:00
|
|
|
* undefined behaviour.
|
2009-10-10 06:02:45 -04:00
|
|
|
*/
|
|
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_avg_queue_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2009-10-10 06:02:45 -04:00
|
|
|
{
|
2021-10-12 12:48:05 -04:00
|
|
|
struct proxy *px = args->data.prx;
|
2009-10-10 06:02:45 -04:00
|
|
|
int nbsrv;
|
2021-10-12 12:48:05 -04:00
|
|
|
|
|
|
|
|
if (px == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (px->cap & PR_CAP_DEF)
|
|
|
|
|
px = smp->px;
|
2009-10-10 06:02:45 -04:00
|
|
|
|
2012-04-23 10:16:37 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2009-10-10 06:02:45 -04:00
|
|
|
|
2017-03-12 16:56:56 -04:00
|
|
|
nbsrv = be_usable_srv(px);
|
2009-10-10 06:02:45 -04:00
|
|
|
|
|
|
|
|
if (nbsrv > 0)
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = (px->totpend + nbsrv - 1) / nbsrv;
|
2009-10-10 06:02:45 -04:00
|
|
|
else
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = px->totpend * 2;
|
2009-10-10 06:02:45 -04:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2007-11-30 14:48:53 -05:00
|
|
|
|
2012-04-19 11:16:54 -04:00
|
|
|
/* set temp integer to the number of concurrent connections on the server in the backend.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
2011-08-05 06:09:44 -04:00
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_srv_conn(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2011-08-05 06:09:44 -04:00
|
|
|
{
|
2012-04-23 12:53:56 -04:00
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2015-08-19 03:07:19 -04:00
|
|
|
smp->data.u.sint = args->data.srv->cur_sess;
|
2011-08-05 06:09:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 18:01:35 -04:00
|
|
|
/* set temp integer to the number of available connections on the server in the backend.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_conn_free(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
unsigned int maxconn;
|
|
|
|
|
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
|
|
|
|
|
if (args->data.srv->maxconn == 0) {
|
|
|
|
|
/* one active server is unlimited, return -1 */
|
|
|
|
|
smp->data.u.sint = -1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxconn = srv_dynamic_maxconn(args->data.srv);
|
|
|
|
|
if (maxconn > args->data.srv->cur_sess)
|
|
|
|
|
smp->data.u.sint = maxconn - args->data.srv->cur_sess;
|
|
|
|
|
else
|
|
|
|
|
smp->data.u.sint = 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-13 05:46:26 -04:00
|
|
|
/* set temp integer to the number of connections pending in the server's queue.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_queue(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
2025-01-15 10:37:35 -05:00
|
|
|
smp->data.u.sint = args->data.srv->queueslength;
|
2017-10-13 05:46:26 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-05 21:39:31 -05:00
|
|
|
/* set temp integer to the number of enabled servers on the proxy.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2015-05-11 09:42:45 -04:00
|
|
|
smp_fetch_srv_sess_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
2012-12-05 21:39:31 -05:00
|
|
|
{
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
2015-08-19 03:00:18 -04:00
|
|
|
smp->data.type = SMP_T_SINT;
|
2024-04-29 10:05:27 -04:00
|
|
|
smp->data.u.sint = read_freq_ctr(&args->data.srv->counters.sess_per_sec);
|
2012-12-05 21:39:31 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 10:03:45 -04:00
|
|
|
/* set temp integer to the server weight.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_weight(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct server *srv = args->data.srv;
|
|
|
|
|
struct proxy *px = srv->proxy;
|
|
|
|
|
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = (srv->cur_eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set temp integer to the server initial weight.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_iweight(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = args->data.srv->iweight;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set temp integer to the server user-specified weight.
|
|
|
|
|
* Accepts exactly 1 argument. Argument is a server, other types will lead to
|
|
|
|
|
* undefined behaviour.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_srv_uweight(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
|
|
|
|
{
|
|
|
|
|
smp->flags = SMP_F_VOL_TEST;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = args->data.srv->uweight;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-10 07:43:56 -05:00
|
|
|
static int
|
|
|
|
|
smp_fetch_be_server_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct proxy *px = NULL;
|
|
|
|
|
|
|
|
|
|
if (smp->strm)
|
|
|
|
|
px = smp->strm->be;
|
|
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
|
|
|
|
px = __objt_check(smp->sess->origin)->proxy;
|
|
|
|
|
if (!px)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->flags = SMP_F_VOL_TXN;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = TICKS_TO_MS(px->timeout.server);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
smp_fetch_be_tunnel_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct proxy *px = NULL;
|
|
|
|
|
|
|
|
|
|
if (smp->strm)
|
|
|
|
|
px = smp->strm->be;
|
|
|
|
|
else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
|
|
|
|
|
px = __objt_check(smp->sess->origin)->proxy;
|
|
|
|
|
if (!px)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->flags = SMP_F_VOL_TXN;
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = TICKS_TO_MS(px->timeout.tunnel);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-12 16:56:55 -04:00
|
|
|
static int sample_conv_nbsrv(const struct arg *args, struct sample *smp, void *private)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
|
|
|
|
|
if (!smp_make_safe(smp))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2018-07-13 04:54:26 -04:00
|
|
|
px = proxy_find_by_name(smp->data.u.str.area, PR_CAP_BE, 0);
|
2017-03-12 16:56:55 -04:00
|
|
|
if (!px)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
|
|
|
|
smp->data.u.sint = be_usable_srv(px);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 19:58:13 -04:00
|
|
|
static int
|
|
|
|
|
sample_conv_srv_queue(const struct arg *args, struct sample *smp, void *private)
|
|
|
|
|
{
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
struct server *srv;
|
|
|
|
|
char *bksep;
|
|
|
|
|
|
|
|
|
|
if (!smp_make_safe(smp))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
bksep = strchr(smp->data.u.str.area, '/');
|
|
|
|
|
|
|
|
|
|
if (bksep) {
|
|
|
|
|
*bksep = '\0';
|
|
|
|
|
px = proxy_find_by_name(smp->data.u.str.area, PR_CAP_BE, 0);
|
|
|
|
|
if (!px)
|
|
|
|
|
return 0;
|
|
|
|
|
smp->data.u.str.area = bksep + 1;
|
|
|
|
|
} else {
|
|
|
|
|
if (!(smp->px->cap & PR_CAP_BE))
|
|
|
|
|
return 0;
|
|
|
|
|
px = smp->px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srv = server_find_by_name(px, smp->data.u.str.area);
|
|
|
|
|
if (!srv)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
smp->data.type = SMP_T_SINT;
|
2025-01-15 10:37:35 -05:00
|
|
|
smp->data.u.sint = srv->queueslength;
|
2019-08-26 19:58:13 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
|
|
|
|
|
/* Note: must not be declared <const> as its list will be overwritten.
|
|
|
|
|
* Please take care of keeping this list alphabetically sorted.
|
|
|
|
|
*/
|
2013-06-21 17:16:39 -04:00
|
|
|
static struct sample_fetch_kw_list smp_kws = {ILH, {
|
2020-12-10 07:43:56 -05:00
|
|
|
{ "avg_queue", smp_fetch_avg_queue_size, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "be_conn", smp_fetch_be_conn, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "be_conn_free", smp_fetch_be_conn_free, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "be_id", smp_fetch_be_id, 0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
|
|
|
|
|
{ "be_name", smp_fetch_be_name, 0, NULL, SMP_T_STR, SMP_USE_BKEND, },
|
|
|
|
|
{ "be_server_timeout", smp_fetch_be_server_timeout, 0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
|
|
|
|
|
{ "be_sess_rate", smp_fetch_be_sess_rate, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "be_tunnel_timeout", smp_fetch_be_tunnel_timeout, 0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
|
|
|
|
|
{ "connslots", smp_fetch_connslots, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "nbsrv", smp_fetch_nbsrv, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "queue", smp_fetch_queue_size, ARG1(1,BE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_conn", smp_fetch_srv_conn, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_conn_free", smp_fetch_srv_conn_free, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_id", smp_fetch_srv_id, 0, NULL, SMP_T_SINT, SMP_USE_SERVR, },
|
|
|
|
|
{ "srv_is_up", smp_fetch_srv_is_up, ARG1(1,SRV), NULL, SMP_T_BOOL, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_name", smp_fetch_srv_name, 0, NULL, SMP_T_STR, SMP_USE_SERVR, },
|
|
|
|
|
{ "srv_queue", smp_fetch_srv_queue, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_sess_rate", smp_fetch_srv_sess_rate, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_weight", smp_fetch_srv_weight, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_iweight", smp_fetch_srv_iweight, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
|
|
|
|
{ "srv_uweight", smp_fetch_srv_uweight, ARG1(1,SRV), NULL, SMP_T_SINT, SMP_USE_INTRN, },
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
{ /* END */ },
|
|
|
|
|
}};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
|
|
|
|
|
|
2017-03-12 16:56:55 -04:00
|
|
|
/* Note: must not be declared <const> as its list will be overwritten */
|
|
|
|
|
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
2019-08-26 19:58:13 -04:00
|
|
|
{ "nbsrv", sample_conv_nbsrv, 0, NULL, SMP_T_STR, SMP_T_SINT },
|
|
|
|
|
{ "srv_queue", sample_conv_srv_queue, 0, NULL, SMP_T_STR, SMP_T_SINT },
|
2017-03-12 16:56:55 -04:00
|
|
|
{ /* END */ },
|
|
|
|
|
}};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
|
2012-04-19 12:42:05 -04:00
|
|
|
/* Note: must not be declared <const> as its list will be overwritten.
|
|
|
|
|
* Please take care of keeping this list alphabetically sorted.
|
|
|
|
|
*/
|
2013-06-21 17:16:39 -04:00
|
|
|
static struct acl_kw_list acl_kws = {ILH, {
|
MINOR: backend: rename sample fetch functions and declare the sample keywords
The following sample fetch functions were only usable by ACLs but are now
usable by sample fetches too :
avg_queue, be_conn, be_id, be_sess_rate, connslots, nbsrv,
queue, srv_conn, srv_id, srv_is_up, srv_sess_rate
The fetch functions have been renamed "smp_fetch_*".
2013-01-07 16:38:03 -05:00
|
|
|
{ /* END */ },
|
2007-11-30 14:48:53 -05:00
|
|
|
}};
|
|
|
|
|
|
2018-11-25 13:14:37 -05:00
|
|
|
INITCALL1(STG_REGISTER, acl_register_keywords, &acl_kws);
|
2007-11-30 14:48:53 -05:00
|
|
|
|
2006-06-25 20:48:02 -04:00
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|