]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/cache: clear and resize cache on size-max decrease
authorLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 16 Jul 2025 17:16:16 +0000 (19:16 +0200)
committerLukáš Ondráček <lukas.ondracek@nic.cz>
Wed, 16 Jul 2025 17:16:16 +0000 (19:16 +0200)
doc/user/config-cache.rst
lib/cache/api.c
lib/cache/cdb_api.h
lib/cache/cdb_lmdb.c
lib/rules/api.c
python/knot_resolver/manager/manager.py

index 500c7266e17ba4a3da174deffaa9dd84a96a3574..d0f1dd77b84d9bbd4ffeaf3e8ba7b4495d16b69d 100644 (file)
@@ -134,7 +134,9 @@ Configuration reference
 .. note:: Use ``B, K, M, G`` bytes units prefixes.
 
 Opens cache with a size limit. The cache will be reopened if already open.
-Note that the maximum size cannot be lowered, only increased due to how cache is implemented.
+Note that lowering the maximum size removes all cached data;
+increasing the size removes usage statistics of the stored entries used by garbage collector,
+but keeps the actual data.
 
 .. code-block:: yaml
 
index 62f1901911c5659b5639f0dbc29ca1819dcc3d6a..737d38857577531201d01ec161bfb9815c534ef4 100644 (file)
@@ -93,7 +93,7 @@ static int assert_right_version(struct kr_cache *cache)
                                        (int)ver, (int)CACHE_VERSION);
                        }
                }
-               ret = cache_op(cache, clear);
+               ret = cache_op(cache, clear, 0);
        }
        /* Rewrite the entry even if it isn't needed.  Because of cache-size-changing
         * possibility it's good to always perform some write during opening of cache. */
@@ -142,10 +142,13 @@ int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct k
        size_t maxsize = 0;
        if (ret == 0) {
                maxsize = cache->api->get_maxsize(cache->db);
-               if (opts->maxsize && (maxsize > opts->maxsize)) kr_log_warning(CACHE,
-                       "Warning: real cache size is %zu instead of the requested %zu bytes."
-                       "  To reduce the size you need to remove the file '%s' by hand.\n",
-                       maxsize, opts->maxsize, fpath);  // TODO remove file instead
+               if (opts->maxsize && (maxsize > opts->maxsize)) {
+                       kr_log_warning(CACHE,
+                               "Warning: real cache size is %zu instead of the requested %zu bytes, removing all data.",
+                               maxsize, opts->maxsize, fpath);
+                       cache_op(cache, clear, opts->maxsize);
+                       maxsize = cache->api->get_maxsize(cache->db);
+               }
        }
        if (ret != 0)
                return ret;
@@ -202,7 +205,7 @@ int kr_cache_clear(struct kr_cache *cache)
        if (!cache_isvalid(cache)) {
                return kr_error(EINVAL);
        }
