From: Vladimír Čunát Date: Wed, 25 Jul 2018 14:09:27 +0000 (+0200) Subject: cache.clear(): more flexibility via parameters X-Git-Tag: v3.0.0~1^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=720683ed871906c15ac38399c97e6653ef04ca1e;p=thirdparty%2Fknot-resolver.git cache.clear(): more flexibility via parameters --- diff --git a/daemon/README.rst b/daemon/README.rst index dfabc5f13..002e3375a 100644 --- a/daemon/README.rst +++ b/daemon/README.rst @@ -860,34 +860,42 @@ daemons or manipulated from other processes, making for example synchronised loa [AAAA] => true } -.. function:: cache.clear([domain]) - - :return: ``bool`` or ``int`` +.. function:: cache.clear([name], [exact_name], [rr_type], [maxcount], [callback]) Purge cache records. - If the domain isn't provided, whole cache is purged and ``bool`` is returned (denoting success). - If you provide a name, only records in that subtree are purged, - and the number of removed records is returned. + :return: ``bool`` (success of removing all in one go) + + :param string name: if the name isn't provided, whole cache is purged + (and any other parameters are disregarded). + Otherwise only records in that subtree are removed. + :param bool exact_name: if set to ``true``, only records with *the same* name are removed. + :param kres.type rr_type: you may additionally specify the type to remove, + but that is only supported with ``exact_name == true``. + :param integer maxcount: the number of records to remove at one go, default: 100. + The purpose is not to block the resolver for long; + the ``callback`` parameter by default handles this by asynchronous repetition. + :param function callback: custom code to handle result of the underlying C call. + As the first parameter it gets the return code from :func:`kr_cache_remove_subtree()`, + and the following parameters are copies of those passed to `cache.clear()`. + The default callback repeats the command after one millisecond (if successful). Examples: .. code-block:: lua - -- Clear records at and below 'bad.cz' - cache.clear('bad.cz') -- Clear whole cache cache.clear() + -- Clear records at and below 'bad.cz' + cache.clear('bad.cz') - .. attention:: In case you provide a name: + .. attention:: - - The number of removed records is limited to 1000. - The purpose is not to block the resolver for long; if you need larger purges, - you may e.g. repeat the command with a 1ms timer until it returns a lower value. - - To minimize surprises, you may prefer to specify names that have NS/SOA records, - e.g. ``example.com``. Details: validated NSEC and NSEC3 records - (which are used for aggressive non-existence proofs) - will be removed only for zones whose **apex** is at or below the specified name. + To minimize surprises with partial cache removal, + you may prefer to specify names that have NS/SOA records, + e.g. ``example.com``. Details: validated NSEC and NSEC3 records + (which are used for aggressive non-existence proofs) + will be removed only for zones whose **apex** is at or below the specified name. Timers and events diff --git a/daemon/bindings.c b/daemon/bindings.c index a409b8ffa..3f49e2087 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -1076,52 +1076,16 @@ static int cache_close(lua_State *L) } /** @internal Prefix walk. */ -static int cache_prefixed(struct kr_cache *cache, const char *args, knot_db_val_t *results, int maxresults) +static int cache_prefixed(struct kr_cache *cache, const char *prefix, bool exact_name, + knot_db_val_t keyval[][2], int maxcount) { /* Convert to domain name */ uint8_t buf[KNOT_DNAME_MAXLEN]; - if (!knot_dname_from_str(buf, args, sizeof(buf))) { + if (!knot_dname_from_str(buf, prefix, sizeof(buf))) { return kr_error(EINVAL); } - /* Start prefix search */ - int ret = kr_cache_match(cache, buf, results, maxresults); - kr_cache_sync(cache); - return ret; -} - -/** @internal Delete iterated key. */ -static int cache_remove_prefix(struct kr_cache *cache, const char *args) -{ - /* Check if we can remove */ - if (!cache || !cache->api || !cache->api->remove) { - return kr_error(ENOSYS); - } - knot_db_val_t result_set[1000]; - int ret = cache_prefixed(cache, args, result_set, 1000); - if (ret < 0) { - return ret; - } - /* Duplicate result set as we're going to remove it - * which will invalidate result set. */ - int i; - for (i = 0; i < ret; ++i) { - void *dst = malloc(result_set[i].len); - if (!dst) { - ret = kr_error(ENOMEM); - goto cleanup; - } - memcpy(dst, result_set[i].data, result_set[i].len); - result_set[i].data = dst; - } - cache->api->remove(cache->db, result_set, ret); - kr_cache_sync(cache); -cleanup: - /* Free keys */ - while (--i >= 0) { - free(result_set[i].data); - } - return ret; + return kr_cache_match(cache, buf, exact_name, keyval, maxcount); } /** Prune expired/invalid records. */ @@ -1149,30 +1113,12 @@ static int cache_prune(lua_State *L) return 1; } -/** Clear all records. */ -static int cache_clear(lua_State *L) +/** Clear everything. */ +static int cache_clear_everything(lua_State *L) { struct kr_cache *cache = cache_assert_open(L); - /* Check parameters */ - const char *args = NULL; - int n = lua_gettop(L); - if (n >= 1 && lua_isstring(L, 1)) { - args = lua_tostring(L, 1); - } - - /* Clear a sub-tree in cache. */ - if (args && strlen(args) > 0) { - int ret = cache_remove_prefix(cache, args); - if (ret < 0) { - format_error(L, kr_strerror(ret)); - lua_error(L); - } - lua_pushinteger(L, ret); - return 1; - } - - /* Clear cache. */ + /* Clear records and packets. */ int ret = kr_cache_clear(cache); if (ret < 0) { format_error(L, kr_strerror(ret)); @@ -1189,13 +1135,13 @@ static int cache_clear(lua_State *L) } /** @internal Dump cache key into table on Lua stack. */ -static void cache_dump_key(lua_State *L, knot_db_val_t key) +static void cache_dump(lua_State *L, knot_db_val_t keyval[]) { knot_dname_t dname[KNOT_DNAME_MAXLEN]; char name[KNOT_DNAME_TXT_MAXLEN]; uint16_t type; - int ret = kr_unpack_cache_key(key, dname, &type); + int ret = kr_unpack_cache_key(keyval[0], dname, &type); if (ret < 0) { return; } @@ -1232,9 +1178,9 @@ static int cache_get(lua_State *L) } /* Retrieve set of keys */ - const char *args = lua_tostring(L, 1); - knot_db_val_t result_set[100]; - int ret = cache_prefixed(cache, args, result_set, 100); + const char *prefix = lua_tostring(L, 1); + knot_db_val_t keyval[100][2]; + int ret = cache_prefixed(cache, prefix, false/*FIXME*/, keyval, 100); if (ret < 0) { format_error(L, kr_strerror(ret)); lua_error(L); @@ -1242,7 +1188,7 @@ static int cache_get(lua_State *L) /* Format output */ lua_newtable(L); for (int i = 0; i < ret; ++i) { - cache_dump_key(L, result_set[i]); + cache_dump(L, keyval[i]); } return 1; } @@ -1365,8 +1311,8 @@ int lib_cache(lua_State *L) { "open", cache_open }, { "close", cache_close }, { "prune", cache_prune }, - { "clear", cache_clear }, - { "get", cache_get }, + { "clear_everything", cache_clear_everything }, + { "get", cache_get }, { "max_ttl", cache_max_ttl }, { "min_ttl", cache_min_ttl }, { "ns_tout", cache_ns_tout }, diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index d644e9638..20bd48d0d 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -322,7 +322,10 @@ _Bool kr_dnssec_key_ksk(const uint8_t *); _Bool kr_dnssec_key_revoked(const uint8_t *); int kr_dnssec_key_tag(uint16_t, const uint8_t *, size_t); int kr_dnssec_key_match(const uint8_t *, size_t, const uint8_t *, size_t); +int kr_cache_closest_apex(struct kr_cache *, const knot_dname_t *, _Bool); int kr_cache_insert_rr(struct kr_cache *, const knot_rrset_t *, const knot_rrset_t *, uint8_t, uint32_t); +int kr_cache_remove(struct kr_cache *, const knot_dname_t *, uint16_t); +int kr_cache_remove_subtree(struct kr_cache *, const knot_dname_t *, _Bool, int); int kr_cache_sync(struct kr_cache *); typedef struct { uint8_t bitmap[32]; diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index ec52f5e8d..e02d72297 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -183,6 +183,8 @@ EOF # Cache kr_cache_insert_rr kr_cache_sync + kr_cache_remove + kr_cache_remove_subtree EOF diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua index 489184637..33559dce0 100644 --- a/daemon/lua/sandbox.lua +++ b/daemon/lua/sandbox.lua @@ -157,6 +157,44 @@ setmetatable(modules, { end }) + +cache.clear = function (name, exact_name, rr_type, maxcount, callback) + if name == nil then return cache.clear_everything() end + -- Check parameters, in order, and set defaults if missing. + local dname = kres.str2dname(name) + if not dname then error('cache.clear(): incorrect name passed') end + if exact_name == nil then exact_name = false end + if type(exact_name) ~= 'boolean' + then error('cache.clear(): incorrect exact_name passed') end + if rr_type ~= nil then + -- Special case, without any subtree searching. + if not exact_name + then error('cache.clear(): specifying rr_type only supported with exact_name') end + if maxcount or callback + then error('cache.clear(): maxcount and callback parameters not supported with rr_type') end + local ret = ffi.C.kr_cache_remove(kres.context().cache, dname, rr_type) + if ret ~= 0 then error(ffi.string(ffi.C.knot_strerror(ret))) end + return true + end + if maxcount == nil then maxcount = 100 end + if type(maxcount) ~= 'number' or maxcount <= 0 + then error('cache.clear(): maxcount has to be a positive integer') end + -- Default callback function: repeat after 1ms + if callback == nil then callback = + function (ret, name, exact_name, rr_type, maxcount, callback) + if ret < 0 then error(ffi.string(ffi.C.knot_strerror(ret))) end + if (ret < maxcount) then return true end + event.after(1, function () + cache.clear(name, exact_name, rr_type, maxcount, callback) + end) + return false + end + end + + -- Do the C call and callback. + local ret = ffi.C.kr_cache_remove_subtree(kres.context().cache, dname, exact_name, maxcount) + return callback(ret, name, exact_name, rr_type, maxcount, callback) +end -- Syntactic sugar for cache -- `cache[x] -> cache.get(x)` -- `cache.{size|storage} = value` @@ -372,4 +410,4 @@ else worker.sleep = disabled worker.map = disabled worker.coroutine = disabled -end \ No newline at end of file +end diff --git a/lib/cache/api.c b/lib/cache/api.c index 634ced86c..34b415508 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -779,7 +779,7 @@ int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t t } int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name, - knot_db_val_t *keys, int max) + bool exact_name, knot_db_val_t keyval[][2], int maxcount) { if (!cache_isvalid(cache)) { return kr_error(EINVAL); @@ -795,9 +795,13 @@ int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name, // use a mock type knot_db_val_t key = key_exact_type(k, KNOT_RRTYPE_A); - key.len -= 2 /* '\0' 'E' */ + sizeof(uint16_t); /* CACHE_KEY_DEF */ - if (name[0] == '\0') ++key.len; /* the root name is special ATM */ - return cache_op(cache, match, &key, keys, max); + /* CACHE_KEY_DEF */ + key.len -= sizeof(uint16_t); /* the type */ + if (!exact_name) { + key.len -= 2; /* '\0' 'E' */ + if (name[0] == '\0') ++key.len; /* the root name is special ATM */ + } + return cache_op(cache, match, &key, keyval, maxcount); } int kr_unpack_cache_key(knot_db_val_t key, knot_dname_t *buf, uint16_t *type) @@ -831,3 +835,39 @@ int kr_unpack_cache_key(knot_db_val_t key, knot_dname_t *buf, uint16_t *type) return kr_ok(); } + + +int kr_cache_remove_subtree(struct kr_cache *cache, const knot_dname_t *name, + bool exact_name, int maxcount) +{ + if (!cache_isvalid(cache)) { + return kr_error(EINVAL); + } + + knot_db_val_t keyval[maxcount][2], keys[maxcount]; + int ret = kr_cache_match(cache, name, exact_name, keyval, maxcount); + if (ret < 0) { + return ret; + } + const int count = ret; + /* Duplicate the key strings, as deletion may invalidate the pointers. */ + int i; + for (i = 0; i < count; ++i) { + keys[i].len = keyval[i][0].len; + keys[i].data = malloc(keys[i].len); + if (!keys[i].data) { + ret = kr_error(ENOMEM); + goto cleanup; + } + memcpy(keys[i].data, keyval[i][0].data, keys[i].len); + } + ret = cache->api->remove(cache->db, keys, count); +cleanup: + kr_cache_sync(cache); /* Sync even after just kr_cache_match(). */ + /* Free keys */ + while (--i >= 0) { + free(keys[i].data); + } + return ret ? ret : count; +} + diff --git a/lib/cache/api.h b/lib/cache/api.h index 2dcb1cefc..d347af3ae 100644 --- a/lib/cache/api.h +++ b/lib/cache/api.h @@ -153,14 +153,23 @@ int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t t * Get keys matching a dname lf prefix * @param cache cache structure * @param name dname - * @param keys matched keys + * @param exact_name whether to only consider exact name matches + * @param keyval matched key-value pairs + * @param maxcount limit on the number of returned key-value pairs * @return result count or an errcode * @note the cache keys are matched by prefix, i.e. it very much depends * on their structure; CACHE_KEY_DEF. */ KR_EXPORT int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name, - knot_db_val_t *keys, int max); + bool exact_name, knot_db_val_t keyval[][2], int maxcount); + +/** + * Remove a subtree in cache. It's like _match but removing them instead of returning. + */ +KR_EXPORT +int kr_cache_remove_subtree(struct kr_cache *cache, const knot_dname_t *name, + bool exact_name, int maxcount); /** * Unpack dname and type from db key diff --git a/lib/cache/cdb_api.h b/lib/cache/cdb_api.h index 6048c7337..8836d57a2 100644 --- a/lib/cache/cdb_api.h +++ b/lib/cache/cdb_api.h @@ -47,11 +47,15 @@ struct kr_cdb_api { int maxcount); int (*write)(knot_db_t *db, const knot_db_val_t *key, knot_db_val_t *val, int maxcount); - int (*remove)(knot_db_t *db, knot_db_val_t *key, int maxcount); + /** Remove maxcount keys. Return error code. (Returns on first error.) */ + int (*remove)(knot_db_t *db, knot_db_val_t keys[], int maxcount); /* Specialised operations */ - int (*match)(knot_db_t *db, knot_db_val_t *key, knot_db_val_t *val, int maxcount); + /** Find key-value pairs that are prefixed by the given key, limited by maxcount. + * \return the number of pairs or negative error. */ + int (*match)(knot_db_t *db, knot_db_val_t *key, knot_db_val_t keyval[][2], int maxcount); + /** Not implemented ATM. */ int (*prune)(knot_db_t *db, int maxcount); /** Less-or-equal search (lexicographic ordering). diff --git a/lib/cache/cdb_lmdb.c b/lib/cache/cdb_lmdb.c index 23f65261c..e4e323638 100644 --- a/lib/cache/cdb_lmdb.c +++ b/lib/cache/cdb_lmdb.c @@ -547,14 +547,14 @@ static int cdb_writev(knot_db_t *db, const knot_db_val_t *key, knot_db_val_t *va return ret; } -static int cdb_remove(knot_db_t *db, knot_db_val_t *key, int maxcount) +static int cdb_remove(knot_db_t *db, knot_db_val_t keys[], int maxcount) { struct lmdb_env *env = db; MDB_txn *txn = NULL; int ret = txn_get(env, &txn, false); for (int i = 0; ret == kr_ok() && i < maxcount; ++i) { - MDB_val _key = val_knot2mdb(key[i]); + MDB_val _key = val_knot2mdb(keys[i]); MDB_val val = { 0, NULL }; ret = lmdb_error(mdb_del(txn, env->dbi, &_key, &val)); } @@ -562,7 +562,7 @@ static int cdb_remove(knot_db_t *db, knot_db_val_t *key, int maxcount) return ret; } -static int cdb_match(knot_db_t *db, knot_db_val_t *key, knot_db_val_t *val, int maxcount) +static int cdb_match(knot_db_t *db, knot_db_val_t *key, knot_db_val_t keyval[][2], int maxcount) { struct lmdb_env *env = db; MDB_txn *txn = NULL; @@ -594,7 +594,8 @@ static int cdb_match(knot_db_t *db, knot_db_val_t *key, knot_db_val_t *val, int } /* Add to result set */ if (results < maxcount) { - val[results] = val_mdb2knot(cur_key); + keyval[results][0] = val_mdb2knot(cur_key); + keyval[results][1] = val_mdb2knot(cur_val); ++results; } else { break;