Add req.grace timer: We only serve degraded mode objects if both the
request and the object's grace timers are satisfied.
Sort expiry list on obj.ttl + obj.grace and fiddle list if either changes.
In the hash lookup: record if any objects still in grace by obj.grace which
match our Vary: criteria (if any).
If no in-ttl object was found AND we have a graced object AND it is also
graced by req.grace AND it is being fetched: serve the graced object.
Otherwise, mark us as successor to the graced object while we fetch to
give others the chance.
When we unbusy the object, clean the magic pointers between the two
objects again.
To play with this you need at least:
sub vcl_recv {
set req.grace = 2m;
}
sub vcl_fetch {
set obj.grace = 2m;
}
git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@2392
d4fa192b-c00b-0410-8231-
f00ffab90ce4
};
/* -------------------------------------------------------------------*/
-
enum e_objtimer {
TIMER_TTL,
TIMER_PREFETCH
VTAILQ_HEAD(, esi_bit) esibits;
double lru_stamp;
+
+ /* Prefetch */
+ struct object *parent;
+ struct object *child;
};
struct objhead {
double t_resp;
double t_end;
+ /* Acceptable grace period */
+ double grace;
+
enum step step;
unsigned cur_method;
unsigned handling;
o->timer_when = o->prefetch;
o->timer_what = TIMER_PREFETCH;
} else {
- o->timer_when = o->ttl;
+ o->timer_when = o->ttl + o->grace;
o->timer_what = TIMER_TTL;
}
}
struct worker *w;
struct http *h;
struct objhead *oh;
- struct object *o, *busy_o;
+ struct object *o, *busy_o, *grace_o;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
}
busy_o = NULL;
+ grace_o = NULL;
VTAILQ_FOREACH(o, &oh->objects, list) {
if (o->busy) {
busy_o = o;
continue;
if (o->ttl == 0)
continue;
- if (o->ttl <= sp->t_req)
- continue;
if (BAN_CheckObject(o, h->hd[HTTP_HDR_URL].b, oh->hash)) {
o->ttl = 0;
WSP(sp, SLT_ExpBan, "%u was banned", o->xid);
EXP_TTLchange(o);
continue;
}
- if (o->vary == NULL || VRY_Match(sp, o->vary))
+ if (o->vary != NULL && !VRY_Match(sp, o->vary))
+ continue;
+
+ /* If still valid, use it */
+ if (o->ttl >= sp->t_req)
break;
+
+ /* Remember any matching objects inside their grace period */
+ if (o->ttl + o->grace >= sp->t_req)
+ grace_o = o;
}
+
+ /*
+ * If we have a object in grace and being fetched,
+ * use it, if req.grace is also satisified.
+ */
+ if (o == NULL && grace_o != NULL &&
+ grace_o->child != NULL &&
+ grace_o->ttl + sp->grace >= sp->t_req)
+ o = grace_o;
+
if (o != NULL) {
/* We found an object we like */
o->refcnt++;
o = w->nobj;
w->nobj = NULL;
o->objhead = oh;
+ /* XXX: Should this not be ..._HEAD now ? */
VTAILQ_INSERT_TAIL(&oh->objects, o, list);
/* NB: do not deref objhead the new object inherits our reference */
+ if (grace_o != NULL) {
+ grace_o->child = o;
+ o->parent = grace_o;
+ grace_o->refcnt++;
+ }
UNLOCK(&oh->mtx);
BAN_NewObj(o);
/*
HSH_Unbusy(struct object *o)
{
struct objhead *oh;
+ struct object *parent;
CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
assert(o->busy);
}
o->busy = 0;
hsh_rush(oh);
+ parent = o->parent;
+ o->parent = NULL;
+ if (parent != NULL)
+ parent->child = NULL;
if (oh != NULL)
UNLOCK(&oh->mtx);
+ if (parent != NULL)
+ HSH_Deref(parent);
}
void
if (a < 0)
a = 0;
sp->obj->grace = a;
+ if (sp->obj->timer_idx != 0)
+ EXP_TTLchange(sp->obj);
}
double
return (sp->restarts);
}
+/*--------------------------------------------------------------------
+ * req.grace
+ */
+
+void
+VRT_l_req_grace(struct sess *sp, double a)
+{
+
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ if (a < 0)
+ a = 0;
+ sp->grace = a;
+}
+
+double
+VRT_r_req_grace(struct sess *sp)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ return (sp->grace);
+}
+
/*--------------------------------------------------------------------*/
const char *
#include "cache.h"
/* Enable this to get detailed logging of WS usage */
-#ifdef DIAGNOSTICS
+#ifdef DIAGNOSTICS0
# define WS_DEBUG(fmt, ...) VSL(SLT_Debug, 0, fmt, __VA_ARGS__)
#else
# define WS_DEBUG(fmt, ...) /* nothing */
struct backend * VRT_r_req_backend(struct sess *);
void VRT_l_req_backend(struct sess *, struct backend *);
int VRT_r_req_restarts(const struct sess *);
+double VRT_r_req_grace(struct sess *);
+void VRT_l_req_grace(struct sess *, double);
const char * VRT_r_bereq_request(const struct sess *);
void VRT_l_bereq_request(const struct sess *, const char *, ...);
const char * VRT_r_bereq_url(const struct sess *);
vsb_cat(sb, "struct backend * VRT_r_req_backend(struct sess *);\n");
vsb_cat(sb, "void VRT_l_req_backend(struct sess *, struct backend *);\n");
vsb_cat(sb, "int VRT_r_req_restarts(const struct sess *);\n");
+ vsb_cat(sb, "double VRT_r_req_grace(const struct sess *);\n");
+ vsb_cat(sb, "void VRT_l_req_grace(const struct sess *, double);\n");
vsb_cat(sb, "const char * VRT_r_bereq_request(const struct sess *);\n");
vsb_cat(sb, "void VRT_l_bereq_request(const struct sess *, const char *, ...);\n");
vsb_cat(sb, "const char * VRT_r_bereq_url(const struct sess *);\n");
{recv pipe pass hash miss hit fetch deliver }
"const struct sess *"
}
+ { req.grace
+ RW TIME
+ {recv pipe pass hash miss hit fetch deliver }
+ "struct sess *"
+ }
# Request sent to backend
{ bereq.request
0,
VCL_MET_RECV | VCL_MET_PIPE | VCL_MET_PASS | VCL_MET_HASH | VCL_MET_MISS | VCL_MET_HIT | VCL_MET_FETCH | VCL_MET_DELIVER
},
+ { "req.grace", TIME, 9,
+ "VRT_r_req_grace(sp)",
+ "VRT_l_req_grace(sp, ",
+ V_RW,
+ 0,
+ VCL_MET_RECV | VCL_MET_PIPE | VCL_MET_PASS | VCL_MET_HASH | VCL_MET_MISS | VCL_MET_HIT | VCL_MET_FETCH | VCL_MET_DELIVER
+ },
{ "bereq.request", STRING, 13,
"VRT_r_bereq_request(sp)",
"VRT_l_bereq_request(sp, ",