]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
utils/cache_gc: use KRU values in item categorization
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Tue, 10 Jun 2025 15:44:54 +0000 (17:44 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 2 Jul 2025 16:55:56 +0000 (18:55 +0200)
utils/cache_gc/categories.c
utils/cache_gc/db.c
utils/cache_gc/db.h
utils/cache_gc/kr_cache_gc.c
utils/cache_gc/kr_cache_gc.h

index 48c608bd21870fc94cd10fba01d124011b238feb..e6f11b55d025f16d1d092657671bbab3fe7344ca 100644 (file)
@@ -4,8 +4,9 @@
 #include <libknot/libknot.h>
 #include "lib/utils.h"
 #include "lib/cache/top.h"
+#include "utils/cache_gc/db.h"
 
-static bool rrtype_is_infrastructure(uint16_t r)
+static bool rrtype_is_infrastructure(uint16_t r)  // currently unused
 {
        switch (r) {
        case KNOT_RRTYPE_NS:
@@ -19,41 +20,47 @@ static bool rrtype_is_infrastructure(uint16_t r)
        }
 }
 
-static unsigned int get_random(int to)
+static unsigned int get_random(int to)  // currently unused
 {
        // We don't need these to be really unpredictable,
        // but this should be cheap enough not to be noticeable.
        return kr_rand_bytes(1) % to;
 }
 
-// TODO this is just an example, make this more clever
+static inline int load2cat(uint16_t load) { // 0..64, reversed
+       const uint32_t load32 = ((uint32_t)load << 16) | 0xFFFF;
+       const int leading_zeroes = __builtin_clz(load32);  // 0..16
+       const int logss2 =  //  0, 4, 6, 8..64; approx of log with base 2^{1/4}
+               4 * (16 - leading_zeroes) +             // 4 * floor(log2(load32 >> 15))
+               (load32 >> (29 - leading_zeroes)) - 7;  // partition rounded ranges linearly
+       const int lin_log = load <= logss2 ? load : logss2;  // 0..64; linear from the beginning then logarithmic
+       return 64 - lin_log;  // lowest load -> highest cat
+}
+
 category_t kr_gc_categorize(struct kr_cache_top *top, gc_record_info_t * info, void *key, size_t key_len)
 {
-       category_t res;
+       category_t res; // 0..(CATEGORIES - 1), highest will be dropped first
 
        if (!info->valid)
                return CATEGORIES - 1;
 
-       uint16_t load = kr_cache_top_load(top, key, key_len); // TODO use it
-
-       switch (info->no_labels) {
-       case 0:         /* root zone */
-               res = 5;
-               break;
-       case 1:         /* TLD */
-               res = 10;
-               break;
-       default:                /* SLD and below */
-               res = (rrtype_is_infrastructure(info->rrtype) ? 15 : 20);
-               if (info->entry_size > 300)
-                       /* Penalty for big answers */
-                       res += 30;
-               break;
-       }
+       uint16_t load = kr_cache_top_load(top, key, key_len);
+       res = load2cat(load);  // 0..64
 
-       if (info->expires_in <= 0) {
-               res += 40;
+       // TODO check/reconsider penalties
+       if (info->rrtype == KNOT_CACHE_RTT) {
+               // TODO same priority, or prioritize this
+       } 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
+               }
        }
+       static_assert(CATEGORIES - 1 > 64 + 4 + 28);
 
-       return res + get_random(5);
+       return res;
 }
index 0e8f90c17d4dc4c46c2a7440577a453eb440e783..805891e0bcb64ee04712361324b09df2f0b18445 100644 (file)
@@ -99,8 +99,8 @@ int kr_gc_key_consistent(knot_db_val_t key)
                return KNOT_RRTYPE_NSEC;
        case '3':
                return KNOT_RRTYPE_NSEC3;
-       case 'S': // the rtt_state entries are considered inconsistent, at least for now
-               return -1;
+       case 'S':
+               return KNOT_CACHE_RTT;
        default:
                kr_assert(!EINVAL);
                return kr_error(EINVAL);
