[PATCH] Fallback director (update)
Rogier R. Mulhuijzen
varnish at bsdchicks.com
Tue Aug 23 00:43:06 CEST 2011
By Tollef's request, here's the fallback director patch that applies
cleanly to current trunk. I managed to get something else into the
previous patch I linked from github.
Cheers,
Rogier/DocWilco
-------------- next part --------------
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..c039650
--- /dev/null
+++ b/bin/varnishtest/tests/v00036.vtc
@@ -0,0 +1,132 @@
+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-dev
mailing list