[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
}
/** @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. */
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));
}
/** @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;
}
}
/* 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);
/* 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;
}
{ "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 },
_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];
# Cache
kr_cache_insert_rr
kr_cache_sync
+ kr_cache_remove
+ kr_cache_remove_subtree
EOF
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`
worker.sleep = disabled
worker.map = disabled
worker.coroutine = disabled
-end
\ No newline at end of file
+end
}
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);
// 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)
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;
+}
+
* 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
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).
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));
}
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;
}
/* 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;