diff --git a/CHANGES.rst b/CHANGES.rst index 4b09c45..7c2dd11 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,10 @@ version 2.8.0. NEXT (no release branch) ------------------------ +* Added the new ``prefer`` parameter to prioritize backends during selection based on an ACL (`132`_) + +.. _132: https://github.com/nigoroll/libvmod-dynamic/issues/132 + 7.7 branch ---------- diff --git a/src/tbl/list_prop.h b/src/tbl/list_prop.h index 88bb70b..ba9fdb9 100644 --- a/src/tbl/list_prop.h +++ b/src/tbl/list_prop.h @@ -7,6 +7,7 @@ DIRPROP("hosthdr", "\"%s\"",obj->hosthdr ? obj->hosthdr : "") DIRPROP("share", "\"%s\"",share_s[obj->share]) DIRPROP("probe", "%s", obj->probe ? "true" : "false") DIRPROP("whitelist", "%s", obj->whitelist ? "true" : "false") +DIRPROP("prefer", "%s", obj->prefer ? "true" : "false") DIRPROP("connect_timeout", "%.2f", obj->connect_tmo) DIRPROP("first_byte_timeout", "%.2f", obj->first_byte_tmo) DIRPROP("between_bytes_timeout","%.2f", obj->between_bytes_tmo) diff --git a/src/vmod_dynamic.c b/src/vmod_dynamic.c index 8a0271c..8f5dbe2 100644 --- a/src/vmod_dynamic.c +++ b/src/vmod_dynamic.c @@ -252,68 +252,70 @@ dom_wait_active(struct dynamic_domain *dom) } /* find a healthy dynamic_ref */ + static struct dynamic_ref * -dom_find(VRT_CTX, struct dynamic_domain *dom, struct dynamic_ref *start, - VCL_BOOL *healthy, VCL_TIME *changed, unsigned wait) -{ - struct dynamic_ref *next, *alt; +dom_find_v2(VRT_CTX, struct dynamic_domain *dom, VCL_BOOL *healthy, VCL_TIME *changed, unsigned wait) { + struct dynamic_ref *r, *r_alt; VCL_TIME c, cc; VCL_BOOL h; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(dom, DYNAMIC_DOMAIN_MAGIC); - CHECK_OBJ_ORNULL(start, DYNAMIC_REF_MAGIC); dom_wait_active(dom); if (dom->status > DYNAMIC_ST_ACTIVE) return (NULL); - if (start == NULL) - start = VTAILQ_FIRST(&dom->refs); - - h = 0; - cc = dom->changed_cached; - next = start; - alt = NULL; - - //lint -e{506} Constant value boolean - 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) { - h = VRT_Healthy(ctx, next->dir, &c); - if (c > cc) - cc = c; - if (h) - break; - } - /* if we do not find a healthy backend, use one with a director - * or, alternatively, whatever we can get - */ - if (alt == NULL || - (alt->dir == creating && next->dir != creating)) - alt = next; - if (next != start) - continue; - - // we have iterated the list once - - if (alt->dir != creating) { - next = alt; - break; - } - if (wait == 0) - break; - - assert(alt->dir == creating); + if (VTAILQ_EMPTY(&dom->refs)) + return (NULL); + + r = dom->current == NULL ? VTAILQ_FIRST(&dom->refs) : VTAILQ_NEXT(dom->current, list); + r_alt = NULL; + + cc = dom->changed_cached; + h = 0; + + // 1st pass: find a healthy backend + VTAILQ_FOREACH_FROM(r, &dom->refs, list) { + if (r->dir != NULL && r->dir != creating) { + h = VRT_Healthy(ctx, r->dir, &c); + if(h) { + dom->current = r; + + if (c > cc) + cc = c; + + LOG(ctx, SLT_Error, dom, "Selecting healthy backend %s", dom->current->dir->vcl_name); + break; + } + } + } + + // 2nd pass: find a healthy backend AND a preferred one + if (r == NULL) { + goto done; + } + + VTAILQ_FOREACH_FROM(r_alt, &dom->refs, list) { + if (r_alt == r) { + continue; + } + + if (r_alt->dir != NULL && r_alt->dir != creating) { + h = VRT_Healthy(ctx, r_alt->dir, NULL); + if(h && r_alt->preferred > r->preferred) { + dom->current = r_alt; + LOG(ctx, SLT_Error, dom, "Selecting preferred backend %s", dom->current->dir->vcl_name); + break; + } + } + } + +done: + if (wait && dom->current->dir == creating) { AZ(Lck_CondWait(&dom->resolve, &dom->mtx)); - } while (1); + } dom->healthy_cached = h; dom->changed_cached = cc; @@ -323,14 +325,14 @@ dom_find(VRT_CTX, struct dynamic_domain *dom, struct dynamic_ref *start, if (changed) *changed = cc; - return (next); + return dom->current; } 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,8 +350,7 @@ 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); - dom->current = r; + r = dom_find_v2(ctx, dom, NULL, NULL, 1); if (r != NULL) VRT_Assign_Backend(&n, r->dir); Lck_Unlock(&dom->mtx); @@ -388,7 +389,7 @@ dom_healthy(VRT_CTX, VCL_BACKEND d, VCL_TIME *changed) return (dom->healthy_cached); } - (void) dom_find(ctx, dom, NULL, &retval, changed, IS_CLI() ? 0 : 1); + (void) dom_find_v2(ctx, dom, &retval, changed, IS_CLI() ? 0 : 1); Lck_Unlock(&dom->mtx); return (retval); @@ -443,7 +444,9 @@ dom_list(VRT_CTX, VCL_BACKEND dir, struct vsb *vsb, int pflag, int jflag) VSB_indent(vsb, 2); VSB_printf(vsb, "\"health\": \"%s\"\n", h ? "healthy" : "sick"); - VSB_indent(vsb, -2); + VSB_printf(vsb, "\"preferred\": %d\n", + r->preferred); + VSB_indent(vsb, -2); VSB_cat(vsb, "}"); } else if (pflag) { @@ -775,6 +778,9 @@ dom_update(struct dynamic_domain *dom, const struct res_cb *res, r->sa = VSA_Clone(info->sa); AZ(r->dir); r->dir = creating; + if (r->dom->obj->prefer != NULL) { + r->preferred = VRT_acl_match(ctx, r->dom->obj->prefer, info->sa); + } VTAILQ_INSERT_TAIL(&dom->refs, r, list); } @@ -1318,6 +1324,7 @@ vmod_director__init(VRT_CTX, VCL_ENUM share_arg, VCL_PROBE probe, VCL_ACL whitelist, + VCL_ACL prefer, VCL_DURATION ttl, VCL_DURATION connect_timeout, VCL_DURATION first_byte_timeout, @@ -1396,6 +1403,7 @@ vmod_director__init(VRT_CTX, obj->share = dynamic_share_parse(share_arg); obj->probe = probe; obj->whitelist = whitelist; + obj->prefer = prefer; obj->ttl = ttl; obj->retry_after = retry_after; obj->connect_tmo = connect_timeout; diff --git a/src/vmod_dynamic.h b/src/vmod_dynamic.h index bce0729..995314e 100644 --- a/src/vmod_dynamic.h +++ b/src/vmod_dynamic.h @@ -47,6 +47,7 @@ struct dynamic_ref { unsigned magic; #define DYNAMIC_REF_MAGIC 0x79a19d81 unsigned keep; + unsigned preferred; VTAILQ_ENTRY(dynamic_ref) list; struct dynamic_domain *dom; VCL_BACKEND dir; @@ -174,6 +175,7 @@ struct vmod_dynamic_director { enum dynamic_share_e share; VCL_PROBE probe; VCL_ACL whitelist; + VCL_ACL prefer; VCL_DURATION ttl; VCL_DURATION retry_after; VCL_DURATION connect_tmo; diff --git a/src/vmod_dynamic.man.rst b/src/vmod_dynamic.man.rst index 404eae4..0555347 100644 --- a/src/vmod_dynamic.man.rst +++ b/src/vmod_dynamic.man.rst @@ -23,7 +23,7 @@ SYNOPSIS import dynamic [as name] [from "path"] - new xdirector = dynamic.director(STRING port, STRING host_header, ENUM share, PROBE probe, ACL whitelist, DURATION ttl, DURATION connect_timeout, DURATION first_byte_timeout, DURATION between_bytes_timeout, DURATION domain_usage_timeout, DURATION first_lookup_timeout, INT max_connections, INT proxy_header, BLOB resolver, ENUM ttl_from, DURATION retry_after, BACKEND via, INT keep, STRING authority, DURATION wait_timeout, INT wait_limit) + new xdirector = dynamic.director(STRING port, STRING host_header, ENUM share, PROBE probe, ACL whitelist, ACL prefer, DURATION ttl, DURATION connect_timeout, DURATION first_byte_timeout, DURATION between_bytes_timeout, DURATION domain_usage_timeout, DURATION first_lookup_timeout, INT max_connections, INT proxy_header, BLOB resolver, ENUM ttl_from, DURATION retry_after, BACKEND via, INT keep, STRING authority, DURATION wait_timeout, INT wait_limit) BACKEND xdirector.backend(STRING host, STRING port, STRING authority) @@ -260,8 +260,8 @@ logged with the following event:: .. _dynamic.director(): -new xdirector = dynamic.director(STRING port, STRING host_header, ENUM share, PROBE probe, ACL whitelist, DURATION ttl, DURATION connect_timeout, DURATION first_byte_timeout, DURATION between_bytes_timeout, DURATION domain_usage_timeout, DURATION first_lookup_timeout, INT max_connections, INT proxy_header, BLOB resolver, ENUM ttl_from, DURATION retry_after, BACKEND via, INT keep, STRING authority, DURATION wait_timeout, INT wait_limit) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +new xdirector = dynamic.director(STRING port, STRING host_header, ENUM share, PROBE probe, ACL whitelist, ACL prefer, DURATION ttl, DURATION connect_timeout, DURATION first_byte_timeout, DURATION between_bytes_timeout, DURATION domain_usage_timeout, DURATION first_lookup_timeout, INT max_connections, INT proxy_header, BLOB resolver, ENUM ttl_from, DURATION retry_after, BACKEND via, INT keep, STRING authority, DURATION wait_timeout, INT wait_limit) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- :: @@ -271,6 +271,7 @@ new xdirector = dynamic.director(STRING port, STRING host_header, ENUM share, PR ENUM {DEFAULT, DIRECTOR, HOST} share=DEFAULT, PROBE probe=0, ACL whitelist=0, + ACL prefer=0, DURATION ttl=3600, DURATION connect_timeout=-1, DURATION first_byte_timeout=-1, @@ -317,6 +318,10 @@ Parameters: Only name resolution results matching the acl will be used. + - *prefer* - an acl (defaults to none) + + Name resolution results matching the acl will be prefered. + - *ttl* - interval between lookups (defaults to one hour) Minimum configured backend lifetime before address resultion diff --git a/src/vmod_dynamic.vcc b/src/vmod_dynamic.vcc index 873e886..eee2460 100644 --- a/src/vmod_dynamic.vcc +++ b/src/vmod_dynamic.vcc @@ -237,6 +237,7 @@ $Object director( ENUM { DEFAULT, DIRECTOR, HOST } share = "DEFAULT", PROBE probe = 0, ACL whitelist = 0, + ACL prefer = 0, DURATION ttl = 3600, DURATION connect_timeout = -1, DURATION first_byte_timeout = -1, @@ -282,6 +283,10 @@ Parameters: Only name resolution results matching the acl will be used. + - *prefer* - an acl (defaults to none) + + Name resolution results matching the acl will be prefered. + - *ttl* - interval between lookups (defaults to one hour) Minimum configured backend lifetime before address resultion