Skip to content

Commit 0e115d9

Browse files
committed
vdire: plug the remaining vcl temperature change races
The context for this change is #4142, but while it builds upon it, it is still causally unrelated. To motivate this change, let's look at vcl_set_state() from before, and consider some non-issues and issues: transition to COLD ------------------ Lck_Lock(&vcl_mtx); vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ? VCL_TEMP_COLD : VCL_TEMP_COOLING; Lck_Unlock(&vcl_mtx); // *A* vcl_BackendEvent(vcl, VCL_EVENT_COLD); non-issues: At point *A*, backends could get added. For VCL_TEMP_COOLING, this fails (see VRT_AddDirector()). For VCL_TEMP_COLD, the state is consistent, because backends get created cold and that's it. At point *A*, backends could get removed. vdire_resign() from #4142 ensures that the temperature passed to vcldir_retire() is consistent with the events the backends have received. transition to WARM (success) ---------------------------- Lck_Lock(&vcl_mtx); vcl->temp = VCL_TEMP_WARM; Lck_Unlock(&vcl_mtx); // *B* ... i = vcl_send_event(vcl, VCL_EVENT_WARM, msg); if (i == 0) { // ... *B* vcl_BackendEvent(vcl, VCL_EVENT_WARM); break; } issues: (1) In region *B*, backends could get removed. They will not have received a WARM event, but will be called with a WARM temperature, so they will receive a bogus COLD event. (2) Also in region *B*, backends could get added. They will implicitly receive a WARM event (see VDI_event() in VRT_AddDirector()), and then another from vcl_BackendEvent() transition to WARM (failed) --------------------------- AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg)); AZ(nomsg); vcl->temp = VCL_TEMP_COLD; issues: (3) Backends added in region *B* will have received the implicit WARM event, and thus need a COLD event for the "cancel". To solve these issues, we need to do two things: There needs to be some kind of transaction which begins with the temperature change and ends when all backends have been notified appropriately. Backends can not get deleted while the transaction is in progress. We need a notion of "backends from before the temperature change" and "backends from after". The first part is already delivered by #4142: The vdire facility already provides the transaction during which backends do not actually get deleted and it ensures that the right temperature gets passed when the deletion is carried out. So for this part, we only need to use vdire. But issues (2) and (3) are not yet covered. For these, we add a checkpoint, such that we know which directors from the list are the "base" from before the temperature change and which are the "delta" added after it. That's this patch. vdire_start_event() atomically (under the vcl_mtx) sets the checkpoint and the new temperature. vdire_end_event() just uses the existing vdire_end_iter() and clears the checkpoint. vcl_BackendEvent() gets split into two: backend_event_base() notifies backends from before the checkpoint. backend_event_delta() atomically sets a new temperature and notifies backends from after the checkpoint (but not from after its temperature change). Fixes #4199
1 parent a3e3c42 commit 0e115d9

File tree

2 files changed

+95
-10
lines changed

2 files changed

+95
-10
lines changed

bin/varnishd/cache/cache_vcl.c

+93-10
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,36 @@ vdire_end_iter(struct vdire *vdire)
436436
vcldir_retire(vdir, temp);
437437
}
438438

439+
/*
440+
* like vdire_start_iter, but also prepare for backend_event_*()
441+
* by setting the checkpoint
442+
*/
443+
static void
444+
vdire_start_event(struct vdire *vdire, const struct vcltemp *temp)
445+
{
446+
447+
CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
448+
AN(temp);
449+
450+
Lck_AssertHeld(vdire->mtx);
451+
452+
// https://github.com/varnishcache/varnish-cache/pull/4142#issuecomment-2593091097
453+
ASSERT_CLI();
454+
455+
AZ(vdire->checkpoint);
456+
vdire->checkpoint = VTAILQ_LAST(&vdire->directors, vcldir_head);
457+
AN(vdire->tempp);
458+
*vdire->tempp = temp;
459+
vdire->iterating++;
460+
}
461+
462+
static void
463+
vdire_end_event(struct vdire *vdire)
464+
{
465+
vdire->checkpoint = NULL;
466+
vdire_end_iter(vdire);
467+
}
468+
439469
// if there are no iterators, remove from directors and retire
440470
// otherwise put on resigning list to work when iterators end
441471
void
@@ -557,8 +587,13 @@ VCL_IterDirector(struct cli *cli, const char *pat,
557587
return (found);
558588
}
559589

