[master] 141ea56a4 Add the VCF - aka "The CatFlap"

Poul-Henning Kamp phk at FreeBSD.org
Mon Mar 4 09:40:08 UTC 2019


commit 141ea56a43459d2c1b29d2a299ff9b67356228c6
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Mon Mar 4 09:37:24 2019 +0000

    Add the VCF - aka "The CatFlap"
    
    I have reimplemented this based on Nils's #2858, because I found
    it too complex and intrusive.  (In particular we try to avoid
    unions in Varnish).
    
    Testcase m00051 by:     Nils Goroll
    
    Closes: #2858

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 2018c9bce..163c52948 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -101,6 +101,7 @@ struct sess;
 struct transport;
 struct worker;
 struct listen_sock;
+struct vcf;
 
 #define DIGEST_LEN		32
 
@@ -530,6 +531,8 @@ struct req {
 	struct acct_req		acct;
 
 	struct vrt_privs	privs[1];
+
+	struct vcf		*vcf;
 };
 
 #define IS_TOPREQ(req) ((req)->topreq == (req))
diff --git a/bin/varnishd/cache/cache_hash.c b/bin/varnishd/cache/cache_hash.c
index e9b8466d9..a778bf535 100644
--- a/bin/varnishd/cache/cache_hash.c
+++ b/bin/varnishd/cache/cache_hash.c
@@ -81,6 +81,15 @@ static int hsh_deref_objhead_unlock(struct worker *wrk, struct objhead **poh);
 
 /*---------------------------------------------------------------------*/
 
+#define VCF_RETURN(x) const struct vcf_return VCF_##x[1] = { \
+	{ .name = #x, } \
+};
+
+VCF_RETURNS()
+#undef VCF_RETURN
+
+/*---------------------------------------------------------------------*/
+
 static struct objhead *
 hsh_newobjhead(void)
 {
@@ -344,6 +353,7 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
 	struct objhead *oh;
 	struct objcore *oc;
 	struct objcore *exp_oc;
+	const struct vcf_return *vr;
 	vtim_real exp_t_origin;
 	int busy_found;
 	const uint8_t *vary;
@@ -359,6 +369,7 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
 	wrk = req->wrk;
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CHECK_OBJ_NOTNULL(req->http, HTTP_MAGIC);
+	CHECK_OBJ_ORNULL(req->vcf, VCF_MAGIC);
 	AN(hash);
 
 	hsh_prealloc(wrk);
@@ -436,6 +447,19 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
 				continue;
 		}
 
+		if (req->vcf != NULL) {
+			vr = req->vcf->func(req, &oc, &exp_oc, 0);
+			if (vr == VCF_CONTINUE)
+				continue;
+			if (vr == VCF_MISS) {
+				oc = NULL;
+				break;
+			}
+			if (vr == VCF_HIT)
+				break;
+			assert(vr == VCF_DEFAULT);
+		}
+
 		if (EXP_Ttl(req, oc) > req->t_req) {
 			assert(oh->refcnt > 1);
 			assert(oc->objhead == oh);
@@ -452,6 +476,9 @@ HSH_Lookup(struct req *req, struct objcore **ocp, struct objcore **bocp)
 		}
 	}
 