@@ -208,13 +208,15 @@ int kr_gc_cache_iter(knot_db_t * knot_db, const  kr_cache_gc_cfg_t *cfg,
                info.valid = false;
                const int entry_type = kr_gc_key_consistent(key);
                const struct entry_h *entry = NULL;
-               if (entry_type >= 0) {
+               if (entry_type == KNOT_CACHE_RTT) {
+                       counter_gc_consistent++;
+                       info.valid = true;
+                       info.rrtype = entry_type;
+               } else if (entry_type >= 0) {
                        counter_gc_consistent++;
                        entry = val2entry(val, entry_type);
                }
                /* TODO: perhaps improve some details around here:
-                *  - rtt_state entries are considered gc_inconsistent;
-                *    therefore they'll be the first to get freed (see kr_gc_categorize())
                 *  - xNAME have .rrtype NS
                 *  - DNAME hidden on NS name will not be considered here
                 *  - if zone has NSEC* meta-data but no NS, it will be seen
index 7865471b552e482bafbe702f28a24ab214fc6133..23aeedca2d89ff92e133b50219b4b1efbf7d719e 100644 (file)
@@ -21,12 +21,12 @@ typedef int (*kr_gc_iter_callback)(const knot_db_val_t * key,
 int kr_gc_cache_iter(knot_db_t * knot_db, const  kr_cache_gc_cfg_t *cfg,
                        kr_gc_iter_callback callback, void *ctx);
 
-/** Return RR type corresponding to the key or negative error code.
+/** Return RR type corresponding to the key, KNOT_CACHE_RTT or negative error code.
  *
- * Error is returned on unexpected values (those also trigger assertion)
- * and on other kinds of data in cache (e.g. struct rtt_state).
+ * Error is returned on unexpected values (those also trigger assertion).
  */
 int kr_gc_key_consistent(knot_db_val_t key);
+#define KNOT_CACHE_RTT 0x10000
 
 /** Printf a *binary* string in a human-readable way. */
 void debug_printbin(const char *str, unsigned int len);
index d48a217fa3df911b212cd905fae453d8b773b62c..c610da42bbbfbb943586006be70f5fca6e1222e2 100644 (file)
@@ -218,9 +218,11 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
 
        //// 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 = { .top = &(*state)->kres_db.top };
-       to_del.cfg_temp_keys_space = cfg->temp_keys_space;
-       to_del.limit_category = limit_category;
+       ctx_delete_categories_t to_del = {
+               .top = &(*state)->kres_db.top,
+               .cfg_temp_keys_space = cfg->temp_keys_space,
+               .limit_category = limit_category,
+       };
        ret = kr_gc_cache_iter(db, cfg, cb_delete_categories, &to_del);
        if (ret != KNOT_EOK) {
                entry_array_deep_free(&to_del.to_delete);
@@ -240,6 +242,7 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
        kr_timer_start(&timer_delete);
        kr_timer_start(&timer_rw_txn);
        rrtype_array_t deleted_rrtypes = { 0 };
+       bool deleted_rtt = false;
 
        ret = api->txn_begin(db, &txn, 0);
        if (ret != KNOT_EOK) {
@@ -257,8 +260,13 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
                case KNOT_EOK:
                        deleted_records++;
                        const int entry_type = kr_gc_key_consistent(*val);
-                       if (entry_type >= 0) // some "inconsistent" entries are OK
-                               rrtypelist_add(&deleted_rrtypes, entry_type);
+                       if (entry_type >= 0) { // some "inconsistent" entries are OK
+                               if (entry_type == KNOT_CACHE_RTT) {
+                                       deleted_rtt = true;
+                               } else {
+                                       rrtypelist_add(&deleted_rrtypes, entry_type);
+                               }
+                       }
                        break;
                case KNOT_ENOENT:
                        already_gone++;
@@ -310,6 +318,9 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
 finish:
        printf("Deleted %zu records (%zu already gone) types", deleted_records,
               already_gone);
+       if (deleted_rtt) {
+               printf(" RTT");
+       }
        rrtypelist_print(&deleted_rrtypes);
        printf("It took %.0lf msecs, %zu transactions (%s)\n\n",
               kr_timer_elapsed(&timer_delete) * 1000, rw_txn_count, knot_strerror(ret));
index c64e99e9954d1e5e31ea41fe1a07acba9efb9425..27140e41a08f19df0dcb2f1fad60e2438a14dd7e 100644 (file)
@@ -9,7 +9,7 @@ typedef struct {
        size_t entry_size;      // amount of bytes occupied in cache by this record
        bool valid;             // fields further down are valid (ignore them if false)
        int64_t expires_in;     // < 0 => already expired
-       uint16_t rrtype;
+       uint32_t rrtype; // RR type or KNOT_CACHE_RTT
        uint8_t no_labels;      // 0 == ., 1 == root zone member, 2 == TLD member ...
        uint8_t rank;
 } gc_record_info_t;