]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stick-table: move process_table_expire() to a single thread
authorWilly Tarreau <w@1wt.eu>
Wed, 10 Sep 2025 16:47:50 +0000 (18:47 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 10 Sep 2025 17:13:33 +0000 (19:13 +0200)
A big deal of the task_queue() contention is caused by this function
because it's created using task_new_anywhere() and is subject to
heavy updates. Let's turn it to single thread by rotating the assigned
threads during initialization so that a table only runs on one thread
at a time.

However there's a trick: the function used to call task_queue() to
requeue the task if it had advanced its timer (may only happen when
learning an entry from a peer). We can't do that anymore since we can't
queue another thread's task. Thus instead of the task needs to be
scheduled earlier than previously planned, we simply perform a wakeup.
It will likely do nothing and will self-adjust its next wakeup timer.

Doing so halves the number of multi-thread task wakeups. In addition
the request rate at saturation increased by 12% with 16 peers and 40
tables on a 16 8-thread processes. This should improve the situation
described by Felipe in issues #3084 and #3101.

This should be backported to 3.2 after some extended checks.

src/stick_table.c

index b7f6f89bbafc4362a0a9edfef32a4f5d790e0d18..b3ff76878d329269664126e46d4f86bd31966a4d 100644 (file)
@@ -740,9 +740,11 @@ void stktable_requeue_exp(struct stktable *t, const struct stksess *ts)
                new_exp = tick_first(expire, old_exp);
        }
 
-       task_queue(t->exp_task);
-
        HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       /* the timer was advanced, only the task can update it */
+       if (!tick_isset(old_exp) || tick_is_lt(new_exp, old_exp))
+               task_wakeup(t->exp_task, TASK_WOKEN_OTHER);
 }
 
 /* Returns a valid or initialized stksess for the specified stktable_key in the
@@ -1087,6 +1089,7 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
  */
 int stktable_init(struct stktable *t, char **err_msg)
 {
+       static int operating_thread = 0;
        int peers_retval = 0;
        int shard;
        int i;
@@ -1106,9 +1109,11 @@ int stktable_init(struct stktable *t, char **err_msg)
                t->pool = create_pool("sticktables", sizeof(struct stksess) + round_ptr_size(t->data_size) + t->key_size, MEM_F_SHARED);
 
                if ( t->expire ) {
-                       t->exp_task = task_new_anywhere();
+                       t->exp_task = task_new_on(operating_thread);
                        if (!t->exp_task)
                                goto mem_error;
+                       operating_thread = (operating_thread + 1) % global.nbthread;
+
                        t->exp_task->process = process_table_expire;
                        t->exp_task->context = (void *)t;
                }