summaryrefslogtreecommitdiffstats
path: root/utils/cache_gc/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/cache_gc/main.c')
-rw-r--r--utils/cache_gc/main.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/utils/cache_gc/main.c b/utils/cache_gc/main.c
new file mode 100644
index 0000000..5adf19f
--- /dev/null
+++ b/utils/cache_gc/main.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "lib/defines.h"
+#include "lib/utils.h"
+#include <libknot/libknot.h>
+#include <lmdb.h>
+
+#include "kresconfig.h"
+#include "kr_cache_gc.h"
+
+static volatile int killed = 0;
+
+static void got_killed(int signum)
+{
+ (void)signum;
+ switch (++killed) {
+ case 1:
+ break;
+ case 2:
+ exit(5);
+ break;
+ case 3:
+ abort();
+ default:
+ kr_assert(false);
+ }
+}
+
+static void print_help(void)
+{
+ printf("Usage: kr_cache_gc -c <resolver_cache> [ optional params... ]\n");
+ printf("Optional params:\n");
+ printf(" -d <garbage_interval(millis)>\n");
+ printf(" -l <deletes_per_txn>\n");
+ printf(" -L <reads_per_txn>\n");
+ printf(" -m <rw_txn_duration(usecs)>\n");
+ printf(" -u <cache_max_usage(percent)>\n");
+ printf(" -f <cache_to_be_freed(percent-of-current-usage)>\n");
+ printf(" -w <wait_next_rw_txn(usecs)>\n");
+ printf(" -t <temporary_memory(MBytes)>\n");
+ printf(" -n (= dry run)\n");
+}
+
+static long get_nonneg_optarg(void)
+{
+ char *end;
+ const long result = strtol(optarg, &end, 10);
+ if (result >= 0 && end && *end == '\0')
+ return result;
+ // not OK
+ print_help();
+ exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+ printf("Knot Resolver Cache Garbage Collector, version %s\n", PACKAGE_VERSION);
+ if (setvbuf(stdout, NULL, _IONBF, 0) || setvbuf(stderr, NULL, _IONBF, 0)) {
+ fprintf(stderr, "Failed to to set output buffering (ignored): %s\n",
+ strerror(errno));
+ fflush(stderr);
+ }
+
+ signal(SIGTERM, got_killed);
+ signal(SIGKILL, got_killed);
+ signal(SIGPIPE, got_killed);
+ signal(SIGCHLD, got_killed);
+ signal(SIGINT, got_killed);
+
+ kr_cache_gc_cfg_t cfg = {
+ .ro_txn_items = 200,
+ .rw_txn_items = 100,
+ .cache_max_usage = 80,
+ .cache_to_be_freed = 10
+ };
+
+ int o;
+ while ((o = getopt(argc, argv, "hnvc:d:l:L:m:u:f:w:t:")) != -1) {
+ switch (o) {
+ case 'c':
+ cfg.cache_path = optarg;
+ break;
+ case 'd':
+ cfg.gc_interval = get_nonneg_optarg();
+ cfg.gc_interval *= 1000;
+ break;
+ case 'l':
+ cfg.rw_txn_items = get_nonneg_optarg();
+ break;
+ case 'L':
+ cfg.ro_txn_items = get_nonneg_optarg();
+ break;
+ case 'm':
+ cfg.rw_txn_duration = get_nonneg_optarg();
+ break;
+ case 'u':
+ cfg.cache_max_usage = get_nonneg_optarg();
+ break;
+ case 'f':
+ cfg.cache_to_be_freed = get_nonneg_optarg();
+ break;
+ case 'w':
+ cfg.rw_txn_delay = get_nonneg_optarg();
+ break;
+ case 't':
+ cfg.temp_keys_space = get_nonneg_optarg();
+ cfg.temp_keys_space *= 1048576;
+ break;
+ case 'n':
+ cfg.dry_run = true;
+ break;
+ case 'v':
+ kr_log_level_set(LOG_DEBUG);
+ break;
+ case ':':
+ case '?':
+ case 'h':
+ print_help();
+ return 1;
+ default:
+ kr_assert(false);
+ }
+ }
+
+ if (cfg.cache_path == NULL) {
+ print_help();
+ return 1;
+ }
+
+ int exit_code = 0;
+ kr_cache_gc_state_t *gc_state = NULL;
+ bool last_espace = false;
+ do {
+ int ret = kr_cache_gc(&cfg, &gc_state);
+
+ /* Let's tolerate ESPACE unless twice in a row. */
+ if (ret == KNOT_ESPACE) {
+ if (!last_espace)
+ ret = KNOT_EOK;
+ last_espace = true;
+ } else {
+ last_espace = false;
+ }
+
+ // ENOENT: kresd may not be started yet or cleared the cache now
+ // MDB_MAP_RESIZED: GC bailed out but on next iteration it should be OK
+ if (ret && ret != KNOT_ENOENT && ret != kr_error(MDB_MAP_RESIZED)) {
+ printf("Error (%s)\n", knot_strerror(ret));
+ exit_code = 10;
+ break;
+ }
+
+ usleep(cfg.gc_interval);
+ } while (cfg.gc_interval > 0 && !killed);
+
+ kr_cache_gc_free_state(&gc_state);
+
+ return exit_code;
+}