]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/cache: run check_health() every five seconds
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 19 Aug 2020 09:39:19 +0000 (11:39 +0200)
committerPetr Špaček <petr.spacek@nic.cz>
Mon, 7 Sep 2020 15:47:12 +0000 (17:47 +0200)
... in case of usage from kresd (GC does it a bit differently).

daemon/bindings/cache.c
daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
lib/cache/api.c
lib/cache/api.h
lib/cache/cdb_api.h
lib/cache/cdb_lmdb.c
utils/cache_gc/db.c
utils/cache_gc/kr_cache_gc.c

index 94f8f9b34b1481a64b0dff036e26155cabb6d8e2..49cb978a207f92966eb3fffb918f71e4726d6a2e 100644 (file)
@@ -206,6 +206,13 @@ static int cache_open(lua_State *L)
                return luaL_error(L, "can't open cache path '%s'; working directory '%s'; %s",
                                  opts.path, cwd, kr_strerror(ret));
        }
+       /* Let's check_health() every five seconds to avoid keeping old cache alive
+        * even in case of not having any work to do. */
+       ret = kr_cache_check_health(&engine->resolver.cache, 5000);
+       if (ret != 0) {
+               kr_log_error("[cache] periodic health check failed (ignored): %s\n",
+                               kr_strerror(ret));
+       }
 
        /* Store current configuration */
        lua_getglobal(L, "cache");
index 9dc6efb9562ce47711afeb0de9b3aae917e37b85..b72ba66dd5c1d8d2a043b63b44b251293d29de9f 100644 (file)
@@ -213,6 +213,7 @@ struct kr_cdb_stats {
        uint64_t read_leq_miss;
        double usage_percent;
 };