590+
/*
591+
* vdire_start_event() must have been called before
592+
*
593+
* send event to directors pre checkpoint
594+
*/
560595
static void
561-
vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
596+
backend_event_base(const struct vcl *vcl, enum vcl_event_e e)
562597
{
563598
struct vcldir *vdir;
564599
struct vdire *vdire;
@@ -567,11 +602,54 @@ vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
567602
CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
568603
AZ(vcl->busy);
569604
vdire = vcl->vdire;
605+
AN(vdire->iterating);
570606

571-
vdire_start_iter(vdire);
572-
VTAILQ_FOREACH(vdir, &vdire->directors, directors_list)
607+
if (vdire->checkpoint == NULL)
608+
return;
609+
610+
VTAILQ_FOREACH(vdir, &vdire->directors, directors_list) {
573611
VDI_Event(vdir->dir, e);
574-
vdire_end_iter(vdire);
612+
if (vdir == vdire->checkpoint)
613+
break;
614+
}
615+
}
616+
617+
/*
618+
* vdire_start_event() must have been called before
619+
*
620+
* set a new temperature.
621+
* send event to directors added post checkpoint, but before
622+
* the new temperature
623+
*/
624+
static void
625+
backend_event_delta(const struct vcl *vcl, enum vcl_event_e e, const struct vcltemp *temp)
626+
{
627+
struct vcldir *vdir, *end;
628+
struct vdire *vdire;
629+
630+
ASSERT_CLI();
631+
CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
632+
AZ(vcl->busy);
633+
vdire = vcl->vdire;
634+
AN(temp);
635+
AN(vdire->iterating);
636+
637+
Lck_Lock(vdire->mtx);
638+
if (vdire->checkpoint == NULL)
639+
vdir = VTAILQ_FIRST(&vdire->directors);
640+
else
641+
vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list);
642+
AN(vdire->tempp);
643+
*vdire->tempp = temp;
644+
end = VTAILQ_LAST(&vdire->directors, vcldir_head);
645+
Lck_Unlock(vdire->mtx);
646+
647+
while (vdir != NULL) {
648+
VDI_Event(vdir->dir, e);
649+
if (vdir == end)
650+
break;
651+
vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list);
652+
}
575653
}
576654

577655
static void
@@ -742,10 +820,12 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
742820
break;
743821
if (vcl->busy == 0 && vcl->temp->is_warm) {
744822
Lck_Lock(&vcl_mtx);
745-
vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
746-
VCL_TEMP_COLD : VCL_TEMP_COOLING;
823+
vdire_start_event(vcl->vdire, VTAILQ_EMPTY(&vcl->ref_list) ?
824+
VCL_TEMP_COLD : VCL_TEMP_COOLING);
747825
Lck_Unlock(&vcl_mtx);
748-
vcl_BackendEvent(vcl, VCL_EVENT_COLD);
826+
backend_event_base(vcl, VCL_EVENT_COLD);
827+
vdire_end_event(vcl->vdire);
828+
// delta directors at VCL_TEMP_COLD do not need an event
749829
AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg));
750830
AZ(*msg);
751831
}
@@ -769,16 +849,19 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
769849
}
770850
else {
771851
Lck_Lock(&vcl_mtx);
772-
vcl->temp = VCL_TEMP_WARM;
852+
vdire_start_event(vcl->vdire, VCL_TEMP_WARM);
773853
Lck_Unlock(&vcl_mtx);
774854
i = vcl_send_event(vcl, VCL_EVENT_WARM, msg);
775855
if (i == 0) {
776-
vcl_BackendEvent(vcl, VCL_EVENT_WARM);
856+
backend_event_base(vcl, VCL_EVENT_WARM);
857+
// delta directors are already warm
858+
vdire_end_event(vcl->vdire);
777859
break;
778860
}
779861
AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg));
780862
AZ(nomsg);
781-
vcl->temp = VCL_TEMP_COLD;
863+
backend_event_delta(vcl, VCL_EVENT_COLD, VCL_TEMP_COLD);
864+
vdire_end_event(vcl->vdire);
782865
}
783866
break;
784867
default:

bin/varnishd/cache/cache_vcl.h

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ struct vdire {
5050
// to signal when iterators can enter again
5151
pthread_cond_t cond;
5252
const struct vcltemp **tempp;
53+
// last director present when vdire_start_event is called
54+
struct vcldir *checkpoint;
5355
};
5456

5557
struct vcl {

0 commit comments

Comments
 (0)