#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:
}
}
-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;
}
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);
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
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);
//// 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);
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) {
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++;
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));
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;