[master] 4e2c50da9 Serialize VCL temperature transitions with VRT_AddDirector()
Nils Goroll
nils.goroll at uplex.de
Wed Jul 31 12:51:05 UTC 2024
commit 4e2c50da9b4b66a39191dcc7ea11bc4e3d11ab6c
Author: Nils Goroll <nils.goroll at uplex.de>
Date: Wed Jul 31 14:20:30 2024 +0200
Serialize VCL temperature transitions with VRT_AddDirector()
VRT_AddDirector() reads the temperature under vcl_mtx and only warms up a newly
created director for a warm VCL.
vcl_set_state, when transitioning to a cold temperature, implicitly assumes that
all directors are warm, as it sends a cold event after changing the temperature.
Before this commit, we had no guarantee which temperature VRT_AddDirector()
would read while racing with vcl_set_state, it could really be any except
VCL_TEMP_INIT.
By adding this serialization on vcl_mtx, we make sure that the critical region
of VRT_AddDirector executes either before the temperature change or after. If
before, a warm event is generated for the newly added director, followed by a
cold event from vcl_set_state. If after, VRT_AddDirector() should find the vcl
temperature as COOLING and not add a backend (backround: VRT_AddDirector()
should only be called while holding a vcl reference via
VRT_VCL_Prevent_{Cold,Discard}()).
The second change, to also momentarily hold vcl_mtx for the transition to a WARM
vcl, is not to prevent a known race, but rather to ensure visibility of the new
temperature across all threads.
I _suspect_ that this change might also fix
https://github.com/nigoroll/libvmod-dynamic/issues/117 , but due to lack of a
reproducer, this is speculative. Besides the visibility issue, another potential
reason is that vcl_set_state could also race VCL_Rel() (likely called via
VRT_VCL_Allow_{Cold,Discard}()).
diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c
index 897563b09..262b48538 100644
--- a/bin/varnishd/cache/cache_vcl.c
+++ b/bin/varnishd/cache/cache_vcl.c
@@ -602,8 +602,10 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
if (vcl->temp == VCL_TEMP_COLD)
break;
if (vcl->busy == 0 && vcl->temp->is_warm) {
+ Lck_Lock(&vcl_mtx);
vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
VCL_TEMP_COLD : VCL_TEMP_COOLING;
+ Lck_Unlock(&vcl_mtx);
vcl_BackendEvent(vcl, VCL_EVENT_COLD);
AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg));
AZ(*msg);
@@ -627,7 +629,9 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
i = -1;
}
else {
+ Lck_Lock(&vcl_mtx);
vcl->temp = VCL_TEMP_WARM;
+ Lck_Unlock(&vcl_mtx);
i = vcl_send_event(vcl, VCL_EVENT_WARM, msg);
if (i == 0) {
vcl_BackendEvent(vcl, VCL_EVENT_WARM);
More information about the varnish-commit
mailing list