Commit e58d1688 authored by Marek Vavruša's avatar Marek Vavruša Committed by Marek Vavruša

lib/cache: save cache lookup if record is stale and not missing

the cache_peek() api was reworked to return an error code instead, from this the caller can tell whether the record is present (but expired) or missing. this save a secondary CNAME lookup in case the original record was just expired
parent 149d5e1c
......@@ -126,40 +126,41 @@ static struct kr_cache_entry *cache_entry(struct kr_cache_txn *txn, uint8_t tag,
return (struct kr_cache_entry *)val.data;
}
struct kr_cache_entry *kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name,
uint16_t type, uint32_t *timestamp)
int kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
struct kr_cache_entry **entry, uint32_t *timestamp)
{
if (!txn || !txn->owner || !tag || !name) {
return NULL;
if (!txn || !txn->owner || !tag || !name || !entry) {
return kr_error(EINVAL);
}
struct kr_cache_entry *entry = cache_entry(txn, tag, name, type);
if (!entry) {
struct kr_cache_entry *found = cache_entry(txn, tag, name, type);
if (!found) {
txn->owner->stats.miss += 1;
return NULL;
return kr_error(ENOENT);
}
/* No time constraint */
*entry = found;
if (!timestamp) {
txn->owner->stats.hit += 1;
return entry;
} else if (*timestamp <= entry->timestamp) {
return kr_ok();
} else if (*timestamp <= found->timestamp) {
/* John Connor record cached in the future. */
*timestamp = 0;
txn->owner->stats.hit += 1;
return entry;
return kr_ok();
} else {
/* Check if the record is still valid. */
uint32_t drift = *timestamp - entry->timestamp;
if (drift < entry->ttl) {
uint32_t drift = *timestamp - found->timestamp;
if (drift < found->ttl) {
*timestamp = drift;
txn->owner->stats.hit += 1;
return entry;
return kr_ok();
}
}
txn->owner->stats.miss += 1;
return NULL;
return kr_error(ESTALE);
}
static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *header, namedb_val_t data)
......@@ -237,15 +238,14 @@ int kr_cache_peek_rr(struct kr_cache_txn *txn, knot_rrset_t *rr, uint32_t *times
}
/* Check if the RRSet is in the cache. */
struct kr_cache_entry *entry = kr_cache_peek(txn, KR_CACHE_RR, rr->owner, rr->type, timestamp);
if (entry) {
rr->rrs.rr_count = entry->count;
rr->rrs.data = entry->data;
return kr_ok();
struct kr_cache_entry *entry = NULL;
int ret = kr_cache_peek(txn, KR_CACHE_RR, rr->owner, rr->type, &entry, timestamp);
if (ret != 0) {
return ret;
}
/* Not found. */
return kr_error(ENOENT);
rr->rrs.rr_count = entry->count;
rr->rrs.data = entry->data;
return kr_ok();
}
int kr_cache_materialize(knot_rrset_t *dst, const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm)
......
......@@ -108,11 +108,12 @@ void kr_cache_txn_abort(struct kr_cache_txn *txn);
* @param tag asset tag
* @param name asset name
* @param type asset type
* @param entry cache entry, will be set to valid pointer or NULL
* @param timestamp current time (will be replaced with drift if successful)
* @return cache entry or NULL
* @return 0 or an errcode
*/
struct kr_cache_entry *kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name,
uint16_t type, uint32_t *timestamp);
int kr_cache_peek(struct kr_cache_txn *txn, uint8_t tag, const knot_dname_t *name, uint16_t type,
struct kr_cache_entry **entry, uint32_t *timestamp);
/**
* Insert asset into cache, replacing any existing data.
......
......@@ -58,10 +58,10 @@ static void adjust_ttl(knot_rrset_t *rr, uint32_t drift)
static int loot_cache_pkt(struct kr_cache_txn *txn, knot_pkt_t *pkt, const knot_dname_t *qname,
uint16_t rrtype, uint8_t tag, uint32_t timestamp)
{
struct kr_cache_entry *entry;
entry = kr_cache_peek(txn, tag, qname, rrtype, &timestamp);
if (!entry) { /* Not in the cache */
return kr_error(ENOENT);
struct kr_cache_entry *entry = NULL;
int ret = kr_cache_peek(txn, tag, qname, rrtype, &entry, &timestamp);
if (ret != 0) { /* Not in the cache */
return ret;
}
/* Copy answer, keep the original message id */
......@@ -79,7 +79,6 @@ static int loot_cache_pkt(struct kr_cache_txn *txn, knot_pkt_t *pkt, const knot_
}
/* Adjust TTL in records. */
int ret = 0;
for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
for (unsigned k = 0; k < sec->count; ++k) {
......@@ -176,7 +175,7 @@ static int stash(knot_layer_t *ctx, knot_pkt_t *pkt)
}
bool is_auth = knot_wire_get_aa(pkt->wire);
int pkt_class = kr_response_classify(pkt);
if (!(pkt_class & (PKT_NODATA|PKT_REFUSED|PKT_NXDOMAIN)) && is_auth) {
if (!(pkt_class & (PKT_NODATA|PKT_NXDOMAIN)) && is_auth) {
return ctx->state; /* Cache only negative, not-cached answers. */
}
if (knot_pkt_qclass(pkt) != KNOT_CLASS_IN) {
......
......@@ -152,7 +152,7 @@ static int write_cache_rr(const knot_pktsection_t *section, knot_rrset_t *rr, st
/* Check if already cached. */
knot_rrset_t query_rr;
knot_rrset_init(&query_rr, rr->owner, rr->type, rr->rclass);
if (kr_cache_peek_rr(txn, &query_rr, &timestamp) == KNOT_EOK) {
if (kr_cache_peek_rr(txn, &query_rr, &timestamp) == 0) {
return KNOT_EOK;
}
......@@ -160,10 +160,10 @@ static int write_cache_rr(const knot_pktsection_t *section, knot_rrset_t *rr, st
int ret = KNOT_EOK;
uint16_t orig_rrtype = rr->type;
rr->type = KNOT_RRTYPE_CNAME;
while((merge_in_section(rr, section, 0, pool)) == KNOT_EOK) {
while((merge_in_section(rr, section, 0, pool)) == 0) {
/* Cache the merged RRSet */
ret = kr_cache_insert_rr(txn, rr, timestamp);
if (ret != KNOT_EOK) {
if (ret != 0) {
knot_rdataset_clear(&rr->rrs, pool);
return ret;
}
......@@ -171,7 +171,7 @@ static int write_cache_rr(const knot_pktsection_t *section, knot_rrset_t *rr, st
rr->owner = (knot_dname_t *)knot_ns_name(&rr->rrs, 0);
knot_rdataset_clear(&rr->rrs, pool);
/* Check if target already cached. */
if (kr_cache_peek_rr(txn, rr, &timestamp) == KNOT_EOK) {
if (kr_cache_peek_rr(txn, rr, &timestamp) == 0) {
break;
}
}
......
......@@ -94,7 +94,7 @@ static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param)
if (!next_type || kr_rplan_satisfies(qry->parent, qry->ns.name, KNOT_CLASS_IN, next_type)) {
/* No IPv4 nor IPv6, flag server as unuseable. */
DEBUG_MSG("=> unresolvable NS address, bailing out\n");
kr_nsrep_update_rep(&qry->ns, qry->ns.reputation | (KR_NS_NOIP4|KR_NS_NOIP6), ctx->cache_rep);
kr_nsrep_update_rep(&qry->ns, qry->ns.reputation | KR_NS_NOIP6, ctx->cache_rep);
invalidate_ns(rplan, qry);
return kr_error(EHOSTUNREACH);
}
......
......@@ -210,20 +210,21 @@ static struct kr_cache_txn *test_txn_rdonly(void **state)
static void test_fake_invalid (void **state)
{
struct kr_cache_txn *txn = NULL;
const namedb_api_t *api_saved;
const namedb_api_t *api_saved = NULL;
knot_dname_t dname[] = "";
struct kr_cache_entry *ret;
struct kr_cache_entry *entry = NULL;
int ret = 0;
assert_int_not_equal(kr_cache_txn_commit(txn), KNOT_EOK);
assert_int_not_equal(kr_cache_txn_commit(txn), 0);
txn = test_txn_write(state);
assert_int_not_equal(kr_cache_txn_commit(txn), KNOT_EOK);
ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, 0);
assert_int_equal(ret, &global_fake_ce);
assert_int_not_equal(kr_cache_txn_commit(txn), 0);
ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, &entry, 0);
assert_int_equal(ret, 0);
api_saved = txn->owner->api;
txn->owner->api = NULL;
ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, 0);
ret = kr_cache_peek(txn, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, &entry, 0);
txn->owner->api = api_saved;
assert_null(ret);
assert_int_not_equal(ret, 0);
}
static void test_fake_insert(void **state)
......@@ -256,6 +257,7 @@ static void test_invalid(void **state)
knot_dname_t dname[] = "";
uint32_t timestamp = CACHE_TIME;
struct namedb_lmdb_opts opts;
struct kr_cache_entry *entry = NULL;
memset(&opts, 0, sizeof(opts));
opts.path = global_env;
......@@ -267,9 +269,9 @@ static void test_invalid(void **state)
assert_int_not_equal(kr_cache_txn_begin(NULL, &global_txn, 0), 0);
assert_int_not_equal(kr_cache_txn_begin(*state, NULL, 0), 0);
assert_int_not_equal(kr_cache_txn_commit(NULL), 0);
assert_null(kr_cache_peek(NULL, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, &timestamp));
assert_null(kr_cache_peek(&global_txn, 0, dname, KNOT_RRTYPE_TSIG, &timestamp));
assert_null(kr_cache_peek(&global_txn, KR_CACHE_USER, NULL, KNOT_RRTYPE_TSIG, &timestamp));
assert_int_not_equal(kr_cache_peek(NULL, KR_CACHE_USER, dname, KNOT_RRTYPE_TSIG, NULL, &timestamp), 0);
assert_int_not_equal(kr_cache_peek(&global_txn, 0, dname, KNOT_RRTYPE_TSIG, &entry, &timestamp), 0);
assert_int_not_equal(kr_cache_peek(&global_txn, KR_CACHE_USER, NULL, KNOT_RRTYPE_TSIG, &entry, &timestamp), 0);
assert_int_not_equal(kr_cache_peek_rr(NULL, NULL, NULL), 0);
assert_int_not_equal(kr_cache_peek_rr(&global_txn, NULL, NULL), 0);
assert_int_not_equal(kr_cache_insert_rr(&global_txn, NULL, 0), 0);
......@@ -364,7 +366,7 @@ static void test_query_aged(void **state)
struct kr_cache_txn *txn = test_txn_rdonly(state);
int ret = kr_cache_peek_rr(txn, &cache_rr, &timestamp);
assert_int_equal(ret, KNOT_ENOENT);
assert_int_equal(ret, kr_error(ESTALE));
kr_cache_txn_abort(txn);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment