Commit 59caf963 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Exploit the new and cleaner director interface and add a "hash" director

by sharing most of the code with the "random" director.

The Hash director will use the hash output from vcl_hash{} to pick a
backend from the configured set.

If this backend is unhealthy, it pick another backend, using only the
healthy subset of backends, but as soon as the "canonical" backend becomes
healthy it will revert to using that.

This means that one backend going unhealthy on "rehashes" the requests for
that backend, hashing for healthy backends are not affected.





git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@4419 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent ed03c78f
......@@ -155,6 +155,8 @@ void VBP_Stop(struct backend *b);
/* Init functions for directors */
void VRT_init_dir_simple(struct cli *, struct director **, int , const void*);
void VRT_init_dir_random(struct cli *, struct director **, int , const void*);
void VRT_init_dir_round_robin(struct cli *, struct director **, int , const void*);
typedef void dir_init_f(struct cli *, struct director **, int , const void*);
dir_init_f VRT_init_dir_simple;
dir_init_f VRT_init_dir_hash;
dir_init_f VRT_init_dir_random;
dir_init_f VRT_init_dir_round_robin;
......@@ -269,6 +269,8 @@ VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
ASSERT_CLI();
if (!strcmp(name, "simple"))
VRT_init_dir_simple(cli, dir, idx, priv);
else if (!strcmp(name, "hash"))
VRT_init_dir_hash(cli, dir, idx, priv);
else if (!strcmp(name, "random"))
VRT_init_dir_random(cli, dir, idx, priv);
else if (!strcmp(name, "round-robin"))
......
......@@ -26,6 +26,17 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This code is shared between the random and hash directors, because they
* share the same properties and most of the same selection logic.
*
* The random director picks a backend on random, according to weight,
* from the healty subset of backends.
*
* The hash director first tries to locate the "canonical" backend from
* the full set, according to weight, and if it is healthy selects it.
* If the canonical backend is not healthy, we pick a backend according
* to weight from the healthy subset. That way only traffic to unhealthy
* backends gets redistributed.
*/
#include "config.h"
......@@ -59,7 +70,9 @@ struct vdi_random {
#define VDI_RANDOM_MAGIC 0x3771ae23
struct director dir;
unsigned use_hash;
unsigned retries;
double tot_weight;
struct vdi_random_host *hosts;
unsigned nhosts;
};
......@@ -70,6 +83,7 @@ vdi_random_getfd(struct director *d, struct sess *sp)
int i, k;
struct vdi_random *vs;
double r, s1;
unsigned u;
struct vbe_conn *vbe;
struct director *d2;
......@@ -77,6 +91,30 @@ vdi_random_getfd(struct director *d, struct sess *sp)
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
/*
* If we are hashing, first try to hit our "canonical backend"
* If that fails, we fall through, and select a weighted backend
* amongst the good set.
*/
if (vs->use_hash) {
memcpy(&u, sp->digest, sizeof u);
r = u / 4294967296.0;
r *= vs->tot_weight;
s1 = 0.0;
for (i = 0; i < vs->nhosts; i++) {
s1 += vs->hosts[i].weight;
if (r >= s1)
continue;
d2 = vs->hosts[i].backend;
if (!VBE_Healthy(d2, sp))
break;
vbe = VBE_GetFd(d2, sp);
if (vbe != NULL)
return (vbe);
break;
}
}
for (k = 0; k < vs->retries; ) {
/* Sum up the weights of healty backends */
......@@ -91,8 +129,13 @@ vdi_random_getfd(struct director *d, struct sess *sp)
if (s1 == 0.0)
return (NULL);
/* Pick a random threshold in that interval */
r = random() / 2147483648.0; /* 2^31 */
if (vs->use_hash) {
memcpy(&u, sp->digest, sizeof u);
r = u / 4294967296.0;
} else {
/* Pick a random threshold in that interval */
r = random() / 2147483648.0; /* 2^31 */
}
assert(r >= 0.0 && r < 1.0);
r *= s1;
......@@ -119,15 +162,13 @@ vdi_random_healthy(struct director *d, const struct sess *sp)
{
struct vdi_random *vs;
int i;
struct director *d2;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
for (i = 0; i < vs->nhosts; i++) {
d2 = vs->hosts[i].backend;
if (VBE_Healthy(d2, sp))
if (VBE_Healthy(vs->hosts[i].backend, sp))
return 1;
}
return 0;
......@@ -148,9 +189,9 @@ vdi_random_fini(struct director *d)
FREE_OBJ(vs);
}
void
VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
const void *priv)
static void
vrt_init(struct cli *cli, struct director **bp, int idx,
const void *priv, int use_hash)
{
const struct vrt_dir_random *t;
struct vdi_random *vs;
......@@ -175,17 +216,34 @@ VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
vs->dir.fini = vdi_random_fini;
vs->dir.healthy = vdi_random_healthy;
vs->use_hash = use_hash;
vs->retries = t->retries;
if (vs->retries == 0)
vs->retries = t->nmember;
vh = vs->hosts;
te = t->members;
vs->tot_weight = 0.;
for (i = 0; i < t->nmember; i++, vh++, te++) {
assert(te->weight > 0.0);
vh->weight = te->weight;
vs->tot_weight += vh->weight;
vh->backend = bp[te->host];
AN(vh->backend);
}
vs->nhosts = t->nmember;
bp[idx] = &vs->dir;
}
void
VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init(cli, bp, idx, priv, 0);
}
void
VRT_init_dir_hash(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init(cli, bp, idx, priv, 1);
}
# $Id$
test "Hash director"
server s1 {
rxreq
txresp -hdr "Foo: 1" -body "1"
rxreq
txresp -hdr "Foo: 3" -body "3"
} -start
server s2 {
rxreq
txresp -hdr "Foo: 2" -body "2"
rxreq
txresp -hdr "Foo: 4" -body "4"
} -start
varnish v1 -vcl+backend {
director h1 hash {
{ .backend = s1; .weight = 1; }
{ .backend = s2; .weight = 1; }
}
sub vcl_recv {
set req.backend = h1;
pass;
}
} -start
client c1 {
txreq -url /12
rxresp
expect resp.http.foo == "1"
txreq -url /1
rxresp
expect resp.http.foo == "2"
txreq -url /13
rxresp
expect resp.http.foo == "3"
txreq -url /15
rxresp
expect resp.http.foo == "4"
} -run
......@@ -776,6 +776,7 @@ static const struct dirlist {
const char *name;
parsedirector_f *func;
} dirlist[] = {
{ "hash", vcc_ParseRandomDirector },
{ "random", vcc_ParseRandomDirector },
{ "round-robin", vcc_ParseRoundRobinDirector },
{ NULL, NULL }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment