Upstream: proxy_next_upstream_unique directive

Previously, nginx never picked the same peer twice during a proxy session.
The proxy_next_upstream_unique directive allows to change this behavior
and choose a peer from scratch each time.  By default, the feature is
enabled, which is the old behavior.
This commit is contained in:
Roman Arutyunyan 2026-04-18 10:53:58 +04:00
parent 843cd60630
commit 73cd2e5bda
8 changed files with 43 additions and 10 deletions

View file

@ -69,6 +69,7 @@ struct ngx_peer_connection_s {
unsigned transparent:1;
unsigned so_keepalive:1;
unsigned down:1;
unsigned unique:1;
/* ngx_connection_log_error_e */
unsigned log_error:2;

View file

@ -582,6 +582,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_delay),
NULL },
{ ngx_string("proxy_next_upstream_unique"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_unique),
NULL },
{ ngx_string("proxy_pass_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
@ -3530,6 +3537,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
conf->upstream.next_upstream_unique = NGX_CONF_UNSET;
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.socket_keepalive = NGX_CONF_UNSET;
@ -3663,6 +3671,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.force_ranges,
prev->upstream.force_ranges, 0);
ngx_conf_merge_value(conf->upstream.next_upstream_unique,
prev->upstream.next_upstream_unique, 1);
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);

View file

@ -308,7 +308,9 @@ found:
ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
hp->rrp.tried[n] |= m;
if (pc->unique) {
hp->rrp.tried[n] |= m;
}
return NGX_OK;
}
@ -713,7 +715,9 @@ found:
n = best_i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));
hp->rrp.tried[n] |= m;
if (pc->unique) {
hp->rrp.tried[n] |= m;
}
return NGX_OK;
}

View file

@ -271,7 +271,9 @@ best_chosen:
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
rrp->tried[n] |= m;
if (pc->unique) {
rrp->tried[n] |= m;
}
ngx_http_upstream_rr_peers_unlock(peers);

View file

@ -327,7 +327,9 @@ found:
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
rrp->tried[n] |= m;
if (pc->unique) {
rrp->tried[n] |= m;
}
return NGX_OK;
}
@ -463,7 +465,9 @@ found:
ngx_http_upstream_rr_peers_unlock(peers);
rrp->tried[n] |= m;
if (pc->unique) {
rrp->tried[n] |= m;
}
return NGX_OK;
}

View file

@ -839,9 +839,10 @@ found:
}
u->peer.start_time = ngx_current_msec;
u->peer.unique = u->conf->next_upstream_unique;
if (u->conf->next_upstream_tries
&& u->peer.tries > u->conf->next_upstream_tries)
&& (!u->peer.unique || u->peer.tries > u->conf->next_upstream_tries))
{
u->peer.tries = u->conf->next_upstream_tries;
}
@ -1282,9 +1283,10 @@ ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
ur->ctx = NULL;
u->peer.start_time = ngx_current_msec;
u->peer.unique = u->conf->next_upstream_unique;
if (u->conf->next_upstream_tries
&& u->peer.tries > u->conf->next_upstream_tries)
&& (!u->peer.unique || u->peer.tries > u->conf->next_upstream_tries))
{
u->peer.tries = u->conf->next_upstream_tries;
}

View file

@ -186,6 +186,7 @@ typedef struct {
ngx_uint_t next_upstream;
ngx_uint_t store_access;
ngx_uint_t next_upstream_tries;
ngx_flag_t next_upstream_unique;
ngx_flag_t buffering;
ngx_flag_t request_buffering;
ngx_flag_t pass_request_headers;

View file

@ -713,7 +713,7 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
ngx_http_upstream_rr_peers_wlock(peers);
#if (NGX_HTTP_UPSTREAM_ZONE)
if (peers->config && rrp->config != *peers->config) {
if (pc->unique && peers->config && rrp->config != *peers->config) {
goto busy;
}
#endif
@ -919,7 +919,9 @@ best_chosen:
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
rrp->tried[n] |= m;
if (pc->unique) {
rrp->tried[n] |= m;
}
if (now - best->checked > best->fail_timeout) {
best->checked = now;
@ -1037,7 +1039,13 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
ngx_http_upstream_rr_peers_unlock(rrp->peers);
pc->tries = 0;
if (pc->unique) {
pc->tries = 0;
} else if (pc->tries) {
pc->tries--;
}
return;
}