]> git.kaiwu.me - haproxy.git/commit
BUG/MEDIUM: cache: do not create a second applet when two cache-use rules hit
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 21 Oct 2026 08:32:43 +0000 (08:32 +0000)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 12 Nov 2026 17:20:31 +0000 (18:20 +0100)
commita72de10226b265202f5820b48b7a52c99382a1b6
tree33310ed4ba691609f66c8bb35910e750fbd4b30a
parent171dff32dcbd3a6842055016ff3e3da0b1307b00
BUG/MEDIUM: cache: do not create a second applet when two cache-use rules hit

A configuration with two "http-request cache-use" rules that may both find
the requested object, for example two caches both fed by unconditional
"cache-store" rules, or a cache-use rule in a defaults section that is
repeated in the proxy, leaks memory and cache space on every request that
hits both of them. Each such request leaks one appctx and its task (about
1kB on x86_64) and leaves the row of the first cache's entry retained
forever. The memory grows with the number of requests until the process is
restarted, which affects the system owner, and the rows retained this way
can never be evicted anymore: once their entry expires, their blocks are
lost and the cache progressively shrinks, until it cannot store anything.
Any client may trigger this by simply requesting cached objects. With
2000 requests, "show pools" reports 2016 appctx used and "show cache"
reports a refcount of 2000 on the entry, which stays like this after all
the streams are gone.

The cache-use action is not a final one. On a hit, it sets s->target to
the cache applet and creates the applet on the back stream connector with
sc_applet_create(), after retaining the entry and detaching its row. The
next cache-use rule is still evaluated and, when it also hits, it calls
sc_applet_create() again on the same stream connector. The new appctx
takes the same sedesc, so the first one is not referenced anymore: it was
woken up once, found no request in its input buffer and waits for one
forever. Its release callback is never called, so the entry it retained is
never released and its row never reattached to the avail list.

Let's simply ignore the cache-use rule when the stream already has a
target: the response will be produced by the applet that is already
there. This is consistent with the stats applet that is only set up when
s->target is NULL. An alternative would be to make the action final on a
hit, but this would change the evaluation of the following rules for all
users. Note that "use-service" has the same weakness, it creates its
applet without checking whether one is already attached, so a cache hit
followed by a matching use-service rule could leak the cache applet the
same way. This one is left as is since use-service is a final action and
mixing it with a cache hit is not a sensible configuration.

This patch should be backported to all supported versions. In older versions,
http_action_req_cache_use() may register the applet with a different
function (si_register_handler(), stream_int_register_handler()) but the
fix remains the same: return ACT_RET_CONT at the beginning of the function
when s->target is set.
reg-tests/cache/multiple_cache_use.vtc [new file with mode: 0644]
src/cache.c