[master] bd443dd52 pool: Safely update pool statistics

Dridi Boukelmoune dridi.boukelmoune at gmail.com
Mon Jul 3 14:04:07 UTC 2023


commit bd443dd525654c3108ae57a1073dddf37dde8876
Author: Dridi Boukelmoune <dridi.boukelmoune at gmail.com>
Date:   Wed Jun 28 11:08:48 2023 +0200

    pool: Safely update pool statistics

diff --git a/bin/varnishd/cache/cache_pool.c b/bin/varnishd/cache/cache_pool.c
index 89d12b655..53d00a5b8 100644
--- a/bin/varnishd/cache/cache_pool.c
+++ b/bin/varnishd/cache/cache_pool.c
@@ -116,17 +116,27 @@ void v_matchproto_(task_func_t)
 pool_stat_summ(struct worker *wrk, void *priv)
 {
 	struct VSC_main_wrk *src;
+	struct pool *pp;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk->pool, POOL_MAGIC);
+	pp = wrk->pool;
+	CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
 	AN(priv);
 	src = priv;
+
 	Lck_Lock(&wstat_mtx);
 	VSC_main_Summ_wrk(VSC_C_main, src);
+
+	Lck_Lock(&pp->mtx);
+	VSC_main_Summ_pool(VSC_C_main, pp->stats);
+	Lck_Unlock(&pp->mtx);
+	memset(pp->stats, 0, sizeof pp->stats);
+
 	Lck_Unlock(&wstat_mtx);
 	memset(src, 0, sizeof *src);
-	AZ(wrk->pool->b_stat);
-	wrk->pool->b_stat = src;
+
+	AZ(pp->b_stat);
+	pp->b_stat = src;
 }
 
 /*--------------------------------------------------------------------
diff --git a/bin/varnishd/cache/cache_wrk.c b/bin/varnishd/cache/cache_wrk.c
index 427ddafbf..8803b5048 100644
--- a/bin/varnishd/cache/cache_wrk.c
+++ b/bin/varnishd/cache/cache_wrk.c
@@ -655,10 +655,6 @@ pool_herder(void *priv)
 			t_idle = VTIM_real() - cache_param->wthread_timeout;
 
 			Lck_Lock(&pp->mtx);
-			/* XXX: unsafe counters */
-			VSC_main_Summ_pool(VSC_C_main, pp->stats);
-			memset(pp->stats, 0, sizeof pp->stats);
-
 			wrk = NULL;
 			pt = VTAILQ_LAST(&pp->idle_queue, taskhead);
 			if (pt != NULL) {
diff --git a/bin/varnishtest/tests/c00124.vtc b/bin/varnishtest/tests/c00124.vtc
new file mode 100644
index 000000000..2da09f139
--- /dev/null
+++ b/bin/varnishtest/tests/c00124.vtc
@@ -0,0 +1,116 @@
+varnishtest "rushed task queued"
+
+# thread reserve mitigation barrier
+barrier b0 sock 2
+
+# client ordering barrier
+barrier b1 cond 3
+
+# waitinglist barrier
+barrier b2 cond 2
+
+# thread starvation barrier
+barrier b3 cond 3
+
+server s1 {
+	rxreq
+	barrier b1 sync
+	barrier b3 sync
+	txresp
+} -start
+
+server s2 {
+	rxreq
+	barrier b1 sync
+	barrier b2 sync
+	txresp -nolen -hdr "Transfer-Encoding: chunked"
+	chunkedlen 10
+	barrier b3 sync
+	chunkedlen 0
+} -start
+
+varnish v1 -cliok "param.set thread_pools 1"
+varnish v1 -cliok "param.set thread_pool_min 5"
+varnish v1 -cliok "param.set thread_pool_max 5"
+varnish v1 -cliok "param.set debug +syncvsl"
+varnish v1 -cliok "param.set debug +waitinglist"
+
+varnish v1 -vcl+backend {
+	import vtc;
+
+	sub vcl_recv {
+		if (req.http.server) {
+			# ensure both c1 and c2 got a thread
+			vtc.barrier_sync("${b0_sock}");
+		}
+	}
+
+	sub vcl_backend_fetch {
+		if (bereq.http.server == "s1") {
+			set bereq.backend = s1;
+		} else if (bereq.http.server == "s2") {
+			set bereq.backend = s2;
+		}
+	}
+} -start
+
+# wait for all threads to be started
+varnish v1 -expect threads == 5
+
+# 2 threads
+client c1 {
+	txreq -hdr "Cookie: foo" -hdr "server: s1"
+	rxresp
+} -start
+
+# 2 threads
+client c2 {
+	txreq -hdr "server: s2"
+	rxresp
+} -start
+
+# ensure c1 and c2 fetch tasks are started
+barrier b1 sync
+
+logexpect l1 -v v1 -g raw {
+	expect * 1007 Debug "on waiting list"
+} -start
+
+logexpect l2 -v v1 -g raw {
+	expect * 1007 Debug "off waiting list"
+} -start
+
+varnish v1 -expect sess_dropped == 0
+varnish v1 -expect sess_queued == 0
+
+# At this point, we are thread-starved and c3 below will steal the
+# acceptor thread. It will be queued before the acceptor task queues
+# itself with a lower priority.
+client c3 {
+	txreq
+	rxresp
+} -start
+
+logexpect l1 -wait
+
+varnish v1 -expect sess_dropped == 0
+varnish v1 -expect sess_queued == 1
+varnish v1 -expect busy_sleep == 1
+
+# Wake up c2, This will in turn trigger a waitinglist rush and wake up c3.
+barrier b2 sync
+
+# The acceptor thread could have restarted on the newly available thread
+# if it weren't for the thread pool reserve. For the same reason, c3's
+# client task should be queued once woken up.
+logexpect l2 -wait
+
+# let everyone loose
+barrier b3 sync
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
+
+varnish v1 -expect sess_dropped == 0
+varnish v1 -expect sess_queued == 2


More information about the varnish-commit mailing list