From 68dda70dd2559418714805d41ca78f341a70ef99 Mon Sep 17 00:00:00 2001 From: Walid Boudebouda Date: Sat, 14 Oct 2023 20:07:44 +0200 Subject: [PATCH] vcl: Introduce new obj_stale variable obj_stale variable gives access to the stale object we had in cache when doing a 304 revalidation. It is only readable from vcl_backend_refresh subroutine. --- bin/varnishd/cache/cache_vrt.c | 9 ++ bin/varnishd/cache/cache_vrt_var.c | 19 ++++ bin/varnishtest/tests/b00083.vtc | 77 ++++++++++++++ doc/sphinx/reference/vcl_var.rst | 161 +++++++++++++++++++++++++++++ include/vrt.h | 17 ++- 5 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 bin/varnishtest/tests/b00083.vtc diff --git a/bin/varnishd/cache/cache_vrt.c b/bin/varnishd/cache/cache_vrt.c index d8630d34774..f791dffea93 100644 --- a/bin/varnishd/cache/cache_vrt.c +++ b/bin/varnishd/cache/cache_vrt.c @@ -257,6 +257,8 @@ VRT_selecthttp(VRT_CTX, enum gethdr_e where) case HDR_RESP: hp = ctx->http_resp; break; + case HDR_OBJ_STALE: + /* FALLTHROUGH */ case HDR_OBJ: hp = NULL; break; @@ -281,6 +283,13 @@ VRT_GetHdr(VRT_CTX, VCL_HEADER hs) return (HTTP_GetHdrPack(ctx->req->wrk, ctx->req->objcore, hs->what)); } + + if (hs->where == HDR_OBJ_STALE) { + CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); + CHECK_OBJ_NOTNULL(ctx->bo->stale_oc, OBJCORE_MAGIC); + return (HTTP_GetHdrPack(ctx->bo->wrk, ctx->bo->stale_oc, + hs->what)); + } hp = VRT_selecthttp(ctx, hs->where); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); if (!http_GetHdr(hp, hs->what, &p)) diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c index 1b65ed5fd0e..5d48796d07a 100644 --- a/bin/varnishd/cache/cache_vrt_var.c +++ b/bin/varnishd/cache/cache_vrt_var.c @@ -546,6 +546,7 @@ VRT_r_##which##_reason(VRT_CTX) \ } VRT_OC_VAR_R(obj, req, REQ_MAGIC, objcore); +VRT_OC_VAR_R(obj_stale, bo, BUSYOBJ_MAGIC, stale_oc); /*--------------------------------------------------------------------*/ @@ -774,9 +775,13 @@ VRT_r_##which##_##fld(VRT_CTX) \ /*lint -save -e835 */ // Zero right hand arg to '-' +VRT_DO_EXP_R(obj_stale, ctx->bo->stale_oc, ttl, + ttl_now(ctx) - ctx->bo->stale_oc->t_origin) VRT_DO_EXP_R(obj, ctx->req->objcore, ttl, ttl_now(ctx) - ctx->req->objcore->t_origin) +VRT_DO_EXP_R(obj_stale, ctx->bo->stale_oc, grace, 0) VRT_DO_EXP_R(obj, ctx->req->objcore, grace, 0) +VRT_DO_EXP_R(obj_stale, ctx->bo->stale_oc, keep, 0) VRT_DO_EXP_R(obj, ctx->req->objcore, keep, 0) VRT_DO_EXP_L(beresp, ctx->bo->fetch_objcore, ttl, ttl_now(ctx) - ctx->bo->fetch_objcore->t_origin) @@ -808,6 +813,7 @@ VRT_DO_TIME_R(resp, req, t_resp) VRT_DO_TIME_R(bereq, bo, t_first) VRT_DO_TIME_R(beresp, bo, t_resp) VRT_DO_TIME_R(obj, req->objcore, t_origin) +VRT_DO_TIME_R(obj_stale, bo->stale_oc, t_origin) /*-------------------------------------------------------------------- */ @@ -822,6 +828,7 @@ VRT_r_##which##_##age(VRT_CTX) \ return (ttl_now(ctx) - oc->t_origin); \ } +VRT_DO_AGE_R(obj_stale, ctx->bo->stale_oc) VRT_DO_AGE_R(obj, ctx->req->objcore) VRT_DO_AGE_R(beresp, ctx->bo->fetch_objcore) @@ -985,6 +992,18 @@ VRT_r_obj_hits(VRT_CTX) /*--------------------------------------------------------------------*/ +VCL_INT +VRT_r_obj_stale_hits(VRT_CTX) +{ + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); + CHECK_OBJ_NOTNULL(ctx->bo->stale_oc, OBJCORE_MAGIC); + + return (ctx->bo->stale_oc->hits); +} + +/*--------------------------------------------------------------------*/ + VCL_BOOL VRT_r_resp_is_streaming(VRT_CTX) { diff --git a/bin/varnishtest/tests/b00083.vtc b/bin/varnishtest/tests/b00083.vtc new file mode 100644 index 00000000000..714a31c5d8e --- /dev/null +++ b/bin/varnishtest/tests/b00083.vtc @@ -0,0 +1,77 @@ +varnishtest "Test obj_stale vcl variables" + +server s1 { + rxreq + txresp -hdr "Etag: abcd" -hdr "from-bo: true" -bodylen 10 + rxreq + expect req.http.if-none-match == "abcd" + txresp -status 304 +} -start + +varnish v1 -vcl+backend { + + sub vcl_backend_response { + set beresp.http.vbresp = "true"; + set beresp.ttl = 0.01s; + set beresp.grace = 0s; + set beresp.keep = 10m; + set beresp.http.was-304 = beresp.was_304; + } + + sub vcl_backend_refresh { + set beresp.http.vbref = "true"; + + set beresp.http.http = obj_stale.http.from-bo; + set beresp.http.age = obj_stale.age; + set beresp.http.can_esi = obj_stale.can_esi; + set beresp.http.grace = obj_stale.grace; + set beresp.http.hits = obj_stale.hits; + set beresp.http.keep = obj_stale.keep; + set beresp.http.proto = obj_stale.proto; + set beresp.http.reason = obj_stale.reason; + set beresp.http.status = obj_stale.status; + set beresp.http.storage = obj_stale.storage; + set beresp.http.time = obj_stale.time; + set beresp.http.ttl = obj_stale.ttl; + set beresp.http.uncacheable = obj_stale.uncacheable; + + return (merge); + } +} -start + +client c1 { + txreq + rxresp + expect resp.status == 200 + + expect resp.http.was-304 == false + expect resp.http.vbref == + expect resp.http.vbresp == true + expect resp.http.from-bo == true +} -run + +delay 0.01 + +client c2 { + txreq + rxresp + expect resp.status == 200 + expect resp.http.was-304 == true + expect resp.http.vbresp == true + expect resp.http.vbref == true + expect resp.http.from-bo == true + + expect resp.http.http == true + expect resp.http.age == 0 + expect resp.http.can_esi == false + expect resp.http.grace == 0.000 + expect resp.http.hits == 0 + expect resp.http.keep == 600.000 + expect resp.http.proto == HTTP/1.1 + expect resp.http.reason == OK + expect resp.http.status == 200 + expect resp.http.storage == storage.s0 + expect resp.http.time != + expect resp.http.ttl != + expect resp.http.uncacheable == false +} -run diff --git a/doc/sphinx/reference/vcl_var.rst b/doc/sphinx/reference/vcl_var.rst index 12d5353f276..ca92282050b 100644 --- a/doc/sphinx/reference/vcl_var.rst +++ b/doc/sphinx/reference/vcl_var.rst @@ -1358,6 +1358,167 @@ beresp.was_304 to our conditional fetch from the backend and turned that into ``beresp.status = 200`` +obj_stale +--------- + +This is the stale object we had in cache. It cannot be modified. + +.. _obj_stale.age: + +obj_stale.age + + Type: DURATION + + Readable from: vcl_backend_refresh + + The age of the stale object. + + +.. _obj_stale.can_esi: + +obj_stale.can_esi + + Type: BOOL + + Readable from: vcl_backend_refresh + + If the stale object can be ESI processed, that is if setting + ``resp.do_esi`` or adding ``esi`` to ``resp.filters`` in + ``vcl_deliver {}`` would cause the response body to be ESI + processed. + + +.. _obj_stale.grace: + +obj_stale.grace + + Type: DURATION + + Readable from: vcl_backend_refresh + + The stale object's grace period in seconds. + + +.. _obj_stale.hits: + +obj_stale.hits + + Type: INT + + Readable from: vcl_backend_refresh + + The count of cache-hits on this stale object. + + In `vcl_deliver` a value of 0 indicates a cache miss. + + +.. _obj_stale.http: + +obj_stale.http.* + + Type: HEADER + + Readable from: vcl_backend_refresh + + The HTTP headers stored in the stale object. + + See req.http_ for general notes. + + +.. _obj_stale.keep: + +obj_stale.keep + + Type: DURATION + + Readable from: vcl_backend_refresh + + The stale object's keep period in seconds. + + +.. _obj_stale.proto: + +obj_stale.proto + + Type: STRING + + Readable from: vcl_backend_refresh + + The HTTP protocol version stored in the stale object. + + +.. _obj_stale.reason: + +obj_stale.reason + + Type: STRING + + Readable from: vcl_backend_refresh + + + The HTTP reason phrase stored in the stale object. + + +.. _obj_stale.status: + +obj_stale.status + + Type: INT + + Readable from: vcl_backend_refresh + + + The HTTP status code stored in the stale object. + + More information in the `HTTP response status`_ section. + + +.. _obj_stale.storage: + +obj_stale.storage + + Type: STEVEDORE + + Readable from: vcl_backend_refresh + + The storage backend where this stale object is stored. + + +.. _obj_stale.time: + +obj_stale.time + + Type: TIME + + Readable from: vcl_backend_refresh + + The time the stale object was created from the perspective of the + server which generated it. This will roughly be equivalent to + ``now`` - ``obj.age``. + + +.. _obj_stale.ttl: + +obj_stale.ttl + + Type: DURATION + + Readable from: vcl_backend_refresh + + The stale object's time to live, in seconds. + + +.. _obj_stale.uncacheable: + +obj_stale.uncacheable + + Type: BOOL + + Readable from: vcl_backend_refresh + + Whether the stale object is uncacheable (pass, hit-for-pass or + hit-for-miss). + obj --- diff --git a/include/vrt.h b/include/vrt.h index 36f7081d874..e2027b5b731 100644 --- a/include/vrt.h +++ b/include/vrt.h @@ -57,6 +57,20 @@ * Whenever something is deleted or changed in a way which is not * binary/load-time compatible, increment MAJOR version * + * NEXT (2024-03-15) + * VRT_r_obj_stale_age() added + * VRT_r_obj_stale_can_esi() added + * VRT_r_obj_stale_grace() added + * VRT_r_obj_stale_hits() added + * VRT_r_obj_stale_keep() added + * VRT_r_obj_stale_proto() added + * VRT_r_obj_stale_reason() added + * VRT_r_obj_stale_status() added + * VRT_r_obj_stale_storage() added + * VRT_r_obj_stale_time() added + * VRT_r_obj_stale_ttl() added + * VRT_r_obj_stale_uncacheable() added + * enum gethdr_e has new value HDR_OBJ_STALE * 18.0 (2023-09-15) * [cache_filter.h] struct vdp gained priv1 member * VRT_trace() added @@ -640,7 +654,8 @@ enum gethdr_e { HDR_RESP, HDR_OBJ, HDR_BEREQ, - HDR_BERESP + HDR_BERESP, + HDR_OBJ_STALE }; struct gethdr_s {