Skip to content

Commit 4546d94

Browse files
committed
feature: added the pool_size argument to the balancer keepalive FFI API.
The balancer keepalive cache has a single capacity per upstream {} block, set by balancer_keepalive (32 by default), and evicts the least recently used idle connection regardless of which pool it belongs to. When many pools share one upstream, as is typical when peers are picked dynamically in balancer_by_lua*, a burst to one peer can push out the idle connections of every other peer. ngx_http_lua_ffi_balancer_enable_keepalive() now takes a pool_size argument. A pool is the same key the cache already matches on for reuse: the peer address, the host given to set_current_peer() and the local address. When a connection is about to be cached and its pool already holds pool_size idle connections, the least recently used idle connection of that same pool is closed and its slot reused, instead of taking a slot from the global LRU. Items are inserted at the head of their hash bucket, so walking a bucket from its tail meets the oldest connection of the pool first. pool_size 0 keeps the previous behaviour. pool_size never raises the balancer_keepalive total, it only bounds one pool inside it. The key comparison used by the reuse path is factored out into ngx_http_lua_balancer_item_match() so that counting and reuse can never disagree on what a pool is. This changes the FFI signature, lua-resty-core needs to be updated accordingly.
1 parent d9d95ab commit 4546d94

2 files changed

Lines changed: 361 additions & 19 deletions

File tree

‎src/ngx_http_lua_balancer.c‎

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ typedef struct {
3434
struct ngx_http_lua_balancer_peer_data_s {
3535
ngx_uint_t keepalive_requests;
3636
ngx_msec_t keepalive_timeout;
37+
/* max idle connections cached per pool, 0 means no per-pool limit */
38+
ngx_uint_t keepalive_pool_size;
3739

3840
void *data;
3941

@@ -96,6 +98,13 @@ static ngx_connection_t *ngx_http_lua_balancer_get_cached_item(
9698
ngx_http_lua_srv_conf_t *lscf, ngx_peer_connection_t *pc, ngx_str_t *name);
9799
static ngx_uint_t ngx_http_lua_balancer_calc_hash(ngx_str_t *name,
98100
struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local);
101+
static ngx_int_t ngx_http_lua_balancer_item_match(
102+
ngx_http_lua_balancer_ka_item_t *item, ngx_uint_t hash, ngx_str_t *name,
103+
struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local);
104+
static ngx_http_lua_balancer_ka_item_t *ngx_http_lua_balancer_pool_full_item(
105+
ngx_http_lua_srv_conf_t *lscf, ngx_uint_t hash, ngx_str_t *name,
106+
struct sockaddr *sockaddr, socklen_t socklen, ngx_addr_t *local,
107+
ngx_uint_t pool_size);
99108

100109

101110
static struct sockaddr *ngx_http_lua_balancer_default_server_sockaddr;
@@ -430,6 +439,7 @@ ngx_http_lua_balancer_get_peer(ngx_peer_connection_t *pc, void *data)
430439
bp->more_tries = 0;
431440
bp->keepalive_requests = 0;
432441
bp->keepalive_timeout = 0;
442+
bp->keepalive_pool_size = 0;
433443
bp->keepalive = 0;
434444
bp->total_tries++;
435445

@@ -633,7 +643,41 @@ ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,
633643
goto invalid;
634644
}
635645

636-
if (ngx_queue_empty(&lscf->balancer.free)) {
646+
host = &bp->host;
647+
hash = ngx_http_lua_balancer_calc_hash(host,
648+
bp->sockaddr, bp->socklen,
649+
pc->local);
650+
651+
item = NULL;
652+
653+
if (bp->keepalive_pool_size) {
654+
item = ngx_http_lua_balancer_pool_full_item(lscf, hash, host,
655+
bp->sockaddr, bp->socklen,
656+
pc->local,
657+
bp->keepalive_pool_size);
658+
}
659+
660+
if (item != NULL) {
661+
/*
662+
* the pool already holds pool_size idle connections: evict
663+
* its least recently used one instead of pushing out the
664+
* idle connections of other pools
665+
*/
666+
667+
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0,
668+
"lua balancer: keepalive pool full, "
669+
"evicting connection %p, pool size: %ui, "
670+
"host: %V, name: %V",
671+
item->connection, bp->keepalive_pool_size,
672+
bp->addr_text, host);
673+
674+
q = &item->queue;
675+
ngx_queue_remove(q);
676+
ngx_queue_remove(&item->hnode);
677+
678+
ngx_http_lua_balancer_close(item->connection);
679+
680+
} else if (ngx_queue_empty(&lscf->balancer.free)) {
637681
q = ngx_queue_last(&lscf->balancer.cache);
638682

639683
item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t,
@@ -651,16 +695,12 @@ ngx_http_lua_balancer_free_peer(ngx_peer_connection_t *pc, void *data,
651695
queue);
652696
}
653697

654-
host = &bp->host;
655698
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0,
656699
"lua balancer: keepalive saving connection %p, "
657700
"host: %V, name: %V",
658701
c, bp->addr_text, host);
659702

