MEDIUM: stick-table: use a distinct lock for the updates tree

Updating an entry in the updates tree is currently performed under the
table's write lock, which causes huge contention with other accesses
such as lookups and free. Aside the updates tree, the update,
localupdate and commitupdate variables, nothing is manipulated, so
let's create a distinct lock (updt_lock) to protect these together
to remove this contention. It required to add an extra lock in the
few places where we delete the update (though only if we're really
going to delete it) to protect the tree. This is very convenient
because now peer_send_teachmsgs() only needs to take this read lock,
and there is very little contention left on the stick-table.

With this alone, the performance jumped from 614k to 1140k/s on a
80-thread machine with a peers section! Stick-table updates with
no peers however now has to stand two locks and slightly regressed
from 4.0-4.1M/s to 3.9-4.0. This is fairly minimal compared to the
significant unlocking of the peers updates and considered totally
acceptable.
This commit is contained in:
Willy Tarreau 2023-05-27 17:55:15 +00:00
parent 29982ea769
commit 87e072eea5
3 changed files with 30 additions and 12 deletions

View File

@ -208,6 +208,7 @@ struct stktable {
unsigned int update; /* uses updt_lock */
unsigned int localupdate; /* uses updt_lock */
unsigned int commitupdate;/* used to identify the latest local updates pending for sync, uses updt_lock */
__decl_thread(HA_RWLOCK_T updt_lock); /* lock protecting the updates part */
};
extern struct stktable_data_type stktable_data_types[STKTABLE_DATA_TYPES];

View File

@ -1576,9 +1576,9 @@ static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
new_pushed = 1;
if (locked)
HA_RWLOCK_WRTORD(STK_TABLE_LOCK, &st->table->lock);
else
HA_RWLOCK_RDLOCK(STK_TABLE_LOCK, &st->table->lock);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &st->table->lock);
HA_RWLOCK_RDLOCK(STK_TABLE_LOCK, &st->table->updt_lock);
while (1) {
struct stksess *ts;
@ -1600,10 +1600,10 @@ static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
}
HA_ATOMIC_INC(&ts->ref_cnt);
HA_RWLOCK_RDUNLOCK(STK_TABLE_LOCK, &st->table->lock);
HA_RWLOCK_RDUNLOCK(STK_TABLE_LOCK, &st->table->updt_lock);
ret = peer_send_updatemsg(st, appctx, ts, updateid, new_pushed, use_timed);
HA_RWLOCK_RDLOCK(STK_TABLE_LOCK, &st->table->lock);
HA_RWLOCK_RDLOCK(STK_TABLE_LOCK, &st->table->updt_lock);
HA_ATOMIC_DEC(&ts->ref_cnt);
if (ret <= 0)
break;
@ -1635,7 +1635,7 @@ static inline int peer_send_teachmsgs(struct appctx *appctx, struct peer *p,
}
out:
HA_RWLOCK_RDUNLOCK(STK_TABLE_LOCK, &st->table->lock);
HA_RWLOCK_RDUNLOCK(STK_TABLE_LOCK, &st->table->updt_lock);
if (locked)
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &st->table->lock);

View File

@ -119,7 +119,11 @@ int __stksess_kill(struct stktable *t, struct stksess *ts)
return 0;
eb32_delete(&ts->exp);
eb32_delete(&ts->upd);
if (ts->upd.node.leaf_p) {
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->updt_lock);
eb32_delete(&ts->upd);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->updt_lock);
}
ebmb_delete(&ts->key);
__stksess_free(t, ts);
return 1;
@ -278,7 +282,11 @@ int __stktable_trash_oldest(struct stktable *t, int to_batch)
/* session expired, trash it */
ebmb_delete(&ts->key);
eb32_delete(&ts->upd);
if (ts->upd.node.leaf_p) {
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->updt_lock);
eb32_delete(&ts->upd);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->updt_lock);
}
__stksess_free(t, ts);
batched++;
}
@ -441,7 +449,7 @@ void stktable_touch_with_exp(struct stktable *t, struct stksess *ts, int local,
* or not scheduled for at least one peer.
*/
if (!locked++)
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->lock);
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->updt_lock);
if (!ts->upd.node.leaf_p
|| (int)(t->commitupdate - ts->upd.key) >= 0
@ -460,7 +468,7 @@ void stktable_touch_with_exp(struct stktable *t, struct stksess *ts, int local,
else {
/* If this entry is not in the tree */
if (!locked++)
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->lock);
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->updt_lock);
if (!ts->upd.node.leaf_p) {
ts->upd.key= (++t->update)+(2147483648U);
@ -474,7 +482,7 @@ void stktable_touch_with_exp(struct stktable *t, struct stksess *ts, int local,
}
if (locked)
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->lock);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->updt_lock);
if (decrefcnt)
HA_ATOMIC_DEC(&ts->ref_cnt);
@ -664,6 +672,7 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
struct stktable *t = context;
struct stksess *ts;
struct eb32_node *eb;
int updt_locked = 0;
int looped = 0;
int exp_next;
@ -726,7 +735,13 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
/* session expired, trash it */
ebmb_delete(&ts->key);
eb32_delete(&ts->upd);
if (ts->upd.node.leaf_p) {
if (!updt_locked) {
updt_locked = 1;
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->updt_lock);
}
eb32_delete(&ts->upd);
}
__stksess_free(t, ts);
}
@ -735,6 +750,8 @@ struct task *process_table_expire(struct task *task, void *context, unsigned int
out_unlock:
task->expire = exp_next;
if (updt_locked)
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->updt_lock);
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->lock);
return task;
}