BUG/MAJOR: queue/threads: avoid an AB/BA locking issue in process_srv_queue()
A problem involving server slowstart was reported by @max2k1 in issue #197.
The problem is that pendconn_grab_from_px() takes the proxy lock while
already under the server's lock while process_srv_queue() first takes the
proxy's lock then the server's lock.
While the latter seems more natural, it is fundamentally incompatible with
mayn other operations performed on servers, namely state change propagation,
where the proxy is only known after the server and cannot be locked around
the servers. Howwever reversing the lock in process_srv_queue() is trivial
and only the few functions related to dynamic cookies need to be adjusted
for this so that the proxy's lock is taken for each server operation. This
is possible because the proxy's server list is built once at boot time and
remains stable. So this is what this patch does.
The comments in the proxy and server structs were updated to mention this
rule that the server's lock may not be taken under the proxy's lock but
may enclose it.
Another approach could consist in using a second lock for the proxy's queue
which would be different from the regular proxy's lock, but given that the
operations above are rare and operate on small servers list, there is no
reason for overdesigning a solution.
This fix was successfully tested with 10000 servers in a backend where
adjusting the dyncookies in loops over the CLI didn't have a measurable
impact on the traffic.
The only workaround without the fix is to disable any occurrence of
"slowstart" on server lines, or to disable threads using "nbthread 1".
This must be backported as far as 1.8.
(cherry picked from commit 5e83d996cf965ee5ac625f702a446f4d8c80a220)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/proxy.c b/src/proxy.c
index ae761ea..a537e0b 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1940,9 +1940,12 @@
if (!px)
return 1;
+ /* Note: this lock is to make sure this doesn't change while another
+ * thread is in srv_set_dyncookie().
+ */
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
-
px->ck_opts |= PR_CK_DYNAMIC;
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
for (s = px->srv; s != NULL; s = s->next) {
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
@@ -1950,8 +1953,6 @@
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
}
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
-
return 1;
}
@@ -1971,9 +1972,12 @@
if (!px)
return 1;
+ /* Note: this lock is to make sure this doesn't change while another
+ * thread is in srv_set_dyncookie().
+ */
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
-
px->ck_opts &= ~PR_CK_DYNAMIC;
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
for (s = px->srv; s != NULL; s = s->next) {
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
@@ -1984,8 +1988,6 @@
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
}
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
-
return 1;
}
@@ -2021,10 +2023,13 @@
return 1;
}
+ /* Note: this lock is to make sure this doesn't change while another
+ * thread is in srv_set_dyncookie().
+ */
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
-
free(px->dyncookie_key);
px->dyncookie_key = newkey;
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
for (s = px->srv; s != NULL; s = s->next) {
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
@@ -2032,8 +2037,6 @@
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
}
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
-
return 1;
}