]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: peers: Give up if we fail to take locks in hot path
authorOlivier Houchard <ohouchard@haproxy.com>
Fri, 2 May 2025 11:29:05 +0000 (11:29 +0000)
committerWilly Tarreau <w@1wt.eu>
Fri, 2 May 2025 13:27:55 +0000 (15:27 +0200)
In peer_send_msgs(), give up in order to retry later if we failed at
getting the update read lock.
Similarly, in __process_running_peer_sync(), give up and just reschedule
the task if we failed to get the peer lock. There is an heavy contention
on both those locks, so we could spend a lot of time trying to get them.
This helps getting peers perform better under heavy load.

src/peers.c

index abc00026e30a0a8d1dca26d91d94934b3e0a53b4..f13d9d393dcc2aaac1d4fa5548b5ba693f56764a 100644 (file)
@@ -2639,7 +2639,10 @@ static inline int peer_send_msgs(struct appctx *appctx,
                        if (!(peer->flags & PEER_F_TEACH_PROCESS)) {
                                int must_send;
 
-                               HA_RWLOCK_RDLOCK(STK_TABLE_UPDT_LOCK, &st->table->updt_lock);
+                               if (HA_RWLOCK_TRYRDLOCK(STK_TABLE_UPDT_LOCK, &st->table->updt_lock)) {
+                                       applet_have_more_data(appctx);
+                                       return -1;
+                               }
                                must_send = (peer->learnstate == PEER_LR_ST_NOTASSIGNED) && (st->last_pushed != st->table->localupdate);
                                HA_RWLOCK_RDUNLOCK(STK_TABLE_UPDT_LOCK, &st->table->updt_lock);
 
@@ -3413,6 +3416,7 @@ static void __process_running_peer_sync(struct task *task, struct peers *peers,
 {
        struct peer *peer;
        struct shared_table *st;
+       int must_resched = 0;
 
        /* resync timeout set to TICK_ETERNITY means we just start
         * a new process and timer was not initialized.
@@ -3440,7 +3444,10 @@ static void __process_running_peer_sync(struct task *task, struct peers *peers,
 
        /* For each session */
        for (peer = peers->remote; peer; peer = peer->next) {
-               HA_SPIN_LOCK(PEER_LOCK, &peer->lock);
+               if (HA_SPIN_TRYLOCK(PEER_LOCK, &peer->lock) != 0) {
+                       must_resched = 1;
+                       continue;
+               }
 
                sync_peer_learn_state(peers, peer);
                sync_peer_app_state(peers, peer);
@@ -3567,12 +3574,13 @@ static void __process_running_peer_sync(struct task *task, struct peers *peers,
                HA_ATOMIC_OR(&peers->flags, PEERS_F_RESYNC_REMOTE_FINISHED|PEERS_F_DBG_RESYNC_REMOTETIMEOUT);
        }
 
-       if ((peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
+       if (!must_resched && (peers->flags & PEERS_RESYNC_STATEMASK) != PEERS_RESYNC_FINISHED) {
                /* Resync not finished*/
                /* reschedule task to resync timeout if not expired, to ended resync if needed */
                if (!tick_is_expired(peers->resync_timeout, now_ms))
                        task->expire = tick_first(task->expire, peers->resync_timeout);
-       }
+       } else if (must_resched)
+               task_wakeup(task, TASK_WOKEN_OTHER);
 }
 
 /* Process the sync task for a stopping process. It is called from process_peer_sync() only */