+	if (req->vcf != NULL)
+		(void)req->vcf->func(req, &oc, &exp_oc, 1);
+
 	if (oc != NULL && oc->flags & OC_F_HFP) {
 		xid = ObjGetXID(wrk, oc);
 		dttl = EXP_Dttl(req, oc);
diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c
index 1768efee1..1bd7911ee 100644
--- a/bin/varnishd/cache/cache_req_fsm.c
+++ b/bin/varnishd/cache/cache_req_fsm.c
@@ -490,6 +490,11 @@ cnt_lookup(struct worker *wrk, struct req *req)
 	if (had_objhead)
 		VSLb_ts_req(req, "Waitinglist", W_TIM_real(wrk));
 
+	if (req->vcf != NULL) {
+		(void)req->vcf->func(req, NULL, NULL, 2);
+		req->vcf = NULL;
+	}
+
 	if (busy == NULL) {
 		VRY_Finish(req, DISCARD);
 	} else {
diff --git a/bin/varnishd/cache/cache_varnishd.h b/bin/varnishd/cache/cache_varnishd.h
index 2aab94ab7..a117d8b8d 100644
--- a/bin/varnishd/cache/cache_varnishd.h
+++ b/bin/varnishd/cache/cache_varnishd.h
@@ -85,6 +85,37 @@ typedef enum htc_status_e htc_complete_f(struct http_conn *);
 
 extern volatile struct params * cache_param;
 
+/* -------------------------------------------------------------------
+ * The VCF facility is deliberately undocumented, use at your peril.
+ */
+
+struct vcf_return {
+	const char		*name;
+};
+
+#define VCF_RETURNS()	\
+		VCF_RETURN(CONTINUE) \
+		VCF_RETURN(DEFAULT) \
+		VCF_RETURN(MISS) \
+		VCF_RETURN(HIT)
+
+#define VCF_RETURN(x) extern const struct vcf_return VCF_##x[1];
+VCF_RETURNS()
+#undef VCF_RETURN
+
+typedef const struct vcf_return *vcf_func_f(
+	struct req *req,
+	struct objcore **oc,
+	struct objcore **oc_exp,
+	int state);
+
+struct vcf {
+	unsigned		magic;
+#define VCF_MAGIC		0x183285d1
+	vcf_func_f		*func;
+	void			*priv;
+};
+
 /* Prototypes etc ----------------------------------------------------*/
 
 /* cache_acceptor.c */
diff --git a/bin/varnishd/flint.lnt b/bin/varnishd/flint.lnt
index 42a40290f..5a1942528 100644
--- a/bin/varnishd/flint.lnt
+++ b/bin/varnishd/flint.lnt
@@ -39,6 +39,7 @@
 -esym(765, vrt_magic_string_end)
 -esym(759, vrt_magic_string_end)
 -esym(768, vrt_ref::*)
+-esym(768, vcf_return::name)
 -esym(768, VCL_conf::*)
 
 // FLINT Bug20090910_838
diff --git a/bin/varnishtest/tests/m00051.vtc b/bin/varnishtest/tests/m00051.vtc
new file mode 100644
index 000000000..a5231894a
--- /dev/null
+++ b/bin/varnishtest/tests/m00051.vtc
@@ -0,0 +1,54 @@
+varnishtest "catflap"
+
+varnish v1 -vcl {
+	import debug;
+
+	backend dummy { .host = "${bad_backend}"; }
+
+	sub vcl_recv {
+		if (req.http.id) {
+			debug.catflap(miss);
+		} else if (req.http.get == "first") {
+			debug.catflap(first);
+		} else if (req.http.get == "last") {
+			debug.catflap(last);
+		} else {
+			return (fail);
+		}
+		return (hash);
+	}
+
+	sub vcl_backend_error {
+		if (! bereq.http.id) {
+			return (deliver);
+		}
+		set beresp.status = 200;
+		set beresp.ttl = 1s;
+		set beresp.grace = 1m;
+		set beresp.http.id = bereq.http.id;
+	}
+} -start
+
+client c1 {
+	txreq -hdr "id: 1"
+	rxresp
+	expect resp.status == 200
+	txreq -hdr "id: 2"
+	rxresp
+	expect resp.status == 200
+	txreq -hdr "id: 3"
+	rxresp
+	expect resp.status == 200
+
+	# the first object is the one which went into cache last
+
+	txreq -hdr "get: first"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.id == "3"
+
+	txreq -hdr "get: last"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.id == "1"
+} -run
diff --git a/doc/sphinx/reference/vtla.rst b/doc/sphinx/reference/vtla.rst
index 1fdfb4abf..76dccd068 100644
--- a/doc/sphinx/reference/vtla.rst
+++ b/doc/sphinx/reference/vtla.rst
@@ -31,6 +31,9 @@ VCA
 VCC
     VCL to C Compiler -- The code that compiles VCL to C code. (lib/libvcl)
 
+VCF
+    Varnish CatFlap
+
 VCL
     Varnish Configuration Language -- The domain-specific programming
     language used for configuring a varnishd.
diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc
index 2ff1d0220..4c5f652eb 100644
--- a/lib/libvmod_debug/vmod.vcc
+++ b/lib/libvmod_debug/vmod.vcc
@@ -230,3 +230,7 @@ $Method STRING .meth_opt(PRIV_CALL, PRIV_VCL, PRIV_TASK,
 Test object method with all the fancy stuff.
 
 $Function STRANDS return_strands(STRANDS strand)
+
+$Function VOID catflap(ENUM {miss, first, last} type)
+
+Test the HSH_Lookup catflap
diff --git a/lib/libvmod_debug/vmod_debug.c b/lib/libvmod_debug/vmod_debug.c
index fa05142b6..7161596ae 100644
--- a/lib/libvmod_debug/vmod_debug.c
+++ b/lib/libvmod_debug/vmod_debug.c
@@ -630,3 +630,72 @@ xyzzy_return_strands(VRT_CTX, VCL_STRANDS strand)
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	return (strand);
 }
+
+/*---------------------------------------------------------------------*/
+
+static const struct vcf_return * v_matchproto_(vcf_func_f)
+xyzzy_catflap_simple(struct req *req, struct objcore **oc,
+    struct objcore **oc_exp, int state)
+{
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->vcf, VCF_MAGIC);
+	assert(req->vcf->func == xyzzy_catflap_simple);
+
+	(void)oc;
+	(void)oc_exp;
+	if (state == 0) {
+		if (req->vcf->priv == VENUM(first))
+			return (VCF_HIT);
+		if (req->vcf->priv == VENUM(miss))
+			return (VCF_MISS);
+		WRONG("Shouldn't get here");
+	}
+	return (VCF_DEFAULT);
+}
+
+static const struct vcf_return * v_matchproto_(vcf_func_f)
+xyzzy_catflap_last(struct req *req, struct objcore **oc,
+    struct objcore **oc_exp, int state)
+{
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->vcf, VCF_MAGIC);
+	assert(req->vcf->func == xyzzy_catflap_last);
+
+	(void)oc_exp;
+	if (state == 0) {
+		AN(oc);
+		CHECK_OBJ_NOTNULL(*oc, OBJCORE_MAGIC);
+		req->vcf->priv = *oc;
+		return (VCF_CONTINUE);
+	}
+	if (state == 1) {
+		AN(oc);
+		if (req->vcf->priv != NULL)
+			CAST_OBJ_NOTNULL(*oc, req->vcf->priv, OBJCORE_MAGIC);
+		return (VCF_CONTINUE);
+	}
+	return (VCF_DEFAULT);
+}
+
+VCL_VOID
+xyzzy_catflap(VRT_CTX, VCL_ENUM type)
+{
+	struct req *req;
+
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	req = ctx->req;
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	XXXAZ(req->vcf);
+	req->vcf = WS_Alloc(req->ws, sizeof *req->vcf);
+	INIT_OBJ(req->vcf, VCF_MAGIC);
+	if (type == VENUM(first) || type == VENUM(miss)) {
+		req->vcf->func = xyzzy_catflap_simple;
+		req->vcf->priv = TRUST_ME(type);
+	} else if (type == VENUM(last)) {
+		req->vcf->func = xyzzy_catflap_last;
+	} else {
+		WRONG("Wrong VENUM");
+	}
+}


More information about the varnish-commit mailing list