]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: stick-tables: never leave used entries without expiration
authorWilly Tarreau <w@1wt.eu>
Wed, 3 Sep 2025 12:03:40 +0000 (12:03 +0000)
committerWilly Tarreau <w@1wt.eu>
Wed, 3 Sep 2025 13:51:13 +0000 (15:51 +0200)
When trying to kill/expire entries, if a ref-counted entry is found,
let's requeue it with its expiration timer instead of leaving it out,
because other ref-counters (e.g. peers) will not purge it otherwise,
leaving it orphan. This one seems trickier to trigger, though it seems
to happen sometimes when peers are late and a long resync is active
and competing with intense calls to process_table_expire() (i.e. when
no other acitvity is there).

This must be backported to 3.2. It's likely that older versions are
affected as well, but possibly differently since the expiration
mechanism changed between 3.1 and 3.2, so better not take unneeded
risks there.

src/stick_table.c

index 7d516c8a5210766e56b72f92f41717c7890bdb70..f9334213e2672d9ad7ae327b87c9a19204814c7a 100644 (file)
@@ -341,13 +341,14 @@ int stktable_trash_oldest(struct stktable *t, int to_batch)
                        ts = eb32_entry(eb, struct stksess, exp);
                        eb = eb32_next(eb);
 
-                       /* don't delete an entry which is currently referenced */
-                       if (HA_ATOMIC_LOAD(&ts->ref_cnt) != 0)
-                               continue;
-
+                       /* This entry's key is expired, we must delete it. It
+                        * may be properly requeued if the element is still in
+                        * use or not really expired though.
+                        */
                        eb32_delete(&ts->exp);
 
-                       if (ts->expire != ts->exp.key) {
+                       if (ts->expire != ts->exp.key || HA_ATOMIC_LOAD(&ts->ref_cnt) != 0) {
+                       requeue:
                                if (!tick_isset(ts->expire))
                                        continue;
 
@@ -385,7 +386,7 @@ int stktable_trash_oldest(struct stktable *t, int to_batch)
                         * existing ones already have the ref_cnt.
                         */
                        if (HA_ATOMIC_LOAD(&ts->ref_cnt))
-                               continue;
+                               goto requeue;
 
                        /* session expired, trash it */
                        ebmb_delete(&ts->key);
@@ -939,10 +940,6 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
                        ts = eb32_entry(eb, struct stksess, exp);
                        eb = eb32_next(eb);
 
-                       /* don't delete an entry which is currently referenced */
-                       if (HA_ATOMIC_LOAD(&ts->ref_cnt) != 0)
-                               continue;
-
                        if (updt_locked == 1) {
                                expired++;
                                if (expired == STKTABLE_MAX_UPDATES_AT_ONCE) {
@@ -952,9 +949,14 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
                                }
                        }
 
+                       /* This entry's key is expired, we must delete it. It
+                        * may be properly requeued if the element is still in
+                        * use or not really expired though.
+                        */
                        eb32_delete(&ts->exp);
 
-                       if (!tick_is_expired(ts->expire, now_ms)) {
+                       if (!tick_is_expired(ts->expire, now_ms) || HA_ATOMIC_LOAD(&ts->ref_cnt) != 0) {
+                       requeue:
                                if (!tick_isset(ts->expire))
                                        continue;
 
@@ -991,7 +993,7 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
                         * existing ones already have the ref_cnt.
                         */
                        if (HA_ATOMIC_LOAD(&ts->ref_cnt))
-                               continue;
+                               goto requeue;
 
                        /* session expired, trash it */
                        ebmb_delete(&ts->key);