Commit 68812887 authored by Marek Vavruša's avatar Marek Vavruša

Merge branch 'mod_cachectl'

parents 9cf872fb b8161b4a
......@@ -28,7 +28,9 @@
#include "lib/cache.h"
#include "lib/defines.h"
#define db_api namedb_lmdb_api()
/** Used cache storage engine (default LMDB) */
const namedb_api_t *(*kr_cache_storage)(void) = namedb_lmdb_api;
#define db_api kr_cache_storage()
namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize)
{
......@@ -232,22 +234,3 @@ int kr_cache_clear(namedb_txn_t *txn)
return db_api->clear(txn);
}
int kr_cache_prune(namedb_txn_t *txn, uint32_t timestamp)
{
/* Whole cache sweep is not feasible as we don't have a list of items sorted
* by age nor any sort of LRU/MRU, completely random replace is not possible
* as well.
* - The LMDB also can't delete items when the MAPSIZE is reached.
* - So we're probably need to iteratively scan the LMDB and prune aged
* items.
* - This is not ideal, because queries won't be able to write to cache
* until at least some entry ages out.
* - Idea - make poor man's LRU with two databases doing following:
* - Fill up 1, mark that it's unwritable
* - Fill up 2, mark that it's unwritable
* - Clear 1, all writes will now go in there
* - This gives us LR(written) with resolution 2
*/
return KNOT_EOK;
}
......@@ -32,6 +32,9 @@ struct kr_cache_rrset
uint8_t data[];
};
/** Used storage API for cache (default LMDB) */
extern const namedb_api_t *(*kr_cache_storage)(void);
/**
* Open/create persistent cache in given path.
* @param handle Path to existing directory where the DB should be created.
......@@ -115,12 +118,4 @@ int kr_cache_remove(namedb_txn_t *txn, const knot_rrset_t *rr);
*/
int kr_cache_clear(namedb_txn_t *txn);
/**
* Clear aged items from the database.
* @param txn transaction instance
* @param timestamp current time
* @return KNOT_E*
*/
int kr_cache_prune(namedb_txn_t *txn, uint32_t timestamp);
/** @} */
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Partial sweep is not feasible as we don't have a list of items sorted
* by age nor any sort of LRU/MRU, completely random replace is not possible
* as well.
* - Idea - make poor man's LRU with two databases doing following:
* - Fill up 1, mark that it's unwritable
* - Fill up 2, mark that it's unwritable
* - Clear 1, all writes will now go in there
* - This gives us LR(written) with resolution 2
*/
#include <time.h>
#include "lib/module.h"
#include "lib/context.h"
#include "lib/cache.h"
/*
* Properties.
*/
/**
* Return number of cached records.
*
* Input: N/A
* Output: { result: [int] size }
*
*/
static char* get_size(struct kr_context *ctx, struct kr_module *module, const char *args)
{
char *result = NULL;
const namedb_api_t *storage = kr_cache_storage();
/* Fetch item count */
namedb_txn_t txn;
int ret = kr_cache_txn_begin(ctx->cache, &txn, NAMEDB_RDONLY);
if (ret == 0) {
asprintf(&result, "{ \"result\": %d }", storage->count(&txn));
kr_cache_txn_abort(&txn);
}
return result;
}
/** Return boolean true if a record in the RR set is expired. */
static int is_expired(struct kr_cache_rrset *rr, uint32_t drift)
{
/* Initialize set. */
knot_rdataset_t rrs;
rrs.rr_count = rr->count;
rrs.data = rr->data;
for (unsigned i = 0; i < rrs.rr_count; ++i) {
const knot_rdata_t *rd = knot_rdataset_at(&rrs, i);
if (knot_rdata_ttl(rd) <= drift) {
return 1;
}
}
return 0;
}
/**
* Prune expired/invalid records.
*
* Input: N/A
* Output: { result: [int] nr_pruned }
*
*/
static char* prune(struct kr_context *ctx, struct kr_module *module, const char *args)
{
const namedb_api_t *storage = kr_cache_storage();
namedb_txn_t txn;
int ret = kr_cache_txn_begin(ctx->cache, &txn, 0);
if (ret != 0) {
return NULL;
}
/* Iterate cache and find expired records. */
int pruned = 0;
uint32_t now = time(NULL);
namedb_iter_t *it = storage->iter_begin(&txn, 0);
while (it) {
/* Fetch RR from cache */
namedb_val_t key, val;
if (storage->iter_key(it, &key) != 0 ||
storage->iter_val(it, &val)) {
break;
}
/* Prune expired records. */
struct kr_cache_rrset *rr = val.data;
if (is_expired(rr, now - rr->timestamp)) {
storage->del(&txn, &key);
pruned += 1;
}
it = storage->iter_next(it);
}
/* Commit and format result. */
char *result = NULL;
if (kr_cache_txn_commit(&txn) != 0) {
asprintf(&result, "{ \"result\": %d, \"error\": \"%s\" }", pruned, knot_strerror(ret));
} else {
asprintf(&result, "{ \"result\": %d }", pruned);
}
return result;
}
/**
* Clear all records.
*
* Input: N/A
* Output: { result: [bool] success }
*
*/
static char* clear(struct kr_context *ctx, struct kr_module *module, const char *args)
{
namedb_txn_t txn;
int ret = kr_cache_txn_begin(ctx->cache, &txn, 0);
if (ret != 0) {
return NULL;
}
/* Clear cache and commit. */
ret = kr_cache_clear(&txn);
if (ret == 0) {
ret = kr_cache_txn_commit(&txn);
} else {
kr_cache_txn_abort(&txn);
}
char *result = NULL;
asprintf(&result, "{ \"result\": %s }", ret == 0 ? "true" : "false");
return result;
}
/*
* Module implementation.
*/
struct kr_prop *cachectl_props(void)
{
static struct kr_prop prop_list[] = {
{ &get_size, "size", "Return number of cached records.", },
{ &prune, "prune", "Prune expired/invalid records.", },
{ &clear, "clear", "Clear all cache records.", },
{ NULL, NULL, NULL }
};
return prop_list;
}
KR_MODULE_EXPORT(cachectl);
cachectl_SOURCES := modules/cachectl/cachectl.c
cachectl_DEPEND := libkresolve
cachectl_LIBS := $(libkresolve_TARGET) $(libkresolve_LIBS)
$(call make_c_module,cachectl)
\ No newline at end of file
# List of built-in modules
modules_TARGETS := hints
modules_TARGETS := hints \
cachectl
# List of Golang modules
$(eval $(call find_bin,gccgo))
......@@ -41,4 +42,4 @@ endef
modules: $(modules_TARGETS)
modules-clean: $(addsuffix -clean,$(modules_TARGETS))
modules-install: $(addsuffix -install,$(modules_TARGETS))
$(foreach module,$(modules_TARGETS),$(eval include modules/$(module)/$(module).mk))
\ No newline at end of file
$(foreach module,$(modules_TARGETS),$(eval include modules/$(module)/$(module).mk))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment