Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 87e1084

Browse files
committed
hash: Make the exponential rush exponential again
After the migration of the waiting list to the objcore, the result was that each request would rush more requests upon reembarking by dropping the waited objcore reference before the objhead lookup. The reference is now dropped after the objhead lookup, allowing for the lookup outcome to be checked, either triggerring a complete rush when the object is serviceable (modulus vary match) or moving the waiting list to the next busy objcore. This avoids the spurious wakeups caused by objhead rushes when waiting lists were owned by objheads. This is however a missed opportunity when there are multiple concurrent busy objcores. This could be alleviated by linking busy objcores together but at this point this is already a net improvement. The only way to get multiple concurrent busy objcores on the same objhead would be through the hash_ignore_busy flag anyway.
1 parent de8fe12 commit 87e1084

2 files changed

Lines changed: 132 additions & 8 deletions

File tree

bin/varnishd/cache/cache_hash.c

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,45 @@ hsh_rush_lookup(struct req *req)
384384
return (HSH_MISS);
385385
}
386386

387+
static void
388+
hsh_rush_move(struct objcore *new_oc, struct objcore *old_oc)
389+
{
390+
struct req *req;
391+
392+
CHECK_OBJ_NOTNULL(new_oc, OBJCORE_MAGIC);
393+
CHECK_OBJ_ORNULL(old_oc, OBJCORE_MAGIC);
394+
395+
if (old_oc == NULL || VTAILQ_EMPTY(&old_oc->waitinglist))
396+
return;
397+
398+
assert(old_oc->objhead == new_oc->objhead);
399+
assert(old_oc->refcnt > 0);
400+
assert(new_oc->refcnt > 0);
401+
Lck_AssertHeld(&old_oc->objhead->mtx);
402+
403+
/* NB: req holds a weak reference of its hash_oc, so no reference
404+
* counting is needed when moving to the new_oc. An actual old_oc
405+
* reference should be held by either the fetch task rushing its
406+
* waiting list at unbusy time, or a rushed request exponentially
407+
* rushing other requests from the waiting list.
408+
*/
409+
VTAILQ_FOREACH(req, &old_oc->waitinglist, w_list) {
410+
CHECK_OBJ(req, REQ_MAGIC);
411+
assert(req->hash_oc == old_oc);
412+
req->hash_oc = new_oc;
413+
}
414+
415+
/* NB: The double concatenation of lists allows requests that were
416+
* waiting for the old_oc show up first in the waiting list of the
417+
* new_oc.
418+
*/
419+
VTAILQ_CONCAT(&old_oc->waitinglist, &new_oc->waitinglist, w_list);
420+
VTAILQ_CONCAT(&new_oc->waitinglist, &old_oc->waitinglist, w_list);
421+
}
422+
423+
/*---------------------------------------------------------------------
424+
*/
425+
387426
static enum lookup_e
388427
hsh_objhead_lookup(struct objhead *oh, struct req *req, struct objcore **ocp,
389428
struct objcore **bocp)
@@ -547,6 +586,8 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
547586
struct objhead *oh;
548587
struct objcore *oc;
549588
struct objcore *busy_oc;
589+
struct objcore *rush_oc;
590+
struct rush rush;
550591
intmax_t boc_progress;
551592
unsigned xid = 0;
552593
float dttl = 0.0;
@@ -591,22 +632,47 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
591632
* This req came off the waiting list, and brings an
592633
* incompatible hash_oc refcnt with it.
593634
*/
594-
CHECK_OBJ_NOTNULL(req->hash_oc->objhead, OBJHEAD_MAGIC);
595-
oh = req->hash_oc->objhead;
596-
/* XXX: can we avoid grabbing the oh lock twice here? */
597-
(void)HSH_DerefObjCore(wrk, &req->hash_oc, HSH_RUSH_POLICY);
635+
TAKE_OBJ_NOTNULL(rush_oc, &req->hash_oc, OBJCORE_MAGIC);
636+
oh = rush_oc->objhead;
598637
Lck_Lock(&oh->mtx);
599638
} else {
600639
AN(wrk->wpriv->nobjhead);
601640
oh = hash->lookup(wrk, req->digest, &wrk->wpriv->nobjhead);
641+
rush_oc = NULL;
602642
}
603643