-       int ret = cache_op(cache, clear);
+       int ret = cache_op(cache, clear, 0);
        if (ret == 0) {
                kr_cache_make_checkpoint(cache);
                ret = assert_right_version(cache);
index 23a795d532d942ea9efc20f6b9f786c04c827dcb..4b981a7dda0c9f58ef6fdfe4ca66bb61d4d0667a 100644 (file)
@@ -55,7 +55,9 @@ struct kr_cdb_api {
        int (*open)(kr_cdb_pt *db, struct kr_cdb_stats *stat, struct kr_cdb_opts *opts, knot_mm_t *mm);
        void (*close)(kr_cdb_pt db, struct kr_cdb_stats *stat);
        int (*count)(kr_cdb_pt db, struct kr_cdb_stats *stat);
-       int (*clear)(kr_cdb_pt db, struct kr_cdb_stats *stat);
+
+       /** Clear cache, possibly changing its size unless maxsize == 0. */
+       int (*clear)(kr_cdb_pt db, struct kr_cdb_stats *stat, const size_t maxsize);
 
        /** Run after a row of operations to release transaction/lock if needed.
         * \param accept_rw whether the RW transaction should accept changes (commit vs. abort)
index f5e56bc644c6c82e103660508d7005442655794e..56746c09fbed7b8bdf7aba7be85e59374ae7d547 100644 (file)
@@ -533,7 +533,7 @@ static int cdb_check_health(kr_cdb_pt db, struct kr_cdb_stats *stats)
 
 /** Obtain exclusive (advisory) lock by creating a file, returning FD or negative kr_error().
  * The lock is auto-released by OS in case the process finishes in any way (file remains). */
-static int lockfile_get(const char *path)
+static int lockfile_get(const char *path, bool wait)
 {
        if (kr_fails_assert(path))
                return kr_error(EINVAL);
@@ -549,7 +549,7 @@ static int lockfile_get(const char *path)
        lock_info.l_len = 1; // it's OK for locks to extend beyond the end of the file
        int err;
        do {
-               err = fcntl(fd, F_SETLK, &lock_info);
+               err = fcntl(fd, (wait ? F_SETLKW : F_SETLK), &lock_info);
        } while (err == -1 && errno == EINTR);
        if (err) {
                close(fd);
@@ -570,12 +570,12 @@ static int lockfile_release(int fd)
        }
 }
 
-static int cdb_clear(kr_cdb_pt db, struct kr_cdb_stats *stats)
+static int cdb_clear(kr_cdb_pt db, struct kr_cdb_stats *stats, const size_t mapsize)
 {
        struct lmdb_env *env = db2env(db);
        stats->clear++;
-       /* First try mdb_drop() to clear the DB; this may fail with ENOSPC. */
-       {
+       /* First try mdb_drop() to clear the DB unless size change is requested; this may fail with ENOSPC. */
+       if (mapsize == 0) {
                MDB_txn *txn = NULL;
                int ret = txn_get(env, &txn, false);
                if (ret == kr_ok()) {
@@ -609,7 +609,7 @@ static int cdb_clear(kr_cdb_pt db, struct kr_cdb_stats *stats)
        }
 
        /* Find if we get a lock on lockfile. */
-       const int lockfile_fd = lockfile_get(lockfile);
+       const int lockfile_fd = lockfile_get(lockfile, mapsize != 0);
        if (lockfile_fd < 0) {
                kr_log_error(MDB, "clearing failed to get ./krcachelock (%s); retry later\n",
                                kr_strerror(lockfile_fd));
@@ -631,7 +631,7 @@ static int cdb_clear(kr_cdb_pt db, struct kr_cdb_stats *stats)
                // coverity[toctou]
                unlink(env->mdb_data_path);
                unlink(mdb_lockfile);
-               ret = reopen_env(env, stats, env->mapsize);
+               ret = reopen_env(env, stats, mapsize ? mapsize : env->mapsize);
        }
 
        /* Environment updated, release lockfile. */
index 82e3ee769b65c183b7b68405cb93e04ee436470e..8b8d01d0086d88f40825188141def8ea36e3609c 100644 (file)
@@ -178,7 +178,7 @@ int kr_rules_init(const char *path, size_t maxsize, bool overwrite)
        };
        int ret = the_rules->api->open(&the_rules->db, &the_rules->stats, &opts, NULL);
 
-       if (ret == 0 && overwrite) ret = ruledb_op(clear);
+       if (ret == 0 && overwrite) ret = ruledb_op(clear, 0);
        if (ret != 0) goto failure;
        kr_require(the_rules->db);
 
index dcd14b537859d1a62007876328c6ad227955cb67..56b13ac3518913c96f5322f5f40807df046c60aa 100644 (file)
@@ -333,6 +333,13 @@ class KresManager:  # pylint: disable=too-many-instance-attributes
     async def load_policy_rules(self, _old: KresConfig, new: KresConfig) -> Result[NoneType, str]:
         try:
             async with self._manager_lock:
+                if _old.cache.size_max != new.cache.size_max:
+                    logger.debug("Unlinking shared cache top memory")
+                    try:
+                        os.unlink(str(_old.cache.storage) + "/top")
+                    except FileNotFoundError:
+                        pass
+
                 logger.debug("Running kresd 'policy-loader'")
                 await self._run_policy_loader(new)