return NULL;
}
-int kr_gc_cache_iter(knot_db_t * knot_db, kr_gc_iter_callback callback, void *ctx)
+int kr_gc_cache_iter(knot_db_t * knot_db, const kr_cache_gc_cfg_t *cfg,
+ kr_gc_iter_callback callback, void *ctx)
{
#ifdef DEBUG
unsigned int counter_iter = 0;
return KNOT_ERROR;
}
+ int txn_steps = 0;
while (it != NULL) {
knot_db_val_t key = { 0 }, val = { 0 };
ret = api->iter_key(it, &key);
goto error;
}
-#ifdef DEBUG
- counter_iter++;
-#endif
-
info.entry_size = key.len + val.len;
info.valid = false;
const uint16_t *entry_type =
info.no_labels = entry_labels(&key, *entry_type);
}
#ifdef DEBUG
+ counter_iter++;
counter_kr_consistent += info.valid;
if (!entry_type || !entry) { // don't log fully consistent entries
printf
return ret;
}
-skip:
- it = api->iter_next(it);
+ skip: // Advance to the next GC item.
+ if (++txn_steps < cfg->ro_txn_items || !cfg->ro_txn_items/*unlimited*/) {
+ it = api->iter_next(it);
+ } else {
+ /* The transaction has been too long; let's reopen it. */
+ txn_steps = 0;
+ uint8_t key_storage[key.len];
+ memcpy(key_storage, key.data, key.len);
+ key.data = key_storage;
+
+ api->iter_finish(it);
+ api->txn_abort(&txn);
+
+ ret = api->txn_begin(knot_db, &txn, KNOT_DB_RDONLY);
+ if (ret != KNOT_EOK) {
+ printf("Error restarting DB transaction (%s).\n",
+ knot_strerror(ret));
+ return ret;
+ }
+ it = api->iter_begin(&txn, KNOT_DB_NOOP);
+ if (it == NULL) {
+ printf("Error iterating database.\n");
+ api->txn_abort(&txn);
+ return KNOT_ERROR;
+ }
+ it = api->iter_seek(it, &key, KNOT_DB_GEQ);
+ // NULL here means we'we reached the end
+ }
}
api->txn_abort(&txn);
typedef int (*kr_gc_iter_callback)(const knot_db_val_t * key,
gc_record_info_t * info, void *ctx);
-int kr_gc_cache_iter(knot_db_t * knot_db, kr_gc_iter_callback callback, void *ctx);
+int kr_gc_cache_iter(knot_db_t * knot_db, const kr_cache_gc_cfg_t *cfg,
+ kr_gc_iter_callback callback, void *ctx);
const uint16_t *kr_gc_key_consistent(knot_db_val_t key);
gc_timer_start(&timer_analyze);
ctx_compute_categories_t cats = { { 0 }
};
- ret = kr_gc_cache_iter(db, cb_compute_categories, &cats);
+ ret = kr_gc_cache_iter(db, cfg, cb_compute_categories, &cats);
if (ret != KNOT_EOK) {
kr_cache_gc_free_state(state);
return ret;
ctx_delete_categories_t to_del = { 0 };
to_del.cfg_temp_keys_space = cfg->temp_keys_space;
to_del.limit_category = limit_category;
- ret = kr_gc_cache_iter(db, cb_delete_categories, &to_del);
+ ret = kr_gc_cache_iter(db, cfg, cb_delete_categories, &to_del);
if (ret != KNOT_EOK) {
entry_dynarray_deep_free(&to_del.to_delete);
kr_cache_gc_free_state(state);
size_t temp_keys_space; // maximum amount of temporary memory for copied keys in bytes (0 = unlimited)
size_t rw_txn_items; // maximum number of deleted records per RW transaction (0 = unlimited)
+ size_t ro_txn_items; // maximum number of iterated records (RO transactions, 0 = unlimited)
unsigned long rw_txn_duration; // maximum duration of RW transaction in usecs (0 = unlimited)
unsigned long rw_txn_delay; // waiting time between two RW transactions in usecs
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");
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, "hnc:d:l:m:u:f:w:t:")) != -1) {
+ while ((o = getopt(argc, argv, "hnc:d:l:L:m:u:f:w:t:")) != -1) {
switch (o) {
case 'c':
cfg.cache_path = optarg;
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;