MEDIUM: stick-table: move process_table_expire() to a single thread

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.
This commit is contained in:
Willy Tarreau 2025-09-10 18:47:50 +02:00
parent 2831cb104f
commit e05afda249

View File

@ -740,9 +740,11 @@ void stktable_requeue_exp(struct stktable *t, const struct stksess *ts)
new_exp = tick_first(expire, old_exp); new_exp = tick_first(expire, old_exp);
} }
task_queue(t->exp_task);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->lock); 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 /* 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) int stktable_init(struct stktable *t, char **err_msg)
{ {
static int operating_thread = 0;
int peers_retval = 0; int peers_retval = 0;
int shard; int shard;
int i; 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); t->pool = create_pool("sticktables", sizeof(struct stksess) + round_ptr_size(t->data_size) + t->key_size, MEM_F_SHARED);
if ( t->expire ) { if ( t->expire ) {
t->exp_task = task_new_anywhere(); t->exp_task = task_new_on(operating_thread);
if (!t->exp_task) if (!t->exp_task)
goto mem_error; goto mem_error;
operating_thread = (operating_thread + 1) % global.nbthread;
t->exp_task->process = process_table_expire; t->exp_task->process = process_table_expire;
t->exp_task->context = (void *)t; t->exp_task->context = (void *)t;
} }