604644
lr = hsh_objhead_lookup(oh, req, &oc, &busy_oc);
645+
646+
INIT_OBJ(&rush, RUSH_MAGIC);
647+
648+
if (rush_oc != NULL && !VTAILQ_EMPTY(&rush_oc->waitinglist)) {
649+
switch (lr) {
650+
case HSH_HIT:
651+
case HSH_GRACE:
652+
CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
653+
AZ(oc->flags);
654+
hsh_rush_move(oc, rush_oc);
655+
hsh_rush1(wrk, oc, &rush, HSH_RUSH_POLICY);
656+
assert(VTAILQ_EMPTY(&oc->waitinglist));
657+
case HSH_BUSY:
658+
CHECK_OBJ_NOTNULL(busy_oc, OBJCORE_MAGIC);
659+
hsh_rush_move(busy_oc, rush_oc);
660+
break;
661+
default:
662+
/* The remaining stragglers will be passed on to the
663+
* next busy object or woken up as per rush_exponent
664+
* when the rush_oc reference is dropped.
665+
*/
666+
break;
667+
}
668+
}
669+
605670
switch (lr) {
606671
case HSH_MISS:
607672
CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
608673
AZ(busy_oc);
609674
*bocp = hsh_insert_busyobj(wrk, oh);
675+
hsh_rush_move(*bocp, rush_oc);
610676
Lck_Unlock(&oh->mtx);
611677
break;
612678
case HSH_HITMISS:
@@ -615,6 +681,7 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
615681
xid = VXID(ObjGetXID(wrk, oc));
616682
dttl = EXP_Dttl(req, oc);
617683
*bocp = hsh_insert_busyobj(wrk, oh);
684+
hsh_rush_move(*bocp, rush_oc);
618685
Lck_Unlock(&oh->mtx);
619686
wrk->stats->cache_hitmiss++;
620687
VSLb(req->vsl, SLT_HitMiss, "%u %.6f", xid, dttl);
@@ -659,8 +726,10 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
659726
CHECK_OBJ_NOTNULL(busy_oc, OBJCORE_MAGIC);
660727
AZ(req->hash_ignore_busy);
661728

662-
/* There are one or more busy objects, wait for them */
663-
VTAILQ_INSERT_TAIL(&busy_oc->waitinglist, req, w_list);
729+
if (rush_oc != NULL)
730+
VTAILQ_INSERT_HEAD(&busy_oc->waitinglist, req, w_list);
731+
else
732+
VTAILQ_INSERT_TAIL(&busy_oc->waitinglist, req, w_list);
664733

665734
/*
666735
* The objcore reference transfers to the req, we get it
@@ -689,6 +758,11 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
689758
if (oc != NULL)
690759
*ocp = oc;
691760

761+
if (rush_oc != NULL) {
762+
hsh_rush2(wrk, &rush);
763+
(void)HSH_DerefObjCore(wrk, &rush_oc, HSH_RUSH_POLICY);
764+
}
765+
692766
return (lr);
693767
}
694768

bin/varnishtest/tests/c00099.vtc

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ server s6 {
6868
chunkedlen 0
6969
} -start
7070

71-
varnish v1 -arg "-p thread_pools=1" -arg "-p thread_pool_min=30" -arg "-p rush_exponent=2" -arg "-p debug=+syncvsl" -arg "-p debug=+waitinglist" -vcl+backend {
71+
varnish v1 -cliok "param.set thread_pools 1"
72+
varnish v1 -cliok "param.set rush_exponent 2"
73+
varnish v1 -cliok "param.set debug +syncvsl,+waitinglist"
74+
varnish v1 -vcl+backend {
7275
sub vcl_backend_fetch {
7376
if (bereq.http.user-agent == "c1") {
7477
set bereq.backend = s1;
@@ -97,32 +100,63 @@ client c1 {
97100
# This makes sure that c1->s1 is done first
98101
barrier b1 sync
99102

103+
# This will ensure that c{2..6} enter c1's waiting list in order.
104+
logexpect l2 -v v1 -g raw {
105+
expect * * ReqHeader "User-Agent: c2"
106+
expect * = Debug "on waiting list"
107+
} -start
108+
logexpect l3 -v v1 -g raw {
109+
expect * * ReqHeader "User-Agent: c3"
110+
expect * = Debug "on waiting list"
111+
} -start
112+
logexpect l4 -v v1 -g raw {
113+
expect * * ReqHeader "User-Agent: c4"
114+
expect * = Debug "on waiting list"
115+
} -start
116+
logexpect l5 -v v1 -g raw {
117+
expect * * ReqHeader "User-Agent: c5"
118+
expect * = Debug "on waiting list"
119+
} -start
120+
logexpect l6 -v v1 -g raw {
121+
expect * * ReqHeader "User-Agent: c6"
122+
expect * = Debug "on waiting list"
123+
} -start
124+
100125
client c2 {
101126
txreq
102127
rxresp
103128
} -start
104129

130+
logexpect l2 -wait
131+
105132
client c3 {
106133
txreq
107134
rxresp
108135
} -start
109136

137+
logexpect l3 -wait
138+
110139
client c4 {
111140
txreq
112141
rxresp
113142
} -start
114143

144+
logexpect l4 -wait
145+
115146
client c5 {
116147
txreq
117148
rxresp
118149
} -start
119150

151+
logexpect l5 -wait
152+
120153
client c6 {
121154
txreq
122155
rxresp
123156
} -start
124157

125-
# Wait until c2-c6 are on the waitinglist
158+
logexpect l6 -wait
159+
126160
varnish v1 -vsl_catchup
127161
varnish v1 -expect busy_sleep == 5
128162

@@ -135,3 +169,19 @@ client c3 -wait
135169
client c4 -wait
136170
client c5 -wait
137171
client c6 -wait
172+
173+
varnish v1 -vsl_catchup
174+
175+
# Check the effect of rush_exponent=2, with limited VXID guarantees.
176+
177+
logexpect l1 -v v1 -g raw -d 1 -q "vxid != 0" -i Debug {
178+
expect * 1002 Debug "waiting list rush for req 1004"
179+
expect 0 = Debug "waiting list rush for req 1006"
180+
181+
# triggered by 1004 or 1006
182+
expect * * Debug "waiting list rush for req 1008"
183+
expect 0 = Debug "waiting list rush for req 1010"
184+
185+
# trigerred by any VXID except 1002
186+
expect * * Debug "waiting list rush for req 1012"
187+
} -run

0 commit comments

Comments
 (0)