[PATCH] Add a recycle_maxage attribute to backend definitions.

Martin Blix Grydeland martin at varnish-software.com
Wed Aug 27 17:01:13 CEST 2014


When selecting a backend for reuse, the list is first checked from the
end for connections that has expired it's maximum age. Any connection
in the list that is found to be too old is closed and removed from the
list.

This can be useful to tune when one is having e.g. transparent load
balancers behind Varnish that will close a connection without send RST
to the Varnish servers, resulting in RST on the first packet
sent. These connections will then appear to be ready to be reused. By
tuning this slightly lower than the lowest connection timeout setting
for the complete backend request path, it will be possible to avoid
generating 503.

The maxage is setable as a global parameter, or as a per backend
attribute from VCL.
---
 bin/varnishd/cache/cache_backend.c | 52 +++++++++++++++++++++++++++++++++++---
 bin/varnishd/cache/cache_backend.h |  3 ++-
 bin/varnishd/cache/cache_dir.c     |  2 ++
 bin/varnishd/common/params.h       |  3 +++
 bin/varnishd/mgt/mgt_param_tbl.c   |  8 ++++++
 bin/varnishtest/tests/c00069.vtc   | 47 ++++++++++++++++++++++++++++++++++
 doc/sphinx/reference/vcl.rst       |  4 +++
 include/vrt.h                      |  1 +
 lib/libvcc/vcc_backend.c           |  7 +++++
 9 files changed, 122 insertions(+), 5 deletions(-)
 create mode 100644 bin/varnishtest/tests/c00069.vtc

diff --git a/bin/varnishd/cache/cache_backend.c b/bin/varnishd/cache/cache_backend.c
index 6622f4b..ab638e2 100644
--- a/bin/varnishd/cache/cache_backend.c
+++ b/bin/varnishd/cache/cache_backend.c
@@ -42,6 +42,7 @@
 #include "cache_backend.h"
 #include "vrt.h"
 #include "vtcp.h"
+#include "vtim.h"
 
 static struct mempool	*vbcpool;
 
@@ -185,6 +186,26 @@ bes_conn_try(struct busyobj *bo, struct vbc *vc, const struct vdi_simple *vs)
 }
 
 /*--------------------------------------------------------------------
+ * Check that the connection hasn't expired the relevant maxage setting.
+ */
+
+static int
+vbe_CheckLastUse(const struct vbc *vbc, double now)
+{
+	struct vdi_simple *vdis;
+
+	CHECK_OBJ_NOTNULL(vbc, VBC_MAGIC);
+	vdis = vbc->vdis;
+	CHECK_OBJ_NOTNULL(vdis, VDI_SIMPLE_MAGIC);
+	AN(vdis->vrt);
+
+	assert(vbc->last_use > 0.);
+	if (vdis->vrt->recycle_maxage > 0.)
+		return (now - vbc->last_use < vdis->vrt->recycle_maxage);
+	return (now - vbc->last_use < cache_param->recycle_maxage);
+}
+
+/*--------------------------------------------------------------------
  * Check that there is still something at the far end of a given socket.
  * We poll the fd with instant timeout, if there are any events we can't
  * use it (backends are not allowed to pipeline).
@@ -249,6 +270,8 @@ vbe_GetVbe(struct busyobj *bo, struct vdi_simple *vs)
 {
 	struct vbc *vc;
 	struct backend *bp;
+	double now;
+	int toolate;
 
 	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
 	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
@@ -256,10 +279,23 @@ vbe_GetVbe(struct busyobj *bo, struct vdi_simple *vs)
 	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
 
 	/* first look for vbc's we can recycle */
+	now = VTIM_real();
 	while (1) {
 		Lck_Lock(&bp->mtx);
-		vc = VTAILQ_FIRST(&bp->connlist);
+		toolate = 0;
+		vc = VTAILQ_LAST(&bp->connlist, vtqh_vbc);
+		CHECK_OBJ_ORNULL(vc, VBC_MAGIC);
+		if (vc != NULL && !vbe_CheckLastUse(vc, now)) {
+			/* Least recently used is too old */
+			VSLb(bo->vsl, SLT_BackendClose, "%d %s toolate",
+			    vc->fd, bp->display_name);
+			toolate = 1;
+		} else
+			/* Pick the most recently used */
+			vc = VTAILQ_FIRST(&bp->connlist);
+
 		if (vc != NULL) {
+			CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
 			bp->refcount++;
 			assert(vc->backend == bp);
 			assert(vc->fd >= 0);
@@ -267,9 +303,16 @@ vbe_GetVbe(struct busyobj *bo, struct vdi_simple *vs)
 			VTAILQ_REMOVE(&bp->connlist, vc, list);
 		}
 		Lck_Unlock(&bp->mtx);
+
 		if (vc == NULL)
 			break;
-		if (vbe_CheckFd(vc->fd)) {
+		if (!toolate && !vbe_CheckFd(vc->fd)) {
+			VSLb(bo->vsl, SLT_BackendClose, "%d %s poll",
+			    vc->fd, bp->display_name);
+			toolate = 1;
+		}
+
+		if (!toolate) {
 			/* XXX locking of stats */
 			VSC_C_main->backend_reuse += 1;
 			VSLb(bo->vsl, SLT_Backend, "%d %s %s",
@@ -277,11 +320,11 @@ vbe_GetVbe(struct busyobj *bo, struct vdi_simple *vs)
 			    bp->display_name);
 			vc->vdis = vs;
 			vc->recycled = 1;
+			vc->last_use = now;
 			return (vc);
 		}
+
 		VSC_C_main->backend_toolate++;
-		VSLb(bo->vsl, SLT_BackendClose, "%d %s toolate",
-		    vc->fd, bp->display_name);
 
 		/* Checkpoint log to flush all info related to this connection
 		   before the OS reuses the FD */
@@ -318,6 +361,7 @@ vbe_GetVbe(struct busyobj *bo, struct vdi_simple *vs)
 	VSLb(bo->vsl, SLT_Backend, "%d %s %s",
 	    vc->fd, bo->director->vcl_name, bp->display_name);
 	vc->vdis = vs;
+	vc->last_use = now;
 	return (vc);
 }
 
diff --git a/bin/varnishd/cache/cache_backend.h b/bin/varnishd/cache/cache_backend.h
index c9ce112..adb978b 100644
--- a/bin/varnishd/cache/cache_backend.h
+++ b/bin/varnishd/cache/cache_backend.h
@@ -117,7 +117,7 @@ struct backend {
 	struct suckaddr		*ipv6;
 
 	unsigned		n_conn;
-	VTAILQ_HEAD(, vbc)	connlist;
+	VTAILQ_HEAD(vtqh_vbc, vbc) connlist;
 
 	struct vbp_target	*probe;
 	unsigned		healthy;
@@ -142,6 +142,7 @@ struct vbc {
 	struct suckaddr		*addr;
 
 	uint8_t			recycled;
+	double			last_use;
 
 	/* Timeouts */
 	double			first_byte_timeout;
diff --git a/bin/varnishd/cache/cache_dir.c b/bin/varnishd/cache/cache_dir.c
index 5d374ab..557aa01 100644
--- a/bin/varnishd/cache/cache_dir.c
+++ b/bin/varnishd/cache/cache_dir.c
@@ -36,6 +36,7 @@
 
 #include "cache_backend.h"
 #include "vtcp.h"
+#include "vtim.h"
 
 /* Close a connection ------------------------------------------------*/
 
@@ -83,6 +84,7 @@ VDI_RecycleFd(struct vbc **vbp, const struct acct_bereq *acct_bereq)
 	CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
 	CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
 	assert(vc->fd >= 0);
+	vc->last_use = VTIM_real();
 
 	bp = vc->backend;
 
diff --git a/bin/varnishd/common/params.h b/bin/varnishd/common/params.h
index 2ce95c6..f1d7615 100644
--- a/bin/varnishd/common/params.h
+++ b/bin/varnishd/common/params.h
@@ -164,6 +164,9 @@ struct params {
 	double			first_byte_timeout;
 	double			between_bytes_timeout;
 
+	/* Maximum recycled backend connection age */
+	double			recycle_maxage;
+
 	/* CLI buffer size */
 	unsigned		cli_buffer;
 
diff --git a/bin/varnishd/mgt/mgt_param_tbl.c b/bin/varnishd/mgt/mgt_param_tbl.c
index 55889bc..2519c65 100644
--- a/bin/varnishd/mgt/mgt_param_tbl.c
+++ b/bin/varnishd/mgt/mgt_param_tbl.c
@@ -365,6 +365,14 @@ struct parspec mgt_parspec[] = {
 		"and backend request. This parameter does not apply to pipe.",
 		0,
 		"60", "s" },
+	{ "recycle_maxage", tweak_timeout,
+		&mgt_param.recycle_maxage,
+		"0", NULL,
+		"Default maximum age of a recycled backend connection. If the "
+		"age is exceeded the connection will be closed. VCL can "
+		"override this default value for each backend.",
+		0,
+		"60", "s" },
 	{ "acceptor_sleep_max", tweak_timeout,
 		&mgt_param.acceptor_sleep_max,
 		"0", "10",
diff --git a/bin/varnishtest/tests/c00069.vtc b/bin/varnishtest/tests/c00069.vtc
new file mode 100644
index 0000000..3244853
--- /dev/null
+++ b/bin/varnishtest/tests/c00069.vtc
@@ -0,0 +1,47 @@
+varnishtest "Check recycle_maxage"
+
+server s1 {
+	rxreq
+	txresp
+
+	rxreq
+	txresp
+
+	# Expect a close happening on the next backend attempt
+	delay 1
+	expect_close
+
+	accept
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl {
+	backend default {
+		.host = "${s1_addr}";
+		.port = "${s1_port}";
+		.recycle_maxage = 1s;
+	}
+	sub vcl_recv {
+		return (pass);
+	}
+} -start
+
+# Make sure that the global parameter is setable and that the per backend
+# takes precedence
+varnish v1 -cliok "param.set recycle_maxage 10"
+
+client c1 {
+	txreq -url "/one"
+	rxresp
+
+	txreq -url "/two"
+	rxresp
+
+	# Delay .1 second longer than the server delay, to put us at the time
+	# the server expects the close
+	delay 1.1
+
+	txreq -url "/three"
+	rxresp
+} -run
diff --git a/doc/sphinx/reference/vcl.rst b/doc/sphinx/reference/vcl.rst
index 6828f99..0d5c660 100644
--- a/doc/sphinx/reference/vcl.rst
+++ b/doc/sphinx/reference/vcl.rst
@@ -202,6 +202,10 @@ are available:
   between_bytes_timeout
     Timeout between bytes.
 
+  recycle_maxage
+    Maximum age allowed for attempts to reuse a recycled backend
+    connection.
+
   probe
     Attach a probe to the backend. See Probes.
 
diff --git a/include/vrt.h b/include/vrt.h
index cb47db9..b193429 100644
--- a/include/vrt.h
+++ b/include/vrt.h
@@ -163,6 +163,7 @@ struct vrt_backend {
 	double				connect_timeout;
 	double				first_byte_timeout;
 	double				between_bytes_timeout;
+	double				recycle_maxage;
 	unsigned			max_connections;
 	const struct vrt_backend_probe	*probe;
 };
diff --git a/lib/libvcc/vcc_backend.c b/lib/libvcc/vcc_backend.c
index 7c079a9..18362a7 100644
--- a/lib/libvcc/vcc_backend.c
+++ b/lib/libvcc/vcc_backend.c
@@ -294,6 +294,7 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be)
 	    "?between_bytes_timeout",
 	    "?probe",
 	    "?max_connections",
+	    "?recycle_maxage",
 	    NULL);
 
 	SkipToken(tl, '{');
@@ -381,6 +382,12 @@ vcc_ParseHostDef(struct vcc *tl, const struct token *t_be)
 			VSB_printf(tl->sb, " at\n");
 			vcc_ErrWhere(tl, tl->t);
 			return;
+		} else if (vcc_IdIs(t_field, "recycle_maxage")) {
+			Fb(tl, 0, "\t.recycle_maxage = ");
+			vcc_Duration(tl, &t);
+			ERRCHK(tl);
+			Fb(tl, 0, "%g,\n", t);
+			SkipToken(tl, ';');
 		} else {
 			ErrInternal(tl);
 			return;
-- 
2.1.0.rc1




More information about the varnish-dev mailing list