Hi,

As mentioned on IRC It's not guaranteed that the first request will have a 
backend assigned before the second request comes in so this is not bulletproof 
but should be good enough for most usages.
Comments? OKs?

f.-

diff --git a/bin/varnishd/cache/cache_backend.h 
b/bin/varnishd/cache/cache_backend.h
index b84e702..3567cda 100644
--- a/bin/varnishd/cache/cache_backend.h
+++ b/bin/varnishd/cache/cache_backend.h
@@ -190,3 +190,4 @@ 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;
+dir_init_f VRT_init_dir_least_connection;
diff --git a/bin/varnishd/cache/cache_backend_cfg.c 
b/bin/varnishd/cache/cache_backend_cfg.c
index 3f15ada..5800b4a 100644
--- a/bin/varnishd/cache/cache_backend_cfg.c
+++ b/bin/varnishd/cache/cache_backend_cfg.c
@@ -263,6 +263,8 @@ VRT_init_dir(struct cli *cli, struct director **dir, const 
char *name,
                VRT_init_dir_fallback(cli, dir, idx, priv);
        else if (!strcmp(name, "client"))
                VRT_init_dir_client(cli, dir, idx, priv);
+       else if (!strcmp(name, "least-connection"))
+               VRT_init_dir_least_connection(cli, dir, idx, priv);
        else
                INCOMPL();
 }
diff --git a/bin/varnishd/cache/cache_dir_random.c 
b/bin/varnishd/cache/cache_dir_random.c
index c291fe0..4959eb4 100644
--- a/bin/varnishd/cache/cache_dir_random.c
+++ b/bin/varnishd/cache/cache_dir_random.c
@@ -26,8 +26,9 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * This code is shared between the random, client and hash directors, because
- * they share the same properties and most of the same selection logic.
+ * This code is shared between the random, client, hash and least-connection
+ * directors, because they share the same properties and most of the same
+ * selection logic.
  *
  * The random director picks a backend on random.
  *
@@ -35,6 +36,9 @@
  *
  * The client director picks based on client identity or IP-address
  *
+ * The least-connection director picks a backend with the least number of
+ * established connections.
+ *
  * In all cases, the choice is by weight of the healthy subset of
  * configured backends.
  *
@@ -63,7 +67,7 @@ struct vdi_random_host {
        double                  weight;
 };
 
-enum crit_e {c_random, c_hash, c_client};
+enum crit_e {c_random, c_hash, c_client, c_least_connection};
 
 struct vdi_random {
        unsigned                magic;
@@ -195,6 +199,36 @@ vdi_random_getfd(const struct director *d, struct req *req)
        return (vdi_random_pick_one(req, vs, r, vs->retries));
 }
 
