]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
cache.clear(): more flexibility via parameters
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 25 Jul 2018 14:09:27 +0000 (16:09 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Fri, 17 Aug 2018 13:58:47 +0000 (15:58 +0200)
daemon/README.rst
daemon/bindings.c
daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/lua/sandbox.lua
lib/cache/api.c
lib/cache/api.h
lib/cache/cdb_api.h
lib/cache/cdb_lmdb.c

index dfabc5f13b7cc69e5124a5e5466bffd4563e8b20..002e3375a5f0208508e9d2a7eec2837285a1b01d 100644 (file)
@@ -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
index a409b8ffa85b4dc6060fdd7a1b35623e4f8a1e12..3f49e2087569247bd92869bb28729a46b4c95d22 100644 (file)
@@ -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 },
index d644e96383a890c7cf15535b31f55a69dcc33b2a..20bd48d0de3f866f261cc5d4013d58559f8fff69 100644 (file)
@@ -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];
index ec52f5e8d0b86083db7960e08c04549976ec0dbd..e02d72297e2c80de09da6e8ba5a90440c0e88a83 100755 (executable)
@@ -183,6 +183,8 @@ EOF
 # Cache
        kr_cache_insert_rr
        kr_cache_sync
+       kr_cache_remove
+       kr_cache_remove_subtree
 EOF
 
 
index 4891846375f73726d50f7231224f38642cafb1b7..33559dce0d2b171430fa2201ab29eadadcb641a6 100644 (file)
@@ -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
index 634ced86c98d4e256704247299403e473f556703..34b415508733dc06331a4e26b528753b84885160 100644 (file)
@@ -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;
+}
+
index 2dcb1cefc2d613f30ec1ae76b6c8630ab1278251..d347af3ae880f55cece17ec6df07304275c34d39 100644 (file)
@@ -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
index 6048c7337ac415baca318beeb246430d1deb84fa..8836d57a253316d3801bef81f45952172f15c8e3 100644 (file)
@@ -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).
index 23f65261c71048d853b388d7d4d7110f5592ec85..e4e32363834db90069d86f263ebf08c68820a90f 100644 (file)
@@ -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;