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.
This commit is contained in:
Amaury Denoyelle 2021-03-02 14:38:53 +01:00
parent 68967e595b
commit 8ede3db080
4 changed files with 31 additions and 24 deletions

View File

@ -492,7 +492,7 @@ enum conn_hash_params_t {
* connection hash.
*/
struct conn_hash_params {
struct server *srv;
void *target;
XXH64_hash_t sni_prehash;
struct sockaddr_storage *src_addr;
struct sockaddr_storage *dst_addr;

View File

@ -429,6 +429,15 @@ static inline void sockaddr_free(struct sockaddr_storage **sap)
*sap = NULL;
}
/* returns 0 if the connection is valid and is a frontend connection, otherwise
* returns 1 indicating it's a backend connection. And uninitialized connection
* also returns 1 to better handle the usage in the middle of initialization.
*/
static inline int conn_is_back(const struct connection *conn)
{
return !objt_listener(conn->target);
}
/* Tries to allocate a new connection and initialized its main fields. The
* connection is returned on success, NULL on failure. The connection must
* be released using pool_free() or conn_free().
@ -444,8 +453,9 @@ static inline struct connection *conn_new(void *target)
conn_init(conn, target);
if (obj_type(target) == OBJ_TYPE_SERVER) {
srv_use_conn(__objt_server(target), conn);
if (conn_is_back(conn)) {
if (obj_type(target) == OBJ_TYPE_SERVER)
srv_use_conn(__objt_server(target), conn);
hash_node = conn_alloc_hash_node(conn);
if (unlikely(!hash_node)) {
@ -968,15 +978,6 @@ static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
return item ? item->mux : NULL;
}
/* returns 0 if the connection is valid and is a frontend connection, otherwise
* returns 1 indicating it's a backend connection. And uninitialized connection
* also returns 1 to better handle the usage in the middle of initialization.
*/
static inline int conn_is_back(const struct connection *conn)
{
return !objt_listener(conn->target);
}
/* returns a pointer to the proxy associated with this connection. For a front
* connection it returns a pointer to the frontend ; for a back connection, it
* returns a pointer to the backend.

View File

@ -1277,14 +1277,18 @@ int connect_server(struct stream *s)
int64_t hash = 0;
struct conn_hash_params hash_params;
/* in standard configuration, srv will be valid
* it can be NULL for dispatch mode or transparent backend */
srv = objt_server(s->target);
/* first, set unique connection parameters and then calculate hash */
memset(&hash_params, 0, sizeof(hash_params));
srv = objt_server(s->target);
hash_params.srv = srv;
/* 1. target */
hash_params.target = s->target;
#ifdef USE_OPENSSL
/* 1. sni */
/* 2. sni */
if (srv && srv->ssl_ctx.sni) {
sni_smp = sample_fetch_as_type(s->be, s->sess, s,
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
@ -1302,7 +1306,7 @@ int connect_server(struct stream *s)
}
#endif /* USE_OPENSSL */
/* 2. destination address */
/* 3. destination address */
if (!(s->flags & SF_ADDR_SET)) {
err = alloc_dst_address(&s->target_addr, srv, s);
if (err != SRV_STATUS_OK)
@ -1314,14 +1318,14 @@ int connect_server(struct stream *s)
if (srv && (!is_addr(&srv->addr) || srv->flags & SRV_F_MAPPORTS))
hash_params.dst_addr = s->target_addr;
/* 3. source address */
/* 4. source address */
err = alloc_bind_address(&bind_addr, srv, s);
if (err != SRV_STATUS_OK)
return SF_ERR_INTERNAL;
hash_params.src_addr = bind_addr;
/* 4. proxy protocol */
/* 5. proxy protocol */
if (srv && srv->pp_opts) {
proxy_line_ret = make_proxy_line(trash.area, trash.size, srv, cli_conn, s);
if (proxy_line_ret) {
@ -1330,8 +1334,7 @@ int connect_server(struct stream *s)
}
}
if (srv)
hash = conn_calculate_hash(&hash_params);
hash = conn_calculate_hash(&hash_params);
/* This will catch some corner cases such as lying connections resulting from
* retries or connect timeouts but will rarely trigger.
@ -1503,7 +1506,11 @@ skip_reuse:
if (srv_conn) {
srv_conn->owner = s->sess;
if (reuse_mode == PR_O_REUSE_NEVR)
/* 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)
conn_set_private(srv_conn);
/* assign bind_addr to srv_conn */
@ -1729,8 +1736,7 @@ skip_reuse:
}
}
if (srv)
srv_conn->hash_node->hash = hash;
srv_conn->hash_node->hash = hash;
return SF_ERR_NONE; /* connection is OK */
}

View File

@ -1465,7 +1465,7 @@ XXH64_hash_t conn_calculate_hash(const struct conn_hash_params *params)
buf = trash.area;
conn_hash_update(buf, &idx, &params->srv, sizeof(params->srv), &hash_flags, 0);
conn_hash_update(buf, &idx, &params->target, sizeof(params->target), &hash_flags, 0);
if (params->sni_prehash) {
conn_hash_update(buf, &idx,