From 06acb57936825c0e017cc87aec96bef4c66f302f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Fri, 2 Feb 2018 18:07:18 +0100 Subject: [PATCH] detect_time_jump: don't clear cache on suspend-resume This changes more time than anticipated, as the old naming didn't apply anymore (time of last cache clear). --- NEWS | 4 +++ daemon/bindings.c | 34 ++++++++++++------- lib/cache/api.c | 11 ++---- lib/cache/api.h | 14 ++++++-- modules/detect_time_jump/README.rst | 12 +++++-- modules/detect_time_jump/detect_time_jump.lua | 13 ++++--- 6 files changed, 57 insertions(+), 31 deletions(-) diff --git a/NEWS b/NEWS index ac6b30a70..8fb5cc639 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ + +- detect_time_jump module: don't clear cache on suspend-resume (#284) + + Knot Resolver 2.0.0 (2018-01-31) ================================ diff --git a/daemon/bindings.c b/daemon/bindings.c index bac13c2f4..bdde5dfa5 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -731,20 +731,30 @@ static int cache_count(lua_State *L) return 0; } -/** Return time of last cache clear */ -static int cache_last_clear(lua_State *L) +/** Return time of last checkpoint, or re-set it if passed `true`. */ +static int cache_checkpoint(lua_State *L) { struct engine *engine = engine_luaget(L); struct kr_cache *cache = &engine->resolver.cache; - lua_newtable(L); - lua_pushnumber(L, cache->last_clear_monotime); - lua_setfield(L, -2, "monotime"); - lua_newtable(L); - lua_pushnumber(L, cache->last_clear_walltime.tv_sec); - lua_setfield(L, -2, "sec"); - lua_pushnumber(L, cache->last_clear_walltime.tv_usec); - lua_setfield(L, -2, "usec"); - lua_setfield(L, -2, "walltime"); + + if (lua_gettop(L) == 0) { /* Return the current value. */ + lua_newtable(L); + lua_pushnumber(L, cache->checkpoint_monotime); + lua_setfield(L, -2, "monotime"); + lua_newtable(L); + lua_pushnumber(L, cache->checkpoint_walltime.tv_sec); + lua_setfield(L, -2, "sec"); + lua_pushnumber(L, cache->checkpoint_walltime.tv_usec); + lua_setfield(L, -2, "usec"); + lua_setfield(L, -2, "walltime"); + return 1; + } + + if (lua_gettop(L) != 1 || !lua_isboolean(L, 1) || !lua_toboolean(L, 1)) { + format_error(L, "cache.checkpoint() takes no parameters or a true value"); + lua_error(L); + } + kr_cache_make_checkpoint(cache); return 1; } @@ -1113,7 +1123,7 @@ int lib_cache(lua_State *L) { "backends", cache_backends }, { "count", cache_count }, { "stats", cache_stats }, - { "last_clear", cache_last_clear }, + { "checkpoint", cache_checkpoint }, { "open", cache_open }, { "close", cache_close }, { "prune", cache_prune }, diff --git a/lib/cache/api.c b/lib/cache/api.c index e0eb6c06f..4cebca665 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -64,13 +64,6 @@ static inline int cache_clear(struct kr_cache *cache) return cache_op(cache, clear); } -/** @internal Set time when clearing cache. */ -static void reset_timestamps(struct kr_cache *cache) -{ - cache->last_clear_monotime = kr_now(); - gettimeofday(&cache->last_clear_walltime, NULL); -} - /** @internal Open cache db transaction and check internal data version. */ static int assert_right_version(struct kr_cache *cache) { @@ -129,7 +122,7 @@ int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct k cache->ttl_min = KR_CACHE_DEFAULT_TTL_MIN; cache->ttl_max = KR_CACHE_DEFAULT_TTL_MAX; /* Check cache ABI version */ - reset_timestamps(cache); + kr_cache_make_checkpoint(cache); (void) assert_right_version(cache); return 0; } @@ -163,7 +156,7 @@ int kr_cache_clear(struct kr_cache *cache) } int ret = cache_clear(cache); if (ret == 0) { - reset_timestamps(cache); + kr_cache_make_checkpoint(cache); ret = assert_right_version(cache); } return ret; diff --git a/lib/cache/api.h b/lib/cache/api.h index 40f24730c..11f9d3b45 100644 --- a/lib/cache/api.h +++ b/lib/cache/api.h @@ -48,8 +48,10 @@ struct kr_cache } stats; uint32_t ttl_min, ttl_max; /**< TTL limits */ - struct timeval last_clear_walltime; /**< Time of last cache clear */ - uint64_t last_clear_monotime; /**< Last cache clear in monotonic milliseconds */ + + /* 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. */ }; /** @@ -83,6 +85,14 @@ static inline bool kr_cache_is_open(struct kr_cache *cache) return cache->db != NULL; } +/** (Re)set the time pair to the current values. */ +static inline void kr_cache_make_checkpoint(struct kr_cache *cache) +{ + cache->checkpoint_monotime = kr_now(); + gettimeofday(&cache->checkpoint_walltime, NULL); +} + + /** * Clear all items from the cache. * @param cache cache structure diff --git a/modules/detect_time_jump/README.rst b/modules/detect_time_jump/README.rst index da92630b7..78932ac73 100644 --- a/modules/detect_time_jump/README.rst +++ b/modules/detect_time_jump/README.rst @@ -4,11 +4,17 @@ Detect discontinuous jumps in the system time --------------------------------------------- This module detect discontinuous jumps in the system time when resolver -is running. It clears cache when some time jumps occurs. +is running. It clears cache when a significant backward time jumps occurs. -Time jumps is ussualy created by NTP time change or by admin intervention. -These change can affect cache records as they store timestamp and TTL in real +Time jumps are usualy created by NTP time change or by admin intervention. +These change can affect cache records as they store timestamp and TTL in real time. If you want to preserve cache during time travel you should disable this module by ``modules.unload('detect_time_jump')``. + +Due to the way monotonic system time works on typical systems, +suspend-resume cycles will be perceived as a foward time jumps, +but this direction of shift does not have the risk of using records +beyond their intended TTL, so forward jumps do not cause erasing the cache. + diff --git a/modules/detect_time_jump/detect_time_jump.lua b/modules/detect_time_jump/detect_time_jump.lua index e236f0686..d8b3dabe4 100644 --- a/modules/detect_time_jump/detect_time_jump.lua +++ b/modules/detect_time_jump/detect_time_jump.lua @@ -10,15 +10,18 @@ local event_id = nil -- time. In ideal case these differences should be almost same. -- If they differ more than mod.threshold value then clear cache. local function check_time() - local clear_time = cache.last_clear() - local cache_timeshift = clear_time.walltime.sec * 1000 - clear_time.monotime + local checkpoint = cache.checkpoint() + local cache_timeshift = checkpoint.walltime.sec * 1000 - checkpoint.monotime local actual_timeshift = os.time() * 1000 - tonumber(ffi.C.kr_now()) - local time_diff = math.abs(cache_timeshift - actual_timeshift) - if time_diff > mod.threshold then - log("Detected time change, clearing cache\n" .. + local jump_backward = cache_timeshift - actual_timeshift + if jump_backward > mod.threshold then + log("Detected backwards time jump, clearing cache.\n" .. "But what does that mean? It means your future hasn't been written yet." ) cache.clear() + elseif -jump_backward > mod.threshold then + log("Detected forward time jump. (Suspend-resume, possibly.)") + cache.checkpoint(true) end end -- 2.47.2