diff --git a/src/vmod_dynamic.c b/src/vmod_dynamic.c index 95e76c0..15b7d5b 100644 --- a/src/vmod_dynamic.c +++ b/src/vmod_dynamic.c @@ -251,6 +251,57 @@ dom_wait_active(struct dynamic_domain *dom) DBG(NULL, dom, "wait-active ret %d", ret); } +/* find a healthy dynamic_ref with the least connections */ +static struct dynamic_ref * +dom_find_leastconn(VRT_CTX, struct dynamic_domain *dom) +{ + struct dynamic_ref *next, *start, *best_next; + unsigned most_connections_available; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(dom, DYNAMIC_DOMAIN_MAGIC); + + dom_wait_active(dom); + + if (dom->status > DYNAMIC_ST_ACTIVE) + return (NULL); + + next = dom->current; + start = next; + best_next = next; + most_connections_available = 0; + + do { + CHECK_OBJ_ORNULL(next, DYNAMIC_REF_MAGIC); + if (next != NULL) + next = VTAILQ_NEXT(next, list); + if (next == NULL) + next = VTAILQ_FIRST(&dom->refs); + if (next == NULL) + break; + + if (next->dir != creating && next->dir != NULL && VRT_Healthy(ctx, next->dir, NULL)) { + if (VALID_OBJ((struct backend *)next->dir->priv, BACKEND_MAGIC)) { + struct backend *be; + unsigned connections_available; + + CAST_OBJ_NOTNULL(be, next->dir->priv, BACKEND_MAGIC); + connections_available = be->max_connections > 0 ? be->max_connections - be->n_conn : - be->n_conn; + if (connections_available > most_connections_available) { + best_next = next; + most_connections_available = connections_available; + } + } + } + } while (next != start); + + if (best_next != NULL) { + return best_next; + } else { + return NULL; + } +} + /* find a healthy dynamic_ref */ static struct dynamic_ref * dom_find(VRT_CTX, struct dynamic_domain *dom, struct dynamic_ref *start, @@ -330,7 +381,7 @@ static VCL_BACKEND v_matchproto_(vdi_resolve_f) dom_resolve(VRT_CTX, VCL_BACKEND d) { struct dynamic_domain *dom; - struct dynamic_ref *r; + struct dynamic_ref *r = NULL; VCL_BACKEND n = NULL; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); @@ -348,7 +399,12 @@ dom_resolve(VRT_CTX, VCL_BACKEND d) dynamic_gc_expired(dom->obj); Lck_Lock(&dom->mtx); - r = dom_find(ctx, dom, dom->current, NULL, NULL, 1); + if (dom->obj->algorithm == LEAST) { + r = dom_find_leastconn(ctx, dom); + } + if (r == NULL) { + r = dom_find(ctx, dom, dom->current, NULL, NULL, 1); + } dom->current = r; if (r != NULL) VRT_Assign_Backend(&n, r->dir); @@ -1306,6 +1362,18 @@ dynamic_ttl_parse(const char *s) INCOMPL(); } +static inline enum dynamic_algorithm_e +dynamic_algorithm_parse(const char *algorithm_s) +{ + switch (algorithm_s[0]) { + case 'R': return RR; break; + case 'L': return LEAST; break; + default: INCOMPL(); + } + INCOMPL(); + NEEDLESS(return(0)); +} + VCL_VOID v_matchproto_() vmod_director__init(VRT_CTX, @@ -1329,7 +1397,8 @@ vmod_director__init(VRT_CTX, VCL_DURATION retry_after, VCL_BACKEND via, VCL_INT keep, - VCL_STRING authority) + VCL_STRING authority, + VCL_ENUM algorithm_arg) { struct vmod_dynamic_director *obj; @@ -1402,6 +1471,7 @@ vmod_director__init(VRT_CTX, obj->max_connections = (unsigned)max_connections; obj->proxy_header = (unsigned)proxy_header; obj->ttl_from = dynamic_ttl_parse(ttl_from_arg); + obj->algorithm = dynamic_algorithm_parse(algorithm_arg); obj->keep = (unsigned)keep; if (resolver != NULL) { diff --git a/src/vmod_dynamic.h b/src/vmod_dynamic.h index d7d0679..9d63ef5 100644 --- a/src/vmod_dynamic.h +++ b/src/vmod_dynamic.h @@ -76,6 +76,11 @@ enum dynamic_ttl_e { TTL_E_MAX }; +enum dynamic_algorithm_e { + RR, + LEAST +}; + struct dynamic_domain { unsigned magic; #define DYNAMIC_DOMAIN_MAGIC 0x1bfe1345 @@ -203,6 +208,7 @@ struct vmod_dynamic_director { const struct res_cb *resolver; struct VPFX(dynamic_resolver) *resolver_inst; enum dynamic_ttl_e ttl_from; + enum dynamic_algorithm_e algorithm; unsigned debug; }; diff --git a/src/vmod_dynamic.vcc b/src/vmod_dynamic.vcc index 42cd2e5..989f5ed 100644 --- a/src/vmod_dynamic.vcc +++ b/src/vmod_dynamic.vcc @@ -250,7 +250,8 @@ $Object director( DURATION retry_after = 30, BACKEND via = NULL, INT keep = 3, - STRING authority = NULL) + STRING authority = NULL, + ENUM { RR, LEAST } algorithm = "RR") Description Create a DNS director. @@ -364,6 +365,16 @@ Parameters: a high positive value (``UINT_MAX``, usually 4 294 967 295, see :ref:`limits.h(7POSIX)`). + - *algorithm* (default: RR) + + Load balancing algorithm to use. + + ``RR`` cycles between health backends. + + ``LEAST`` chooses the healthy backend with the most number of connections available + (i.e. the difference between ``max_connections`` and the backends current connections). + The algorithm works in a similar way if ``max_connections`` isn't set. + Parameters to set attributes of backends See varnish documentation for details