Commit 833f5680 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Add an experimental thread to lurk at the bottom of the ban-list

and try to push objects away from the tail end so that the list
can be shortened.

It won't work if your bans contains any req.* tests, because the
lurker has no request to compare against.

By default the lurker is disabled, you enable it by setting the
'ban_lurker_sleep' parameter to the amount of time the lurker
should sleep between doing things.

If it cannot do anything, it always sleeps for a second.

NB: This feature is on probation, there is currently no guarantee
it will end up in a future varnish release, it depends on the
impact and usability.  Feedback much anticipated.


One of the quasi-nasty details of this feature, is the need to work
against the lock-order in cache_hash.c, see details in comments for
HSH_FindBan().




git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@4206 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 299b3b8f
...@@ -63,6 +63,7 @@ SVNID("$Id$") ...@@ -63,6 +63,7 @@ SVNID("$Id$")
static VTAILQ_HEAD(banhead,ban) ban_head = VTAILQ_HEAD_INITIALIZER(ban_head); static VTAILQ_HEAD(banhead,ban) ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
static struct lock ban_mtx; static struct lock ban_mtx;
static struct ban *ban_magic; static struct ban *ban_magic;
static pthread_t ban_thread;
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* Manipulation of bans * Manipulation of bans
...@@ -285,6 +286,8 @@ BAN_AddTest(struct cli *cli, struct ban *b, const char *a1, const char *a2, cons ...@@ -285,6 +286,8 @@ BAN_AddTest(struct cli *cli, struct ban *b, const char *a1, const char *a2, cons
if (strncmp(a1, pv->name, strlen(pv->name))) if (strncmp(a1, pv->name, strlen(pv->name)))
continue; continue;
bt->func = pv->func; bt->func = pv->func;
if (pv->flag & 2)
b->flags |= BAN_F_REQ;
if (pv->flag & 1) if (pv->flag & 1)
ban_parse_http(bt, a1 + strlen(pv->name)); ban_parse_http(bt, a1 + strlen(pv->name));
break; break;
...@@ -419,8 +422,8 @@ BAN_DestroyObj(struct object *o) ...@@ -419,8 +422,8 @@ BAN_DestroyObj(struct object *o)
} }
int static int
BAN_CheckObject(struct object *o, const struct sess *sp) ban_check_object(struct object *o, const struct sess *sp, int has_req)
{ {
struct ban *b; struct ban *b;
struct ban_test *bt; struct ban_test *bt;
...@@ -445,6 +448,8 @@ BAN_CheckObject(struct object *o, const struct sess *sp) ...@@ -445,6 +448,8 @@ BAN_CheckObject(struct object *o, const struct sess *sp)
for (b = b0; b != o->ban; b = VTAILQ_NEXT(b, list)) { for (b = b0; b != o->ban; b = VTAILQ_NEXT(b, list)) {
if (b->flags & BAN_F_GONE) if (b->flags & BAN_F_GONE)
continue; continue;
if (!has_req && (b->flags & BAN_F_REQ))
return (0);
VTAILQ_FOREACH(bt, &b->tests, list) { VTAILQ_FOREACH(bt, &b->tests, list) {
tests++; tests++;
if (bt->func(bt, o, sp)) if (bt->func(bt, o, sp))
...@@ -484,6 +489,68 @@ BAN_CheckObject(struct object *o, const struct sess *sp) ...@@ -484,6 +489,68 @@ BAN_CheckObject(struct object *o, const struct sess *sp)
} }
} }
int
BAN_CheckObject(struct object *o, const struct sess *sp)
{
return ban_check_object(o, sp, 1);
}
/*--------------------------------------------------------------------
* Ban tail lurker thread
*/
static void *
ban_lurker(struct sess *sp, void *priv)
{
struct ban *b, *bf;
struct objcore *oc;
struct object *o;
(void)priv;
while (1) {
WSL_Flush(sp->wrk, 0);
WRK_SumStat(sp->wrk);
if (params->ban_lurker_sleep == 0.0) {
AZ(sleep(1));
continue;
}
Lck_Lock(&ban_mtx);
/* First try to route the last ban */
bf = BAN_CheckLast();
if (bf != NULL) {
Lck_Unlock(&ban_mtx);
BAN_Free(bf);
TIM_sleep(params->ban_lurker_sleep);
continue;
}
/* Then try to poke the first object on the last ban */
oc = NULL;
while (1) {
b = VTAILQ_LAST(&ban_head, banhead);
if (b == ban_start)
break;
oc = VTAILQ_FIRST(&b->objcore);
if (oc == NULL)
break;
HSH_FindBan(sp, &oc);
break;
}
Lck_Unlock(&ban_mtx);
if (oc == NULL) {
TIM_sleep(1.0);
continue;
}
o = oc->obj;
(void)ban_check_object(o, sp, 0);
WSP(sp, SLT_Debug, "lurker: %p %g", oc, o->ttl);
HSH_Deref(sp->wrk, &o);
TIM_sleep(params->ban_lurker_sleep);
}
}
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* Release a reference * Release a reference
*/ */
...@@ -767,4 +834,5 @@ BAN_Init(void) ...@@ -767,4 +834,5 @@ BAN_Init(void)
AN(ban_magic); AN(ban_magic);
ban_magic->flags |= BAN_F_GONE; ban_magic->flags |= BAN_F_GONE;
BAN_Insert(ban_magic); BAN_Insert(ban_magic);
WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
} }
...@@ -57,6 +57,7 @@ struct ban { ...@@ -57,6 +57,7 @@ struct ban {
int flags; int flags;
#define BAN_F_GONE (1 << 0) #define BAN_F_GONE (1 << 0)
#define BAN_F_PENDING (1 << 1) #define BAN_F_PENDING (1 << 1)
#define BAN_F_REQ (1 << 2)
VTAILQ_HEAD(,ban_test) tests; VTAILQ_HEAD(,ban_test) tests;
VTAILQ_HEAD(,objcore) objcore; VTAILQ_HEAD(,objcore) objcore;
double t0; double t0;
......
...@@ -652,6 +652,39 @@ HSH_DerefObjCore(struct sess *sp) ...@@ -652,6 +652,39 @@ HSH_DerefObjCore(struct sess *sp)
HSH_DeleteObjHead(sp->wrk, oh); HSH_DeleteObjHead(sp->wrk, oh);
} }
/*******************************************************************
* This one is slightly tricky. This is called from the BAN module
* to try to wash the object which holds the oldest ban.
* We compete against HSH_Deref() which comes in the opposite
* locking order, we need to hold the BAN mutex, to stop the
* BAN_DestroyObj() call in HSH_Deref(), so that the objhead
* will not be removed under us.
* NB: Do not call this function any other way or from any other
* NB: place in the code. It will not work for you.
*/
void
HSH_FindBan(struct sess *sp, struct objcore **oc)
{
struct objcore *oc1, *oc2;
struct objhead *oh;
oc1 = *oc;
CHECK_OBJ_NOTNULL(oc1, OBJCORE_MAGIC);
oh = oc1->objhead;
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
Lck_Lock(&oh->mtx);
VTAILQ_FOREACH(oc2, &oh->objcs, list)
if (oc1 == oc2) {
oc1->obj->refcnt++;
break;
}
if (oc2 != NULL && oc2->flags & OC_F_PERSISTENT)
SMP_Fixup(sp, oh, oc2);
Lck_Unlock(&oc1->objhead->mtx);
*oc = oc2;
}
void void
HSH_Deref(const struct worker *w, struct object **oo) HSH_Deref(const struct worker *w, struct object **oo)
{ {
......
...@@ -66,6 +66,7 @@ void HSH_AddString(struct sess *sp, const char *str); ...@@ -66,6 +66,7 @@ void HSH_AddString(struct sess *sp, const char *str);
void HSH_BeforeVclHash(struct sess *sp, unsigned hashcount); void HSH_BeforeVclHash(struct sess *sp, unsigned hashcount);
void HSH_AfterVclHash(const struct sess *sp); void HSH_AfterVclHash(const struct sess *sp);
void HSH_DerefObjCore(struct sess *sp); void HSH_DerefObjCore(struct sess *sp);
void HSH_FindBan(struct sess *sp, struct objcore **oc);
struct objcore *HSH_Insert(const struct sess *sp); struct objcore *HSH_Insert(const struct sess *sp);
#ifdef VARNISH_CACHE_CHILD #ifdef VARNISH_CACHE_CHILD
......
...@@ -195,6 +195,9 @@ struct params { ...@@ -195,6 +195,9 @@ struct params {
/* CLI banner */ /* CLI banner */
unsigned cli_banner; unsigned cli_banner;
/* How long time does the ban lurker sleep */
double ban_lurker_sleep;
}; };
extern volatile struct params *params; extern volatile struct params *params;
......
...@@ -758,6 +758,14 @@ static const struct parspec input_parspec[] = { ...@@ -758,6 +758,14 @@ static const struct parspec input_parspec[] = {
"Set to off for compatibility with pre 2.1 versions.\n", "Set to off for compatibility with pre 2.1 versions.\n",
0, 0,
"on", "bool" }, "on", "bool" },
{ "ban_lurker_sleep", tweak_timeout_double,
&master.ban_lurker_sleep, 0.0, UINT_MAX,
"How long time does the ban lurker thread sleeps between "
"successfull attempts to push the last item up the purge "
" list. It always sleeps a second when nothing can be done.\n"
"A value of zero disables the ban lurker.",
0,
"0.0", "s" },
{ NULL, NULL, NULL } { NULL, NULL, NULL }
}; };
......
...@@ -610,6 +610,7 @@ SMP_Fixup(struct sess *sp, struct objhead *oh, struct objcore *oc) ...@@ -610,6 +610,7 @@ SMP_Fixup(struct sess *sp, struct objhead *oh, struct objcore *oc)
struct smp_seg *sg; struct smp_seg *sg;
struct smp_object *so; struct smp_object *so;
Lck_AssertHeld(&oh->mtx);
(void)sp; (void)sp;
sg = oc->smp_seg; sg = oc->smp_seg;
CHECK_OBJ_NOTNULL(sg, SMP_SEG_MAGIC); CHECK_OBJ_NOTNULL(sg, SMP_SEG_MAGIC);
......
...@@ -53,6 +53,9 @@ varnish v1 -cli "debug.backend" -cli "vcl.list" ...@@ -53,6 +53,9 @@ varnish v1 -cli "debug.backend" -cli "vcl.list"
varnish v1 -cli "vcl.discard vcl1" varnish v1 -cli "vcl.discard vcl1"
# Give expiry thread a chance to let go
delay 2
# It won't go away as long as the workthread holds a VCL reference # It won't go away as long as the workthread holds a VCL reference
varnish v1 -expect n_backend == 2 varnish v1 -expect n_backend == 2
varnish v1 -expect n_vcl_avail == 1 varnish v1 -expect n_vcl_avail == 1
......
...@@ -28,9 +28,13 @@ ...@@ -28,9 +28,13 @@
* $Id$ * $Id$
* *
* Define which variables we can purge on, and which function does it. * Define which variables we can purge on, and which function does it.
*
* Middle field is a bitmap:
* 1 = HTTP header search
* 2 = Needs a client supplied request for test evaluation
*/ */
PVAR("req.url", 0, ban_cond_url) PVAR("req.url", 2|0, ban_cond_url)
PVAR("obj.hash", 0, ban_cond_hash) PVAR("obj.hash", 0|0, ban_cond_hash)
PVAR("req.http.", 1, ban_cond_req_http) PVAR("req.http.", 2|1, ban_cond_req_http)
PVAR("obj.http.", 1, ban_cond_obj_http) PVAR("obj.http.", 0|1, ban_cond_obj_http)
...@@ -383,7 +383,7 @@ parse_purge(struct tokenlist *tl) ...@@ -383,7 +383,7 @@ parse_purge(struct tokenlist *tl)
vcc_ErrWhere(tl, tl->t); vcc_ErrWhere(tl, tl->t);
return; return;
} }
if (pv->flag && if ((pv->flag & 1) &&
tl->t->b + strlen(pv->name) >= tl->t->e) { tl->t->b + strlen(pv->name) >= tl->t->e) {
vsb_printf(tl->sb, "Missing header name."); vsb_printf(tl->sb, "Missing header name.");
vcc_ErrWhere(tl, tl->t); vcc_ErrWhere(tl, tl->t);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment