[master] a4bc345 Add a "fallback" director as a variant of round-robin.

Poul-Henning Kamp phk at varnish-cache.org
Thu Aug 25 14:14:17 CEST 2011


commit a4bc345af4ba8ce9f35985bcf1df7b6c6a15f726
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Thu Aug 25 12:13:21 2011 +0000

    Add a "fallback" director as a variant of round-robin.
    
    This one always picks the first healty backend, in the order
    they are specified in the VCL.
    
    Submitted by:	DocWilco

diff --git a/bin/varnishd/cache_backend.h b/bin/varnishd/cache_backend.h
index d2de06e..21a7061 100644
--- a/bin/varnishd/cache_backend.h
+++ b/bin/varnishd/cache_backend.h
@@ -156,4 +156,5 @@ dir_init_f VRT_init_dir_dns;
 dir_init_f VRT_init_dir_hash;
 dir_init_f VRT_init_dir_random;
 dir_init_f VRT_init_dir_round_robin;
+dir_init_f VRT_init_dir_fallback;
 dir_init_f VRT_init_dir_client;
diff --git a/bin/varnishd/cache_backend_cfg.c b/bin/varnishd/cache_backend_cfg.c
index e6c935c..0582f72 100644
--- a/bin/varnishd/cache_backend_cfg.c
+++ b/bin/varnishd/cache_backend_cfg.c
@@ -256,6 +256,8 @@ VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
 		VRT_init_dir_dns(cli, dir, idx, priv);
 	else if (!strcmp(name, "round-robin"))
 		VRT_init_dir_round_robin(cli, dir, idx, priv);
+	else if (!strcmp(name, "fallback"))
+		VRT_init_dir_fallback(cli, dir, idx, priv);
 	else if (!strcmp(name, "client"))
 		VRT_init_dir_client(cli, dir, idx, priv);
 	else
diff --git a/bin/varnishd/cache_dir_round_robin.c b/bin/varnishd/cache_dir_round_robin.c
index 2a6009a..61d80fc 100644
--- a/bin/varnishd/cache_dir_round_robin.c
+++ b/bin/varnishd/cache_dir_round_robin.c
@@ -47,10 +47,13 @@ struct vdi_round_robin_host {
 	struct director			*backend;
 };
 
+enum mode_e { m_round_robin, m_fallback };
+
 struct vdi_round_robin {
 	unsigned			magic;
 #define VDI_ROUND_ROBIN_MAGIC		0x2114a178
 	struct director			dir;
+	enum mode_e			mode;
 	struct vdi_round_robin_host	*hosts;
 	unsigned			nhosts;
 	unsigned			next_host;
@@ -68,9 +71,17 @@ vdi_round_robin_getfd(const struct director *d, struct sess *sp)
 	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
 	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
 
+	/* 
+	 * In fallback mode we ignore the next_host and always grab the 
+	 * first healthy backend we can find.
+	 */
 	for (i = 0; i < vs->nhosts; i++) {
-		backend = vs->hosts[vs->next_host].backend;
-		vs->next_host = (vs->next_host + 1) % vs->nhosts;
+		if (vs->mode == m_round_robin) {
+			backend = vs->hosts[vs->next_host].backend;
+			vs->next_host = (vs->next_host + 1) % vs->nhosts;
+		} else /* m_fallback */ {
+			backend = vs->hosts[i].backend;
+		}
 		if (!VDI_Healthy(backend, sp))
 			continue;
 		vbe = VDI_GetFd(backend, sp);
@@ -114,9 +125,9 @@ vdi_round_robin_fini(const struct director *d)
 	FREE_OBJ(vs);
 }
 
-void
-VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
+static void
+vrt_init_dir(struct cli *cli, struct director **bp, int idx,
+    const void *priv, enum mode_e mode)
 {
 	const struct vrt_dir_round_robin *t;
 	struct vdi_round_robin *vs;
@@ -141,6 +152,7 @@ VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
 	vs->dir.fini = vdi_round_robin_fini;
 	vs->dir.healthy = vdi_round_robin_healthy;
 
+	vs->mode = mode;
 	vh = vs->hosts;
 	te = t->members;
 	for (i = 0; i < t->nmember; i++, vh++, te++) {
@@ -152,3 +164,18 @@ VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
 
 	bp[idx] = &vs->dir;
 }
+
+void
+VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init_dir(cli, bp, idx, priv, m_round_robin);
+}
+
+void
+VRT_init_dir_fallback(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init_dir(cli, bp, idx, priv, m_fallback);
+}
+
diff --git a/bin/varnishtest/tests/v00036.vtc b/bin/varnishtest/tests/v00036.vtc
new file mode 100644
index 0000000..ff2ecd3
--- /dev/null
+++ b/bin/varnishtest/tests/v00036.vtc
@@ -0,0 +1,131 @@
+varnishtest "Test fallback director"
+
+server s1 {
+	rxreq
+	expect req.url == "/"
+	txresp -body "slash"
+} -start
+
+server s2 {
+	rxreq
+	expect req.url == "/"
+	txresp -body "slash"
+} -start
+
+server s3 {
+	rxreq
+	expect req.url == "/foo"
+	txresp -hdr "Foo: 3" -body "foobar"
+} -start
+
+varnish v1 -vcl {
+
+	probe p1 {
+		.url = "/";
+		.timeout = 1s;
+		.interval = 1s;
+		.window = 4;
+		.threshold = 3;
+		.initial = 0;
+	}
+	probe p2 {
+		.url = "/";
+		.timeout = 1s;
+		.interval = 1s;
+		.window = 3;
+		.threshold = 2;
+		.initial = 0;
+	}
+
+	backend b1 {
+		.host = "${s1_addr}";
+		.port = "${s1_port}";
+		.max_connections = 1;
+		.probe = p1;
+	}
+	backend b2 {
+		.host = "${s2_addr}";
+		.port = "${s2_port}";
+		.max_connections = 1;
+		.probe = p2;
+	}
+	backend b3 {
+		.host = "${s3_addr}";
+		.port = "${s3_port}";
+	}
+	director f1 fallback {
+		{ .backend = b1; }
+		{ .backend = b2; }
+		{ .backend = b3; }
+	}
+
+	sub vcl_recv {
+		set req.backend = f1;
+		return(pass);
+	}
+} -start
+
+# s1 & s2 have both had 1 probe, so both are unhealthy
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.http.foo == "3"
+} -run
+
+# setup for probe #2
+
+server s1 {
+	rxreq
+	expect req.url == "/"
+	txresp -body "slash"
+} -start
+
+server s2 {
+	rxreq
+	expect req.url == "/"
+	txresp -body "slash"
+} -start
+
+# if we muck with a running server, the test will wait until it's done,
+# which will be after probe #2 completes. b2 will then be healthy.
+
+server s2 {
+	rxreq
+	expect req.url == "/foo"
+	txresp -hdr "Foo: 2" -body "foobar"
+} -start
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.http.foo == "2"
+} -run
+
+# setup for probe #3
+
+server s1 {
+        rxreq
+        expect req.url == "/"
+        txresp -body "slash"
+} -start
+
+server s2 {
+        rxreq
+        expect req.url == "/"
+        txresp -body "slash"
+} -start
+
+# after probe #3 b1 should be healthy.
+
+server s1 {
+	rxreq
+	expect req.url == "/foo"
+	txresp -hdr "Foo: 1" -body "foobar"
+} -start
+
+client c1 {
+	txreq -url "/foo"
+	rxresp
+	expect resp.http.foo == "1"
+} -run
diff --git a/doc/sphinx/reference/vcl.rst b/doc/sphinx/reference/vcl.rst
index 281b653..4645cd1 100644
--- a/doc/sphinx/reference/vcl.rst
+++ b/doc/sphinx/reference/vcl.rst
@@ -221,6 +221,22 @@ The above example will append "internal.example.net" to the incoming Host
 header supplied by the client, before looking it up. All settings are
 optional.
 
+The fallback director
+~~~~~~~~~~~~~~~~~~~~~
+
+The fallback director will pick the first backend that is healthy. It 
+considers them in the order in which they are listed in its definition.
+
+The fallback director does not take any options.
+
+An example of a fallback director::
+
+  director b3 fallback {
+    { .backend = www1; }
+    { .backend = www2; } // will only be used if www1 is unhealthy.
+    { .backend = www3; } // will only be used if both www1 and www2
+                         // are unhealthy.
+  }
 
 Backend probes
 --------------
diff --git a/lib/libvcl/vcc_backend.c b/lib/libvcl/vcc_backend.c
index babfe01..b896a7d 100644
--- a/lib/libvcl/vcc_backend.c
+++ b/lib/libvcl/vcc_backend.c
@@ -695,6 +695,7 @@ static const struct dirlist {
 	{ "random",		vcc_ParseRandomDirector },
 	{ "client",		vcc_ParseRandomDirector },
 	{ "round-robin",	vcc_ParseRoundRobinDirector },
+	{ "fallback",		vcc_ParseRoundRobinDirector },
 	{ "dns",		vcc_ParseDnsDirector },
 	{ NULL,		NULL }
 };



More information about the varnish-commit mailing list