... in case of usage from kresd (GC does it a bit differently).
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");
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;
uint32_t ttl_max;
struct timeval checkpoint_walltime;
uint64_t checkpoint_monotime;
+ uv_timer_t *health_timer;
};
typedef struct kr_layer {
int state;
struct kr_request
enum kr_rank
struct kr_cdb_stats
+ typedef uv_timer_t
struct kr_cache
# lib/layer.h
kr_layer_t
#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"
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;
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;
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));
+}
+
/* 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() */
};
/**
*/
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);
+
/** 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);
};
#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"
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)
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) {
&(*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);