From: Lukáš Ondráček Date: Wed, 16 Jul 2025 17:16:16 +0000 (+0200) Subject: lib/cache: clear and resize cache on size-max decrease X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8efd6e4cadba84a06a1089b03a4b8ebe88b29c8b;p=thirdparty%2Fknot-resolver.git lib/cache: clear and resize cache on size-max decrease --- diff --git a/doc/user/config-cache.rst b/doc/user/config-cache.rst index 500c7266e..d0f1dd77b 100644 --- a/doc/user/config-cache.rst +++ b/doc/user/config-cache.rst @@ -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 diff --git a/lib/cache/api.c b/lib/cache/api.c index 62f190191..737d38857 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -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); diff --git a/lib/cache/cdb_api.h b/lib/cache/cdb_api.h index 23a795d53..4b981a7dd 100644 --- a/lib/cache/cdb_api.h +++ b/lib/cache/cdb_api.h @@ -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) diff --git a/lib/cache/cdb_lmdb.c b/lib/cache/cdb_lmdb.c index f5e56bc64..56746c09f 100644 --- a/lib/cache/cdb_lmdb.c +++ b/lib/cache/cdb_lmdb.c @@ -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. */ diff --git a/lib/rules/api.c b/lib/rules/api.c index 82e3ee769..8b8d01d00 100644 --- a/lib/rules/api.c +++ b/lib/rules/api.c @@ -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); diff --git a/python/knot_resolver/manager/manager.py b/python/knot_resolver/manager/manager.py index dcd14b537..56b13ac35 100644 --- a/python/knot_resolver/manager/manager.py +++ b/python/knot_resolver/manager/manager.py @@ -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)