+typedef struct uv_timer_s uv_timer_t;
 struct kr_cache {
        knot_db_t *db;
        const struct kr_cdb_api *api;
@@ -221,6 +222,7 @@ struct kr_cache {
        uint32_t ttl_max;
        struct timeval checkpoint_walltime;
        uint64_t checkpoint_monotime;
+       uv_timer_t *health_timer;
 };
 typedef struct kr_layer {
        int state;
index d2300c5c0a0d447ee35a697e04e610119687f990..13070335230d120048bfabebe0ca110bdcea2769 100755 (executable)
@@ -115,6 +115,7 @@ ${CDEFS} ${LIBKRES} types <<-EOF
        struct kr_request
        enum kr_rank
        struct kr_cdb_stats
+       typedef uv_timer_t
        struct kr_cache
        # lib/layer.h
        kr_layer_t
index 145afb12688f22b78a2a863714078cb2495b67d7..cb0ff392c1777979d4e64cf5bb6db83c9db3a619 100644 (file)
@@ -15,6 +15,8 @@
 #include <libknot/errcode.h>
 #include <libknot/rrtype/rrsig.h>
 
+#include <uv.h>
+
 #include "contrib/base32hex.h"
 #include "contrib/cleanup.h"
 #include "contrib/ucw/lib.h"
@@ -105,14 +107,15 @@ static int assert_right_version(struct kr_cache *cache)
 int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct kr_cdb_opts *opts, knot_mm_t *mm)
 {
        if (!cache) {
+               assert(cache);
                return kr_error(EINVAL);
        }
+       memset(cache, 0, sizeof(*cache));
        /* Open cache */
        if (!api) {
                api = kr_cdb_lmdb();
        }
        cache->api = api;
-       memset(&cache->stats, 0, sizeof(cache->stats));
        int ret = cache->api->open(&cache->db, &cache->stats, opts, mm);
        if (ret != 0) {
                return ret;
@@ -140,6 +143,7 @@ const char *kr_cache_emergency_file_to_remove = NULL;
 
 void kr_cache_close(struct kr_cache *cache)
 {
+       kr_cache_check_health(cache, -1);
        if (cache_isvalid(cache)) {
                cache_op(cache, close);
                cache->db = NULL;
@@ -941,3 +945,46 @@ cleanup:
        return ret;
 }
 
+static void health_timer_cb(uv_timer_t *health_timer)
+{
+       struct kr_cache *cache = health_timer->data;
+       if (cache)
+               cache_op(cache, check_health);
+       /* We don't do anything with the return code.  For example, in some situations
+        * the file may not exist (temporarily), and we just expect to be more lucky
+        * when the timer fires again. */
+}
+
+int kr_cache_check_health(struct kr_cache *cache, int interval)
+{
+       if (interval == 0) {
+               return cache_op(cache, check_health);
+       }
+       if (interval < 0) {
+               if (!cache->health_timer)
+                       return kr_ok(); // tolerate stopping a "stopped" timer
+               uv_close((uv_handle_t *)cache->health_timer, (uv_close_cb)free);
+               cache->health_timer->data = NULL;
+               cache->health_timer = NULL;
+               return kr_ok();
+       }
+
+       assert(interval > 0);
+       if (!cache->health_timer) {
+               /* We avoid depending on daemon's symbols by using uv_default_loop. */
+               cache->health_timer = malloc(sizeof(*cache->health_timer));
+               if (!cache->health_timer) return kr_error(ENOMEM);
+               uv_loop_t *loop = uv_default_loop();
+               assert(loop);
+               int ret = uv_timer_init(loop, cache->health_timer);
+               if (ret) {
+                       free(cache->health_timer);
+                       cache->health_timer = NULL;
+                       return kr_error(ret);
+               }
+               cache->health_timer->data = cache;
+       }
+       assert(cache->health_timer->data);
+       return kr_error(uv_timer_start(cache->health_timer, health_timer_cb, interval, interval));
+}
+
index 85bf8d90ae2c89b234b30c304548a722e82e1683..e0786638d092378adb8b6e7304614572f282ce65 100644 (file)
@@ -34,6 +34,8 @@ struct kr_cache
        /* A pair of stamps for detection of real-time shifts during runtime. */
        struct timeval checkpoint_walltime; /**< Wall time on the last check-point. */
        uint64_t checkpoint_monotime; /**< Monotonic milliseconds on the last check-point. */
+
+       uv_timer_t *health_timer; /**< Timer used for kr_cache_check_health() */
 };
 
 /**
@@ -182,3 +184,10 @@ int kr_cache_closest_apex(struct kr_cache *cache, const knot_dname_t *name, bool
  */
 KR_EXPORT
 int kr_unpack_cache_key(knot_db_val_t key, knot_dname_t *buf, uint16_t *type);
+
+/** Periodic kr_cdb_api::check_health().
+ * @param interval in milliseconds.  0 for one-time check, -1 to stop the checks.
+ * @return see check_health() for one-time check; otherwise normal kr_error() code. */
+KR_EXPORT
+int kr_cache_check_health(struct kr_cache *cache, int interval);
+
index f1bb5de913c0238bfe7282fc543b675d6f116d01..935945f4261c5a75d06fb824d40b5cde0206c8a2 100644 (file)
@@ -81,7 +81,7 @@ struct kr_cdb_api {
 
        /** Perform maintenance.
         * In LMDB case it checks whether data.mdb is still the same
-        * and reopens it if it isn't.
+        * and reopens it if it isn't; it errors out if the file doesn't exist anymore.
         * \return 0 if OK, 1 if reopened OK, < 0 kr_error */
        int (*check_health)(knot_db_t *db, struct kr_cdb_stats *stat);
 };
index c955b3bfb0d0c39491f4edf4f106ee3ee057c55f..073305671d6d584956245dc3a160fe299b809c9a 100644 (file)
@@ -17,7 +17,7 @@
 #include "lib/cache/cdb_lmdb.h"
 #include "lib/cache/cdb_api.h"
 #include "lib/cache/api.h"
-#include <lib/cache/impl.h>
+#include "lib/cache/impl.h"
 #include "lib/utils.h"
 
 
@@ -438,7 +438,6 @@ static int cdb_check_health(knot_db_t *db, struct kr_cdb_stats *stats)
        if (stat(env->mdb_data_path, &st)) {
                int ret = errno;
                return kr_error(ret);
-               // FIXME: if the file doesn't exist?
        }
        if (st.st_dev == env->st_dev && st.st_ino == env->st_ino) {
                if (st.st_size == env->st_size)
index 2da8136cfeb203d3b8bdb47fe2cf969a23be3c96..c71fabf60e152909d5b35fc1010fe7552a0f7db0 100644 (file)
@@ -45,7 +45,7 @@ int kr_gc_cache_open(const char *cache_path, struct kr_cache *kres_db,
 
 int kr_gc_cache_check_health(struct kr_cache *kres_db, knot_db_t ** libknot_db)
 {
-       int ret = kr_cdb_lmdb()->check_health(kres_db->db, &kres_db->stats);
+       int ret = kr_cache_check_health(kres_db, 0);
        if (ret == 0) {
                return 0;
        } else if (ret != 1) {
index b4ef97993315fee51ad69f77d4382cb105cae25b..1c0e376c439692af38ee64b142024ac8fb1ca80b 100644 (file)
@@ -172,6 +172,7 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state)
                                           &(*state)->db);
        } else { // To be sure, we guard against the file getting replaced.
                ret = kr_gc_cache_check_health(&(*state)->kres_db, &(*state)->db);
+               // In particular, missing data.mdb gives us kr_error(ENOENT) == KNOT_ENOENT
        }
        if (ret) {
                free(*state);