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;
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;
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. */
{
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);
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);
.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,
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),
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;
}
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) {
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;
}
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));
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. */
/* 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) {
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();
}
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();
}
#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) {
} // 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");
* 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;
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,
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;
}
#pragma once
#include "lib/mmapped.h"
+#include "lib/kru.h"
struct kr_cache_top {
struct mmapped mmapped;
};
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);
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);
// 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);
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. */
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. */
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);
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;
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;
}
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);
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;
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",
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;
}
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 = {