#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);
}
/* 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)
/* 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;
}
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) &&
/* 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();
#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);
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();
}
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;
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();
}
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);
}
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);
}
}
*/
#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.
*/
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;
* @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.
/**
* 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);