From: Grigorii Demidov Date: Mon, 19 Mar 2018 14:15:00 +0000 (+0100) Subject: lib/nsrep: tuning of 'serve_stale' module X-Git-Tag: v2.2.0~1^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69cb090842c63c0ae8a12df92a99eba8a7afca09;p=thirdparty%2Fknot-resolver.git lib/nsrep: tuning of 'serve_stale' module --- diff --git a/daemon/engine.c b/daemon/engine.c index 502bf046e..4eb39946e 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -650,9 +650,10 @@ static int init_state(struct engine *engine) } static enum lru_apply_do update_stat_item(const char *key, uint len, - unsigned *rtt, void *baton) + kr_nsrep_rtt_lru_entry_t *rtt_cache_entry, + void *baton) { - return *rtt > KR_NS_LONG ? LRU_APPLY_DO_EVICT : LRU_APPLY_DO_NOTHING; + return rtt_cache_entry->score > KR_NS_LONG ? LRU_APPLY_DO_EVICT : LRU_APPLY_DO_NOTHING; } /** @internal Walk RTT table, clearing all entries with bad score * to compensate for intermittent network issues or temporary bad behaviour. */ diff --git a/lib/generic/lru.c b/lib/generic/lru.c index 640fef0e7..c04f6f09f 100644 --- a/lib/generic/lru.c +++ b/lib/generic/lru.c @@ -132,11 +132,12 @@ static void group_inc_count(lru_group_t *g, int i) { /** @internal Implementation of both getting and insertion. * Note: val_len is only meaningful if do_insert. - * *res is only meaningful when return value isn't NULL, - * if return value is NULL, *res remains untouched. + * *is_new is only meaningful when return value isn't NULL, contains + * true when returned lru entry has been allocated right now + * if return value is NULL, *is_new remains untouched. */ KR_EXPORT void * lru_get_impl(struct lru *lru, const char *key, uint key_len, - uint val_len, bool do_insert, bool *res) + uint val_len, bool do_insert, bool *is_new) { bool ok = lru && (key || !key_len) && key_len <= UINT16_MAX && (!do_insert || val_len <= UINT16_MAX); @@ -212,8 +213,8 @@ insert: // insert into position i (incl. key) found: // key and hash OK on g->items[i]; now update stamps assert(i < LRU_ASSOC); group_inc_count(g, i); - if (res) { - *res = is_new_entry; + if (is_new) { + *is_new = is_new_entry; } return item_val(g->items[i]); } diff --git a/lib/generic/lru.h b/lib/generic/lru.h index 37b494625..397e9bb41 100644 --- a/lib/generic/lru.h +++ b/lib/generic/lru.h @@ -191,7 +191,7 @@ struct lru; void lru_free_items_impl(struct lru *lru); struct lru * lru_create_impl(uint max_slots, knot_mm_t *mm_array, knot_mm_t *mm); void * lru_get_impl(struct lru *lru, const char *key, uint key_len, - uint val_len, bool do_insert, bool *res); + uint val_len, bool do_insert, bool *is_new); void lru_apply_impl(struct lru *lru, lru_apply_fun f, void *baton); struct lru_item; diff --git a/lib/nsrep.c b/lib/nsrep.c index b14c0d467..9be90e46a 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -82,8 +82,8 @@ static void update_nsrep_set(struct kr_nsrep *ns, const knot_dname_t *name, uint #undef ADDR_SET -static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsigned score, - uint8_t *addr[], struct kr_qflags opts) +static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_rtt_lru_t *rtt_cache, + unsigned score, uint8_t *addr[], struct kr_qflags opts) { /* Name server is better candidate if it has address record. */ uint8_t *it = pack_head(*addr_set); @@ -101,8 +101,24 @@ static unsigned eval_addr_set(pack_t *addr_set, kr_nsrep_lru_t *rttcache, unsign } /* Get RTT for this address (if known) */ if (is_valid) { - unsigned *cached = rttcache ? lru_get_try(rttcache, val, len) : NULL; - unsigned addr_score = (cached) ? *cached : KR_NS_GLUED; + kr_nsrep_rtt_lru_entry_t *cached = rtt_cache ? + lru_get_try(rtt_cache, val, len) : + NULL; + unsigned addr_score = KR_NS_GLUED; + if (cached) { + uint64_t elapsed = kr_now() - cached->tout_timestamp; + elapsed = elapsed > UINT_MAX ? UINT_MAX : elapsed; + /* If NS once was marked as "timeouted", + * it won't participate in NS elections + * at least KR_NS_TIMEOUT_RETRY_INTERVAL milliseconds. */ + addr_score = cached->score; + if (cached->score >= KR_NS_TIMEOUT && + elapsed > KR_NS_TIMEOUT_RETRY_INTERVAL) { + addr_score = KR_NS_LONG - 1; + cached->score = addr_score; + cached->tout_timestamp = 0; + } + } if (addr_score < score + favour) { /* Shake down previous contenders */ for (size_t i = KR_NSREP_MAXADDR - 1; i > 0; --i) @@ -128,7 +144,7 @@ static int eval_nsrep(const char *k, void *v, void *baton) /* Fetch NS reputation */ if (ctx->cache_rep) { unsigned *cached = lru_get_try(ctx->cache_rep, k, - knot_dname_size((const uint8_t *)k)); + knot_dname_size((const uint8_t *)k)); if (cached) { reputation = *cached; } @@ -167,7 +183,7 @@ static int eval_nsrep(const char *k, void *v, void *baton) if (score >= KR_NS_TIMEOUT) { return kr_ok(); } else if (score <= ns->score && - (score < KR_NS_LONG || qry->flags.NO_THROTTLE)) { + (score < KR_NS_LONG || qry->flags.NO_THROTTLE)) { update_nsrep_set(ns, (const knot_dname_t *)k, addr_choice, score); ns->reputation = reputation; } else if ((kr_rand_uint(100) < 10) && @@ -221,11 +237,11 @@ int kr_nsrep_set(struct kr_query *qry, size_t index, const struct sockaddr *sock /* Retrieve RTT from cache */ struct kr_context *ctx = qry->ns.ctx; - unsigned *score = ctx + kr_nsrep_rtt_lru_entry_t *rtt_cache_entry = ctx ? lru_get_try(ctx->cache_rtt, kr_inaddr(sock), kr_family_len(sock->sa_family)) : NULL; - if (score) { - qry->ns.score = MIN(qry->ns.score, *score); + if (rtt_cache_entry) { + qry->ns.score = MIN(qry->ns.score, rtt_cache_entry->score); } return kr_ok(); @@ -278,7 +294,7 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx) #undef ELECT_INIT int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, - unsigned score, kr_nsrep_lru_t *cache, int umode) + unsigned score, kr_nsrep_rtt_lru_t *cache, int umode) { if (!cache || umode > KR_NS_MAX) { return kr_error(EINVAL); @@ -306,7 +322,8 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, assert(addr_in != NULL && addr_len > 0); bool is_new_entry = false; - unsigned *cur = lru_get_new(cache, addr_in, addr_len, (&is_new_entry)); + kr_nsrep_rtt_lru_entry_t *cur = lru_get_new(cache, addr_in, addr_len, + (&is_new_entry)); if (!cur) { return kr_ok(); } @@ -317,7 +334,7 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, if (is_new_entry) { if (umode == KR_NS_UPDATE_NORESET) { /* Zero initial value. */ - *cur = 0; + cur->score = 0; } else { /* Force KR_NS_RESET otherwise. */ umode = KR_NS_RESET; @@ -328,17 +345,21 @@ int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, switch (umode) { case KR_NS_UPDATE: case KR_NS_UPDATE_NORESET: - new_score = (*cur + score) / 2; break; + new_score = (cur->score + score) / 2; break; case KR_NS_RESET: new_score = score; break; - case KR_NS_ADD: new_score = MIN(KR_NS_MAX_SCORE - 1, *cur + score); break; - case KR_NS_MAX: new_score = MAX(*cur, score); break; + case KR_NS_ADD: new_score = MIN(KR_NS_MAX_SCORE - 1, cur->score + score); break; + case KR_NS_MAX: new_score = MAX(cur->score, score); break; default: break; } /* Score limits */ if (new_score > KR_NS_MAX_SCORE) { new_score = KR_NS_MAX_SCORE; } - *cur = new_score; + if (new_score >= KR_NS_TIMEOUT && cur->score < KR_NS_TIMEOUT) { + /* Set the timestamp only when NS became "timeouted" */ + cur->tout_timestamp = kr_now(); + } + cur->score = new_score; return kr_ok(); } @@ -373,9 +394,9 @@ int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src) return kr_ok(); } -int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache) +int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_rtt_lru_t *rtt_cache) { - if (!ns || !cache) { + if (!ns || !rtt_cache) { assert(false); return kr_error(EINVAL); } @@ -398,22 +419,24 @@ int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache) if (sa->sa_family == AF_UNSPEC) { break; } - unsigned *score = lru_get_try(cache, kr_inaddr(sa), - kr_family_len(sa->sa_family)); - if (!score) { + kr_nsrep_rtt_lru_entry_t *rtt_cache_entry = lru_get_try(rtt_cache, + kr_inaddr(sa), + kr_family_len(sa->sa_family)); + if (!rtt_cache_entry) { scores[i] = 1; /* prefer unknown to probe RTT */ - } else if ((kr_rand_uint(100) < 10) - && (kr_rand_uint(KR_NS_MAX_SCORE) >= *score)) { + } else if ((kr_rand_uint(100) < 10) && + (kr_rand_uint(KR_NS_MAX_SCORE) >= rtt_cache_entry->score)) { /* some probability to bump bad ones up for re-probe */ scores[i] = 1; } else { - scores[i] = *score; + scores[i] = rtt_cache_entry->score; } if (VERBOSE_STATUS) { char sa_str[INET6_ADDRSTRLEN]; inet_ntop(sa->sa_family, kr_inaddr(sa), sa_str, sizeof(sa_str)); kr_log_verbose("[ ][nsre] score %d for %s;\t cached RTT: %d\n", - scores[i], sa_str, score ? *score : -1); + scores[i], sa_str, + rtt_cache_entry ? rtt_cache_entry->score : -1); } } diff --git a/lib/nsrep.h b/lib/nsrep.h index 5c62936d1..36dc6e8d2 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -45,6 +45,10 @@ enum kr_ns_score { */ #define KR_NS_DEAD (((KR_NS_TIMEOUT * 4) + 3) / 3) +/* If once NS was marked as "timeouted", it won't participate in NS elections + * at least KR_NS_TIMEOUT_RETRY_INTERVAL milliseconds. */ +#define KR_NS_TIMEOUT_RETRY_INTERVAL 60000 + /** * NS QoS flags. */ @@ -69,8 +73,22 @@ enum kr_ns_update_mode { KR_NS_MAX /**< Set to maximum of current/proposed value. */ }; +struct kr_nsrep_rtt_lru_entry { + unsigned score; /* combined rtt */ + uint64_t tout_timestamp; /* The time when score became + * greater or equal then KR_NS_TIMEOUT. + * Is meaningful only when score >= KR_NS_TIMEOUT */ +}; + +typedef struct kr_nsrep_rtt_lru_entry kr_nsrep_rtt_lru_entry_t; + /** - * NS reputation/QoS tracking. + * NS QoS tracking. + */ +typedef lru_t(kr_nsrep_rtt_lru_entry_t) kr_nsrep_rtt_lru_t; + +/** + * NS reputation tracking. */ typedef lru_t(unsigned) kr_nsrep_lru_t; @@ -129,13 +147,13 @@ int kr_nsrep_elect_addr(struct kr_query *qry, struct kr_context *ctx); * @param score new score (i.e. RTT), see enum kr_ns_score * after two calls with score = KR_NS_DEAD and umode = KR_NS_UPDATE * server will be guaranteed to have KR_NS_TIMEOUTED score - * @param cache LRU cache + * @param cache RTT LRU cache * @param umode update mode (KR_NS_UPDATE or KR_NS_RESET or KR_NS_ADD) * @return 0 on success, error code on failure */ KR_EXPORT int kr_nsrep_update_rtt(struct kr_nsrep *ns, const struct sockaddr *addr, - unsigned score, kr_nsrep_lru_t *cache, int umode); + unsigned score, kr_nsrep_rtt_lru_t *cache, int umode); /** * Update NSSET reputation information. @@ -159,11 +177,11 @@ int kr_nsrep_copy_set(struct kr_nsrep *dst, const struct kr_nsrep *src); /** * Sort addresses in the query nsrep list * @param ns updated kr_nsrep - * @param cache RTT cache + * @param rtt_cache RTT LRU cache * @return 0 or an error code * @note ns reputation is zeroed, as KR_NS_NOIP{4,6} flags are useless - * in STUB/FORWARD mode. + * in STUB/FORWARD mode. */ KR_EXPORT -int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache); +int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_rtt_lru_t *rtt_cache); diff --git a/lib/resolve.h b/lib/resolve.h index f973ea0cf..5ff492730 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -162,7 +162,7 @@ struct kr_context map_t negative_anchors; struct kr_zonecut root_hints; struct kr_cache cache; - kr_nsrep_lru_t *cache_rtt; + kr_nsrep_rtt_lru_t *cache_rtt; kr_nsrep_lru_t *cache_rep; module_array_t *modules; /* The cookie context structure should not be held within the cookies diff --git a/modules/serve_stale/serve_stale.lua b/modules/serve_stale/serve_stale.lua index 2c3971a8d..dfe20a1ad 100644 --- a/modules/serve_stale/serve_stale.lua +++ b/modules/serve_stale/serve_stale.lua @@ -27,7 +27,7 @@ M.layer = { local now = ffi.C.kr_now() local deadline = qry.creation_time_mono + M.timeout if now > deadline or qry.flags.NO_NS_FOUND then - log('[ ][stal] => deadline has passed') + log('[ ][stal] => no reachable NS, using stale data') qry.stale_cb = M.callback -- TODO: probably start the same request that doesn't stale-serve, -- but first we need some detection of non-interactive / internal requests.