From: Lukáš Ondráček Date: Wed, 25 Jun 2025 08:54:06 +0000 (+0200) Subject: lib/cache/top: divide price with size, other minor changes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9281ca3e25ebc760703f92cbb340e939fb542af;p=thirdparty%2Fknot-resolver.git lib/cache/top: divide price with size, other minor changes --- diff --git a/daemon/lua/kres-gen-33.lua b/daemon/lua/kres-gen-33.lua index 360effe19..21f5ecf03 100644 --- a/daemon/lua/kres-gen-33.lua +++ b/daemon/lua/kres-gen-33.lua @@ -270,7 +270,6 @@ struct kr_request { uint8_t rule_score_apply; uint8_t rule_score_log; struct kr_rplan rplan; - struct kr_cache_top_context cache_top_context; trace_log_f trace_log; trace_callback_f trace_finish; int vars_ref; @@ -287,6 +286,7 @@ struct kr_request { alloc_wire_f alloc_wire_cb; kr_rule_tags_t rule_tags; struct kr_extended_error extended_error; + struct kr_cache_top_context cache_top_context; }; enum kr_rank {KR_RANK_INITIAL, KR_RANK_OMIT, KR_RANK_TRY, KR_RANK_INDET = 4, KR_RANK_BOGUS, KR_RANK_MISMATCH, KR_RANK_MISSING, KR_RANK_INSECURE, KR_RANK_AUTH = 16, KR_RANK_SECURE = 32}; typedef struct kr_cdb * kr_cdb_pt; diff --git a/lib/cache/api.c b/lib/cache/api.c index 82891c6f9..62f190191 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -634,7 +634,7 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry, rdataset_dematerialize(rds_sigs, eh->data + rr_ssize); if (kr_fails_assert(entry_h_consistent_E(val_new_entry, rr->type))) return kr_error(EINVAL); - kr_cache_top_access(&cache->top, key.data, key.len, "stash_rrset"); + kr_cache_top_access(&cache->top, key.data, key.len, val_new_entry.len, "stash_rrset"); #if 0 /* Occasionally useful when debugging some kinds of changes. */ { @@ -811,7 +811,7 @@ static int stash_nsec_p(const knot_dname_t *dname, const char *nsec_p_v, VERBOSE_MSG(qry, "=> EL write failed (ret: %d)\n", ret); return kr_ok(); } - kr_cache_top_access(&cache->top, key.data, key.len, "stash_nsec_p"); + kr_cache_top_access(&cache->top, key.data, key.len, val.len, "stash_nsec_p"); if (log_refresh_by) { VERBOSE_MSG(qry, "=> nsec_p stashed for %s (refresh by %d, hash: %x)\n", log_dname, log_refresh_by, log_hash); @@ -874,7 +874,11 @@ static int peek_exact_real(struct kr_cache *cache, const knot_dname_t *name, uin knot_db_val_t key = key_exact_type(k, type); knot_db_val_t val = { NULL, 0 }; ret = cache_op(cache, read, &key, &val, 1); - if (!ret) ret = entry_h_seek(&val, type); + size_t whole_val_len = 0; + if (!ret) { + whole_val_len = val.len; // whole size before seeking + ret = entry_h_seek(&val, type); + } if (ret) return kr_error(ret); const struct entry_h *eh = entry_h_consistent_E(val, type); @@ -889,7 +893,7 @@ static int peek_exact_real(struct kr_cache *cache, const knot_dname_t *name, uin .raw_data = val.data, .raw_bound = knot_db_val_bound(val), }; - kr_cache_top_access(&cache->top, key.data, key.len, "peek_exact_real"); // hits only + kr_cache_top_access(&cache->top, key.data, key.len, whole_val_len, "peek_exact_real"); // hits only return kr_ok(); } int kr_cache_peek_exact(struct kr_cache *cache, const knot_dname_t *name, uint16_t type, diff --git a/lib/cache/entry_pkt.c b/lib/cache/entry_pkt.c index 36cdb76e7..b853cad0f 100644 --- a/lib/cache/entry_pkt.c +++ b/lib/cache/entry_pkt.c @@ -114,7 +114,7 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry, eh->has_optout = qf->DNSSEC_OPTOUT; memcpy(eh->data, &pkt_size, sizeof(pkt_size)); memcpy(eh->data + sizeof(pkt_size), pkt->wire, pkt_size); - kr_cache_top_access(&cache->top, key.data, key.len, "stash_pkt"); + kr_cache_top_access(&cache->top, key.data, key.len, val_new_entry.len, "stash_pkt"); WITH_VERBOSE(qry) { auto_free char *type_str = kr_rrtype_text(pkt_type), diff --git a/lib/cache/nsec1.c b/lib/cache/nsec1.c index 228c8155e..4865a47f1 100644 --- a/lib/cache/nsec1.c +++ b/lib/cache/nsec1.c @@ -250,7 +250,7 @@ static const char * find_leq_NSEC1(struct kr_cache *cache, const struct kr_query success: - kr_cache_top_access(&cache->top, key_nsec.data, key_nsec.len, "leq_nsec1"); // hits only + kr_cache_top_access(&cache->top, key_nsec.data, key_nsec.len, val.len, "leq_nsec1"); // hits only return NULL; } @@ -409,7 +409,6 @@ int nsec1_src_synth(struct key *k, struct answer *ans, const knot_dname_t *clenc knot_db_val_t val = { NULL, 0 }; knot_db_val_t wild_low_kwz = { NULL, 0 }; uint32_t new_ttl; - kr_cache_top_access(&cache->top, key.data, key.len, "nsec1_src_synth"); // TODO remove, probably redundant, hit (exact/cover) or miss const char *err = find_leq_NSEC1(cache, qry, key, k, &val, &exact_match, &wild_low_kwz, NULL, &new_ttl); if (err) { diff --git a/lib/cache/nsec3.c b/lib/cache/nsec3.c index 7879483a2..a9a439596 100644 --- a/lib/cache/nsec3.c +++ b/lib/cache/nsec3.c @@ -219,7 +219,7 @@ static const char * find_leq_NSEC3(struct kr_cache *cache, const struct kr_query success: - kr_cache_top_access(&cache->top, key_found.data, key_found.len, "leq_nsec3"); // hits only + kr_cache_top_access(&cache->top, key_found.data, key_found.len, val.len, "leq_nsec3"); // hits only return NULL; } diff --git a/lib/cache/peek.c b/lib/cache/peek.c index c75ec1d74..5bf4b41cf 100644 --- a/lib/cache/peek.c +++ b/lib/cache/peek.c @@ -128,7 +128,7 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt) ret = found_exact_hit(qry, pkt, val, lowest_rank); } if (!ret) { - kr_cache_top_access(&cache->top, key.data, key.len, "peek_nosync:exact"); // hits only + kr_cache_top_access(&cache->top, key.data, key.len, val.len, "peek_nosync:exact"); // hits only return KR_STATE_DONE; } else if (kr_fails_assert(ret == kr_error(ENOENT))) { VERBOSE_MSG(qry, "=> exact hit error: %d %s\n", ret, kr_strerror(ret)); @@ -276,7 +276,7 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt) ret = entry2answer(&ans, AR_SOA, eh, knot_db_val_bound(val), k->zname, KNOT_RRTYPE_SOA, new_ttl); if (ret) return ctx->state; - kr_cache_top_access(&cache->top, key.data, key.len, "peek_nosync:SOA"); // hits only + kr_cache_top_access(&cache->top, key.data, key.len, val.len, "peek_nosync:SOA"); // hits only } /* Find our target RCODE. */ @@ -560,7 +560,9 @@ static int try_wild(struct key *k, struct answer *ans, const knot_dname_t *clenc /* Find the record. */ knot_db_val_t val = { NULL, 0 }; int ret = cache_op(cache, read, &key, &val, 1); + size_t whole_val_len = 0; if (!ret) { + whole_val_len = val.len; // original size, before seeking ret = entry_h_seek(&val, type); } if (ret) { @@ -595,7 +597,7 @@ static int try_wild(struct key *k, struct answer *ans, const knot_dname_t *clenc ret, (int)new_ttl); if (ret) return kr_error(ret); ans->rcode = PKT_NOERROR; - kr_cache_top_access(&cache->top, key.data, key.len, "try_wild"); // hits only + kr_cache_top_access(&cache->top, key.data, key.len, whole_val_len, "try_wild"); // hits only return kr_ok(); } @@ -726,7 +728,7 @@ static int closest_NS(struct kr_cache *cache, struct key *k, entry_list_t el, success: k->zlf_len = zlf_len; - kr_cache_top_access(&cache->top, key.data, key.len, "closest_NS"); // hits only + kr_cache_top_access(&cache->top, key.data, key.len, val.len, "closest_NS"); // hits only return kr_ok(); } diff --git a/lib/cache/top.c b/lib/cache/top.c index 0620af8af..081c4e5a5 100644 --- a/lib/cache/top.c +++ b/lib/cache/top.c @@ -7,6 +7,7 @@ #include "lib/utils.h" #include "lib/defines.h" #include "lib/cache/top.h" +#include "lib/cache/impl.h" #include "lib/mmapped.h" #include "lib/kru.h" @@ -15,25 +16,22 @@ #define FILE_FORMAT_VERSION 1 // fail if different -#define TICK_MSEC 1000 -#define BASE_PRICE (((kru_price_t)1) << (KRU_PRICE_BITS - 16)) // increment by ones (16-bit) - // -> instant limit: ~2^16 // TODO -> 2^14 ? +#define TICK_SEC 1 +#define NORMAL_SIZE 150 // B; normal size of cache entry +#define BASE_PRICE (((kru_price_t)5) << (KRU_PRICE_BITS - 16)) + // for cache entries of NORMAL_SIZE + // -> normal increment: 5 (16-bit) + // -> instant limit: ~(2^16 / 5) #define MAX_DECAY (BASE_PRICE / 2) // per sec // -> rate limit: 1/2 per sec (more frequent accesses are incomparable) - // -> half-life: ~ 25h 14min // TODO -> 5h ? - -struct top_data { - uint32_t version; - uint32_t base_price; - uint32_t max_decay; - _Alignas(64) uint8_t kru[]; -}; + // -> half-life: ~5h 3min static inline uint32_t ticks_now(void) { - // TODO use clock_gettime directly or maintain time offset - return kr_now() / TICK_MSEC; // not working over reboots + struct timespec now_ts = {0}; + clock_gettime(CLOCK_REALTIME_COARSE, &now_ts); + return now_ts.tv_sec / TICK_SEC; } static inline bool first_access_ro(struct kr_cache_top_context *ctx, kru_hash_t hash) { @@ -101,20 +99,20 @@ int kr_cache_top_init(struct kr_cache_top *top, char *mmap_file, size_t cache_si } // else use existing file settings struct top_data header = { - .version = (FILE_FORMAT_VERSION << 1) | kru_using_avx2(), - .base_price = BASE_PRICE, - .max_decay = MAX_DECAY + .version = (FILE_FORMAT_VERSION << 1) | kru_using_avx2(), + .base_price_norm = BASE_PRICE * NORMAL_SIZE, + .max_decay = MAX_DECAY }; size_t header_size = offsetof(struct top_data, max_decay) + sizeof(header.max_decay); static_assert( // no padding up to .max_decay offsetof(struct top_data, max_decay) == sizeof(header.version) + - sizeof(header.base_price), + sizeof(header.base_price_norm), "detected padding with undefined data inside mmapped header"); if (cache_size == 0) { - header_size = offsetof(struct top_data, base_price); + header_size = offsetof(struct top_data, base_price_norm); } VERBOSE_LOG("INIT mmapped_init\n"); @@ -173,8 +171,8 @@ void kr_cache_top_deinit(struct kr_cache_top *top) { * hex bytes: * decimal bytes: <0.1.2.3> */ -static char *str_key(void *key, size_t len) { - static char str[401]; +char *kr_cache_top_strkey(void *key, size_t len) { + static char str[4 * KR_CACHE_KEY_MAXLEN + 1]; if (4 * len + 1 > sizeof(str)) len = (sizeof(str) - 1) / 4; unsigned char *k = key; @@ -239,21 +237,25 @@ static char *str_key(void *key, size_t len) { return str; } -void kr_cache_top_access(struct kr_cache_top *top, void *key, size_t len, char *debug_label) +void kr_cache_top_access(struct kr_cache_top *top, void *key, size_t key_len, size_t data_size, char *debug_label) { - kru_hash_t hash = KRU.hash_bytes((struct kru *)&top->data->kru, (uint8_t *)key, len); + kru_hash_t hash = KRU.hash_bytes((struct kru *)&top->data->kru, (uint8_t *)key, key_len); const bool unique = top->ctx ? first_access(top->ctx, hash) : true; + const size_t size = kr_cache_top_entry_size(key_len, data_size); if (unique) { - KRU.load_hash((struct kru *)&top->data->kru, ticks_now(), hash, top->data->base_price); + const kru_price_t price = kr_cache_top_entry_price(top, size); + KRU.load_hash((struct kru *)&top->data->kru, ticks_now(), hash, price); } - VERBOSE_LOG("ACCESS %-19s %s%s%s\n", debug_label, str_key(key, len), unique ? "" : " (SKIP)", top->ctx ? "" : "(NO_CONTEXT, PASS)"); + VERBOSE_LOG("ACCESS %-19s%4d B %-5s %s\n", debug_label, size, + !top->ctx ? "NO_CTX" : unique ? "" : "SKIP", + kr_cache_top_strkey(key, key_len)); } // temporal logging one level under _access void kr_cache_top_access_cdb(struct kr_cache_top *top, void *key, size_t len, char *debug_label) { - // VERBOSE_LOG("ACCESS %-17s %s\n", debug_label, str_key(key, len)); + // VERBOSE_LOG("ACCESS %-17s %s\n", debug_label, kr_cache_top_strkey(key, len)); } struct kr_cache_top_context *kr_cache_top_context_switch(struct kr_cache_top *top, @@ -268,6 +270,6 @@ uint16_t kr_cache_top_load(struct kr_cache_top *top, void *key, size_t len) { kru_hash_t hash = KRU.hash_bytes((struct kru *)&top->data->kru, (uint8_t *)key, len); uint16_t load = KRU.load_hash((struct kru *)&top->data->kru, ticks_now(), hash, 0); - VERBOSE_LOG("LOAD %s -> %d\n", str_key(key, len), load); + // VERBOSE_LOG("LOAD %s -> %d\n", kr_cache_top_strkey(key, len), load); return load; } diff --git a/lib/cache/top.h b/lib/cache/top.h index 024920110..c0b2b7bd2 100644 --- a/lib/cache/top.h +++ b/lib/cache/top.h @@ -4,6 +4,7 @@ #pragma once #include "lib/mmapped.h" +#include "lib/kru.h" struct kr_cache_top { struct mmapped mmapped; @@ -12,10 +13,23 @@ struct kr_cache_top { }; struct kr_cache_top_context { - uint32_t bloom[16]; // TODO require alignment + uint32_t bloom[16]; // size of just one cache-line, but probably not aligned (neither kr_request is) uint32_t cnt; // TODO remove this (and propagate to kres-gen) }; +struct top_data { + uint32_t version; + uint32_t base_price_norm; + uint32_t max_decay; + _Alignas(64) uint8_t kru[]; +}; + +static inline size_t kr_cache_top_entry_size(size_t key_len, size_t data_size) { + return key_len + data_size; // TODO increase by a constant as DB overhead? +} +static inline kru_price_t kr_cache_top_entry_price(struct kr_cache_top *top, size_t size) { + return top->data->base_price_norm / size; +} KR_EXPORT int kr_cache_top_init(struct kr_cache_top *top, char *mmap_file, size_t cache_size); @@ -27,7 +41,7 @@ KR_EXPORT void kr_cache_top_access_cdb(struct kr_cache_top *top, void *key, size_t len, char *debug_label); // temporal, TODO remove KR_EXPORT -void kr_cache_top_access(struct kr_cache_top *top, void *key, size_t len, char *debug_label); +void kr_cache_top_access(struct kr_cache_top *top, void *key, size_t key_len, size_t data_size, char *debug_label); KR_EXPORT uint16_t kr_cache_top_load(struct kr_cache_top *top, void *key, size_t len); @@ -35,3 +49,6 @@ uint16_t kr_cache_top_load(struct kr_cache_top *top, void *key, size_t len); // ctx has to be kept valid until next call KR_EXPORT struct kr_cache_top_context *kr_cache_top_context_switch(struct kr_cache_top *top, struct kr_cache_top_context *ctx, char *debug_label); + +KR_EXPORT +char *kr_cache_top_strkey(void *key, size_t len); diff --git a/lib/resolve.h b/lib/resolve.h index 1cafbdb7c..cbb3a703a 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -282,7 +282,6 @@ struct kr_request { uint8_t rule_score_log; struct kr_rplan rplan; - struct kr_cache_top_context cache_top_context; // divided into two cache lines, TODO change placement (+update kres-gen) trace_log_f trace_log; /**< Logging tracepoint */ trace_callback_f trace_finish; /**< Request finish tracepoint */ int vars_ref; /**< Reference to per-request variable table. LUA_NOREF if not set. */ @@ -299,6 +298,7 @@ struct kr_request { alloc_wire_f alloc_wire_cb; /**< CB to allocate answer wire (can be NULL). */ kr_rule_tags_t rule_tags; /**< TagSet applying to this request. */ struct kr_extended_error extended_error; /**< EDE info; don't modify directly, use kr_request_set_extended_error() */ + struct kr_cache_top_context cache_top_context; }; /** Initializer for an array of *_selected. */ diff --git a/lib/selection.c b/lib/selection.c index c17209ea8..558e58891 100644 --- a/lib/selection.c +++ b/lib/selection.c @@ -157,7 +157,7 @@ struct rtt_state get_rtt_state(const uint8_t *ip, size_t len, state = default_rtt_state; } else { // memcpy is safe for unaligned case (on non-x86) memcpy(&state, value.data, sizeof(state)); - kr_cache_top_access(&cache->top, key.data, key.len, "get_rtt"); + kr_cache_top_access(&cache->top, key.data, key.len, value.len, "get_rtt"); } free(key.data); @@ -176,7 +176,7 @@ int put_rtt_state(const uint8_t *ip, size_t len, struct rtt_state state, int ret = cache->api->write(db, stats, &key, &value, 1); kr_cache_commit(cache); - kr_cache_top_access(&cache->top, key.data, key.len, "put_rtt"); + kr_cache_top_access(&cache->top, key.data, key.len, value.len, "put_rtt"); free(key.data); return ret; diff --git a/utils/cache_gc/categories.c b/utils/cache_gc/categories.c index e6f11b55d..96b58702a 100644 --- a/utils/cache_gc/categories.c +++ b/utils/cache_gc/categories.c @@ -47,20 +47,23 @@ category_t kr_gc_categorize(struct kr_cache_top *top, gc_record_info_t * info, v uint16_t load = kr_cache_top_load(top, key, key_len); res = load2cat(load); // 0..64 - // TODO check/reconsider penalties if (info->rrtype == KNOT_CACHE_RTT) { - // TODO same priority, or prioritize this + // TODO some correction here? } else { - if (info->entry_size > 300) { - // penalty for big answers - res += 4; // ~1 half-life - } if (info->expires_in <= 0) { - // penalty for expired - res += 28; // ~7 half-lifes + // evict all expired before any non-expired + res = res / 2 + 65; // 65..94 } } - static_assert(CATEGORIES - 1 > 64 + 4 + 28); + static_assert(CATEGORIES - 1 > 94); + + const kru_price_t price = kr_cache_top_entry_price(top, info->entry_size); + const double accesses = (double)((kru_price_t)load << (KRU_PRICE_BITS - 16)) / price; + printf("cat %02d %6d l %8.1f acc %6ld B %8ld s %s\n", + res, load, accesses, info->entry_size, info->expires_in, + kr_cache_top_strkey(key, key_len) + ); + return res; } diff --git a/utils/cache_gc/db.c b/utils/cache_gc/db.c index 805891e0b..40c2340c8 100644 --- a/utils/cache_gc/db.c +++ b/utils/cache_gc/db.c @@ -170,7 +170,6 @@ int kr_gc_cache_iter(knot_db_t * knot_db, const kr_cache_gc_cfg_t *cfg, knot_db_txn_t txn = { 0 }; knot_db_iter_t *it = NULL; const knot_db_api_t *api = knot_db_lmdb_api(); - gc_record_info_t info = { 0 }; int64_t now = time(NULL); int ret = api->txn_begin(knot_db, &txn, KNOT_DB_RDONLY); @@ -204,7 +203,8 @@ int kr_gc_cache_iter(knot_db_t * knot_db, const kr_cache_gc_cfg_t *cfg, goto error; } - info.entry_size = key.len + val.len; + gc_record_info_t info = { 0 }; + info.entry_size = kr_cache_top_entry_size(key.len, val.len); info.valid = false; const int entry_type = kr_gc_key_consistent(key); const struct entry_h *entry = NULL; @@ -230,7 +230,7 @@ int kr_gc_cache_iter(knot_db_t * knot_db, const kr_cache_gc_cfg_t *cfg, counter_iter++; counter_kr_consistent += info.valid; if (VERBOSE_STATUS) { - if (!entry_type || !entry) { // don't log fully consistent entries + if (!entry_type || ((entry_type != KNOT_CACHE_RTT) && !entry)) { // don't log fully consistent entries printf ("GC %sconsistent, KR %sconsistent, size %zu, key len %zu: ", entry_type ? "" : "in", entry ? "" : "IN", diff --git a/utils/cache_gc/kr_cache_gc.c b/utils/cache_gc/kr_cache_gc.c index c610da42b..46914a179 100644 --- a/utils/cache_gc/kr_cache_gc.c +++ b/utils/cache_gc/kr_cache_gc.c @@ -165,7 +165,7 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) if (cfg->dry_run || large_usage || VERBOSE_STATUS) { // don't print this on every size check printf("Usage: %.2lf%%\n", db_usage); } - if (cfg->dry_run || !large_usage) { + if (!large_usage) { return KNOT_EOK; } @@ -216,6 +216,10 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) printf("Cache analyzed in %.0lf msecs, %zu records, limit category is %d.\n", kr_timer_elapsed(&timer_analyze) * 1000, cats.records, limit_category); + if (cfg->dry_run) { + return KNOT_EOK; + } + //// 3. pass whole cache again to collect a list of keys that should be deleted. kr_timer_start(&timer_choose); ctx_delete_categories_t to_del = {