]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/cache/top: divide price with size, other minor changes
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 25 Jun 2025 08:54:06 +0000 (10:54 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 2 Jul 2025 16:55:56 +0000 (18:55 +0200)
13 files changed:
daemon/lua/kres-gen-33.lua
lib/cache/api.c
lib/cache/entry_pkt.c
lib/cache/nsec1.c
lib/cache/nsec3.c
lib/cache/peek.c
lib/cache/top.c
lib/cache/top.h
lib/resolve.h
lib/selection.c
utils/cache_gc/categories.c
utils/cache_gc/db.c
utils/cache_gc/kr_cache_gc.c

index 360effe194d453ca58169c7c865cd040344a1d05..21f5ecf034ac544504cd3b223166d7b9c77ac97c 100644 (file)
@@ -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;
index 82891c6f962a06d72455afa7f1f2e8394ff897cc..62f1901911c5659b5639f0dbc29ca1819dcc3d6a 100644 (file)
@@ -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,
index 36cdb76e7f17be6bccc6f94e266183c4b3ea87bf..b853cad0f019451324707fb2faa2ec5007b7c0a6 100644 (file)
@@ -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),
index 228c8155e0a0902addf295f3a743c66a7edc4364..4865a47f189c7b5ee09767d11de95c10310bf689 100644 (file)
@@ -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) {
index 7879483a25b2c89f2f6041fafeee7c8e30b5851c..a9a4395962a90859f3373d5e8ad61740ffad508d 100644 (file)
@@ -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;
 }
 
index c75ec1d74daf79480f49b38d3885389eab32bca9..5bf4b41cf00870aab42a61a10afbe8576b35ce40 100644 (file)
@@ -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();
 }
 
index 0620af8af369abd295cb1b75b936a9abdeae9c13..081c4e5a5d47bb860833f0820fce9a4625a936f0 100644 (file)
@@ -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"
 
 
 #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: <x00010203x>
  * 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;
 }
index 024920110dcc55e933918dcba8c357f3072e5d25..c0b2b7bd2e584bc0dc018e6756eb276a6326292f 100644 (file)
@@ -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);
index 1cafbdb7cbed66de1beb16bb4f9d69250326fb82..cbb3a703aa7780f63530f65d5c81b446354faf1e 100644 (file)
@@ -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. */
index c17209ea83330b8b3de8fc2fe627ffba6e1e36cb..558e58891963561f74bb0e5447718a5cac4a4aff 100644 (file)
@@ -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;
index e6f11b55d025f16d1d092657671bbab3fe7344ca..96b58702a7781d9456340b7b77521e26fe208ee3 100644 (file)
@@ -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;
 }
index 805891e0bcb64ee04712361324b09df2f0b18445..40c2340c8de0239223637cfd7f19d3071a3e0bab 100644 (file)
@@ -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",
index c610da42bbbfbb943586006be70f5fca6e1222e2..46914a17969479a43954f59cf3208ebcc691b1d0 100644 (file)
@@ -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 = {