660703
ngx_queue_insert_head(&lscf->balancer.cache, q);
661-
hash = ngx_http_lua_balancer_calc_hash(host,
662-
bp->sockaddr, bp->socklen,
663-
pc->local);
664704
item->hash = hash;
665705
hash %= lscf->balancer.bucket_cnt;
666706
ngx_queue_insert_head(&lscf->balancer.buckets[hash], &item->hnode);
@@ -1024,7 +1064,8 @@ ngx_http_lua_ffi_balancer_bind_to_local_addr(ngx_http_request_t *r,
10241064

10251065
int
10261066
ngx_http_lua_ffi_balancer_enable_keepalive(ngx_http_request_t *r,
1027-
unsigned long timeout, unsigned int max_requests, char **err)
1067+
unsigned long timeout, unsigned int max_requests, unsigned int pool_size,
1068+
char **err)
10281069
{
10291070
ngx_http_upstream_t *u;
10301071
ngx_http_lua_ctx_t *ctx;
@@ -1062,6 +1103,7 @@ ngx_http_lua_ffi_balancer_enable_keepalive(ngx_http_request_t *r,
10621103

10631104
bp->keepalive_timeout = (ngx_msec_t) timeout;
10641105
bp->keepalive_requests = (ngx_uint_t) max_requests;
1106+
bp->keepalive_pool_size = (ngx_uint_t) pool_size;
10651107
bp->keepalive = 1;
10661108

10671109
return NGX_OK;
@@ -1477,20 +1519,9 @@ ngx_http_lua_balancer_get_cached_item(ngx_http_lua_srv_conf_t *lscf,
14771519
q = ngx_queue_next(q))
14781520
{
14791521
item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, hnode);
1480-
if (item->hash != hash) {
1481-
continue;
1482-
}
14831522

1484-
if (name->len == item->host.len
1485-
&& ngx_memn2cmp((u_char *) &item->sockaddr,
1486-
(u_char *) sockaddr,
1487-
item->socklen, socklen) == 0
1488-
&& ngx_strncasecmp(name->data,
1489-
item->host.data, name->len) == 0
1490-
&& (local == NULL
1491-
|| ngx_memn2cmp((u_char *) &item->local_sockaddr,
1492-
(u_char *) local->sockaddr,
1493-
socklen, local->socklen) == 0))
1523+
if (ngx_http_lua_balancer_item_match(item, hash, name, sockaddr,
1524+
socklen, local))
14941525
{
14951526
c = item->connection;
14961527
ngx_queue_remove(q);
@@ -1516,4 +1547,75 @@ ngx_http_lua_balancer_get_cached_item(ngx_http_lua_srv_conf_t *lscf,
15161547
return NULL;
15171548
}
15181549

1550+
1551+
static ngx_int_t
1552+
ngx_http_lua_balancer_item_match(ngx_http_lua_balancer_ka_item_t *item,
1553+
ngx_uint_t hash, ngx_str_t *name, struct sockaddr *sockaddr,
1554+
socklen_t socklen, ngx_addr_t *local)
1555+
{
1556+
if (item->hash != hash) {
1557+
return 0;
1558+
}
1559+
1560+
return name->len == item->host.len
1561+
&& ngx_memn2cmp((u_char *) &item->sockaddr,
1562+
(u_char *) sockaddr,
1563+
item->socklen, socklen) == 0
1564+
&& ngx_strncasecmp(name->data,
1565+
item->host.data, name->len) == 0
1566+
&& (local == NULL
1567+
|| ngx_memn2cmp((u_char *) &item->local_sockaddr,
1568+
(u_char *) local->sockaddr,
1569+
socklen, local->socklen) == 0);
1570+
}
1571+
1572+
1573+
/*
1574+
* returns the least recently used idle item of the given pool if the pool
1575+
* already caches pool_size or more idle connections, NULL otherwise
1576+
*/
1577+
static ngx_http_lua_balancer_ka_item_t *
1578+
ngx_http_lua_balancer_pool_full_item(ngx_http_lua_srv_conf_t *lscf,
1579+
ngx_uint_t hash, ngx_str_t *name, struct sockaddr *sockaddr,
1580+
socklen_t socklen, ngx_addr_t *local, ngx_uint_t pool_size)
1581+
{
1582+
ngx_uint_t n;
1583+
ngx_queue_t *q;
1584+
ngx_queue_t *head;
1585+
ngx_http_lua_balancer_ka_item_t *item, *oldest;
1586+
1587+
head = &lscf->balancer.buckets[hash % lscf->balancer.bucket_cnt];
1588+
1589+
n = 0;
1590+
oldest = NULL;
1591+
1592+
/*
1593+
* items are inserted at the bucket head, so walking from the tail meets
1594+
* the least recently used item of the pool first
1595+
*/
1596+
1597+
for (q = ngx_queue_last(head);
1598+
q != ngx_queue_sentinel(head);
1599+
q = ngx_queue_prev(q))
1600+
{
1601+
item = ngx_queue_data(q, ngx_http_lua_balancer_ka_item_t, hnode);
1602+
1603+
if (!ngx_http_lua_balancer_item_match(item, hash, name, sockaddr,
1604+
socklen, local))
1605+
{
1606+
continue;
1607+
}
1608+
1609+
if (oldest == NULL) {
1610+
oldest = item;
1611+
}
1612+
1613+
if (++n >= pool_size) {
1614+
return oldest;
1615+
}
1616+
}
1617+
1618+
return NULL;
1619+
}
1620+
15191621
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */

0 commit comments

Comments
 (0)