+static struct vbc *
+vdi_least_connection_getfd(const struct director *d, struct req *req)
+{
+       int i, m;
+       struct vdi_random *vs;
+       struct backend *bi, *bm;
+
+       CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+       CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+       CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
+
+       for (m = 0; m < vs->nhosts; m++)  {
+               if (!VDI_Healthy(vs->hosts[m].backend, req))
+                       continue;
+               bm = vdi_get_backend_if_simple(vs->hosts[m].backend);
+               for (i = m + 1; i < vs->nhosts; i++) {
+                       if (!VDI_Healthy(vs->hosts[i].backend, req))
+                               continue;
+                       bi = vdi_get_backend_if_simple(vs->hosts[i].backend);
+                       if (bm->refcount * vs->hosts[i].weight >
+                           bi->refcount * vs->hosts[m].weight) {
+                               bm = bi;
+                               m = i;
+                       }
+               }
+               return (VDI_GetFd(vs->hosts[m].backend, req));
+       }
+       return (NULL);
+}
+
 /*
  * Healthy if just a single backend is...
  * XXX: we should really have a weight param/criteria here
@@ -252,7 +286,10 @@ vrt_init(struct cli *cli, struct director **bp, int idx,
        vs->dir.priv = vs;
        vs->dir.name = "random";
        REPLACE(vs->dir.vcl_name, t->name);
-       vs->dir.getfd = vdi_random_getfd;
+       if (criteria == c_least_connection)
+               vs->dir.getfd = vdi_least_connection_getfd;
+       else
+               vs->dir.getfd = vdi_random_getfd;
        vs->dir.fini = vdi_random_fini;
        vs->dir.healthy = vdi_random_healthy;
 
@@ -294,3 +331,10 @@ VRT_init_dir_client(struct cli *cli, struct director **bp, 
int idx,
 {
        vrt_init(cli, bp, idx, priv, c_client);
 }
+
+void
+VRT_init_dir_least_connection(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+       vrt_init(cli, bp, idx, priv, c_least_connection);
+}
diff --git a/bin/varnishtest/tests/v00038.vtc b/bin/varnishtest/tests/v00038.vtc
new file mode 100644
index 0000000..a5c3ae3
--- /dev/null
+++ b/bin/varnishtest/tests/v00038.vtc
@@ -0,0 +1,92 @@
+varnishtest "Test least-connection director"
+
+server s1 {
+       rxreq
+       sema r1 sync 3
+       sema r1 sync 2
+       txresp -hdr "be: s1"
+       close
+
+       accept
+       rxreq
+       txresp -hdr "be: s1"
+} -start
+
+server s2 {
+       rxreq
+       sema r1 sync 3
+       txresp -hdr "be: s2"
+       close
+
+       accept
+       rxreq
+       txresp -hdr "be: s2"
+} -start
+
+server s3 {
+       rxreq
+       txresp -hdr "be: s3"
+       close
+
+       accept
+       rxreq
+       txresp -hdr "be: s3"
+} -start
+
+varnish v1 -vcl+backend {
+       director lc least-connection {
+               { .backend = s1; .weight = 1; }
+               { .backend = s2; .weight = 1; }
+               { .backend = s3; .weight = 1; }
+       }
+       sub vcl_recv {
+               set req.backend = lc;
+               return (pass);
+       }
+} -start
+
+client c1 {
+       txreq
+       rxresp
+       expect resp.http.be == "s1"
+} -start
+
+delay .5
+
+client c2 {
+       txreq
+       rxresp
+       expect resp.http.be == "s2"
+} -start
+
+delay .5
+
+client c3 {
+       txreq
+       rxresp
+       expect resp.http.be == "s3"
+
+       txreq
+       rxresp
+       expect resp.http.be == "s3"
+
+       sema r1 sync 3
+
+       delay .5
+
+       txreq
+       rxresp
+       expect resp.http.be == "s2"
+
+       sema r1 sync 2
+
+       delay .5
+
+       txreq
+       rxresp
+       expect resp.http.be == "s1"
+} -start
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
diff --git a/doc/sphinx/reference/vcl.rst b/doc/sphinx/reference/vcl.rst
index 50c339b..16ccdd2 100644
--- a/doc/sphinx/reference/vcl.rst
+++ b/doc/sphinx/reference/vcl.rst
@@ -177,6 +177,17 @@ An example of a fallback director:
                          // are unhealthy.
   }
 
+The least-connection director
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The least-connection director will pick a backend with the least number of
+established connections.
+
+Each backend requires a .weight option which sets the amount of traffic
+each backend will get compared to the others. Equal weight means equal
+traffic. A backend with lower weight than an other will get proportionally
+less traffic.
+
 Backend probes
 --------------
 
diff --git a/lib/libvcl/vcc_backend.c b/lib/libvcl/vcc_backend.c
index 78cb0b2..df31ac6 100644
--- a/lib/libvcl/vcc_backend.c
+++ b/lib/libvcl/vcc_backend.c
@@ -688,6 +688,7 @@ static const struct dirlist {
        { "hash",               vcc_ParseRandomDirector },
        { "random",             vcc_ParseRandomDirector },
        { "client",             vcc_ParseRandomDirector },
+       { "least-connection",   vcc_ParseRandomDirector },
        { "round-robin",        vcc_ParseRoundRobinDirector },
        { "fallback",           vcc_ParseRoundRobinDirector },
        { "dns",                vcc_ParseDnsDirector },


_______________________________________________
varnish-dev mailing list
[email protected]
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev

Reply via email to