/** * Dump the whole in-memory cache onto disk. */ static void dump_cache(bool force) { FILE *f; file_path_t fp; if (!force && !cache_dirty) return; file_path_set(&fp, settings_config_dir(), "sha1_cache"); f = file_config_open_write("SHA-1 cache", &fp); if (f) { struct dump_cache_context ctx; fputs(sha1_persistent_cache_file_header, f); ctx.f = f; ctx.forced = force; hikset_foreach(sha1_cache, dump_cache_one_entry, &ctx); if (file_config_close(f, &fp)) { cache_dirty = FALSE; } } /* * Update the timestamp even on failure to avoid that we retry this * too frequently. */ cache_dumped = tm_time(); }
/** * Callout queue periodic event to perform periodic monitoring of the * registered files. */ static bool watcher_timer(void *unused_udata) { (void) unused_udata; hikset_foreach(monitored, watcher_check_mtime, NULL); return TRUE; /* Keep calling */ }
/** * Shutdown the DHT publisher. */ void G_COLD publisher_close(void) { /* * Purge data we no longer know about from the persisted DB. */ dbmw_foreach_remove(db_pubdata, publisher_remove_orphan, NULL); /* * Final cleanup. */ hikset_foreach(publisher_sha1, free_entry, NULL); hikset_free_null(&publisher_sha1); dbstore_close(db_pubdata, settings_dht_db_dir(), db_pubdata_base); db_pubdata = NULL; cq_free_null(&publish_cq); }
/** * Close local key management. */ G_GNUC_COLD void keys_close(void) { values_close(); dbstore_close(db_keydata, settings_dht_db_dir(), db_keybase); db_keydata = NULL; if (keys) { hikset_foreach(keys, keys_free_kv, NULL); hikset_free_null(&keys); } kuid_atom_free_null(&kball.furthest); kuid_atom_free_null(&kball.closest); gnet_stats_set_general(GNR_DHT_KEYS_HELD, 0); gnet_stats_set_general(GNR_DHT_CACHED_KEYS_HELD, 0); cq_cancel(&kball_ev); cq_periodic_remove(&keys_periodic_ev); cq_periodic_remove(&keys_sync_ev); }
/** * Destroy container, freeing all keys and values, and nullify pointer. */ void aging_destroy(aging_table_t **ag_ptr) { aging_table_t *ag = *ag_ptr; if (ag) { aging_check(ag); aging_synchronize(ag); hikset_foreach(ag->table, aging_free, ag); hikset_free_null(&ag->table); cq_periodic_remove(&ag->gc_ev); if (ag->lock != NULL) { mutex_destroy(ag->lock); WFREE(ag->lock); } ag->magic = 0; WFREE(ag); *ag_ptr = NULL; } }
/* * Offload keys to remote node, as appropriate. * * Firstly we only consider remote nodes whose KUID falls within our k-ball. * * Secondly, we are only considering remote nodes that end-up being in our * routing table (i.e. ones which are close enough to us to get room in the * table, which also means they're not firewalled nor going to shutdown soon). * This is normally ensured by our caller. * * Thirdly, we are only going to consider keys closer to the node than we are * and for which we are the closest among our k-closest nodes, to avoid too * many redundant STORE operations. */ void keys_offload(const knode_t *kn) { struct offload_context ctx; unsigned n; knode_t *kclosest[KDA_K]; /* Our known k-closest nodes */ bool debug; knode_check(kn); if (kn->flags & (KNODE_F_FIREWALLED | KNODE_F_SHUTDOWNING)) return; if ( !dht_bootstrapped() || /* Not bootstrapped */ !keys_within_kball(kn->id) || /* Node KUID outside our k-ball */ 0 == hikset_count(keys) /* No keys held */ ) return; debug = GNET_PROPERTY(dht_storage_debug) > 1 || GNET_PROPERTY(dht_publish_debug) > 1; if (debug) g_debug("DHT preparing key offloading to %s", knode_to_string(kn)); gnet_stats_inc_general(GNR_DHT_KEY_OFFLOADING_CHECKS); ctx.our_kuid = get_our_kuid(); ctx.remote_kuid = kn->id; ctx.found = NULL; ctx.count = 0; /* * We need to have KDA_K closest known alive neighbours in order to * be able to select proper keys to offload. * * Note that we make sure to NOT include the new node in our k-closest set * since it would always be closer than ourselves to keys we wish to * offload to it... */ n = dht_fill_closest(ctx.our_kuid, kclosest, G_N_ELEMENTS(kclosest), ctx.remote_kuid, TRUE); if (n < G_N_ELEMENTS(kclosest)) { if (debug) g_warning("DHT got only %u closest alive nodes, cannot offload", n); return; } /* * Prepare a PATRICIA containing the ID of our k-closest alive nodes * plus ourselves. */ ctx.kclosest = patricia_create(KUID_RAW_BITSIZE); for (n = 0; n < G_N_ELEMENTS(kclosest); n++) { patricia_insert(ctx.kclosest, kclosest[n]->id, kclosest[n]->id); } patricia_insert(ctx.kclosest, ctx.our_kuid, ctx.our_kuid); /* * Select offloading candidate keys. */ hikset_foreach(keys, keys_offload_prepare, &ctx); patricia_destroy(ctx.kclosest); if (debug) { g_debug("DHT found %u/%zu offloading candidate%s", ctx.count, hikset_count(keys), plural(ctx.count)); } if (ctx.count) publish_offload(kn, ctx.found); pslist_free_null(&ctx.found); }
/** * Recreate keyinfo data from persisted information. */ static G_GNUC_COLD void keys_init_keyinfo(void) { struct keys_create_context ctx; if (GNET_PROPERTY(dht_keys_debug)) { size_t count = dbmw_count(db_keydata); g_debug("DHT KEYS scanning %u persisted key%s", (unsigned) count, plural(count)); } ctx.our_kuid = get_our_kuid(); ctx.dbkeys = hset_create(HASH_KEY_FIXED, sizeof(uint64)); dbmw_foreach_remove(db_keydata, reload_ki, &ctx); if (GNET_PROPERTY(dht_keys_debug)) { size_t count = dbmw_count(db_keydata); g_debug("DHT KEYS kept %u key%s, now loading associated values", (unsigned) count, plural(count)); } /* * FIXME: * Unfortunately, we have to reset the keydata database for each of * the keys we're going to recreate, because the logic adding values * back to the key will fill each of the keydata appropriately, and it * expects the amount of values in the keyinfo and the keydata to match. * * Therefore, we need to rename the old keydata database, open a new one, * write empty keydata values in it, then discard the old keydata if * everything goes well. In case of a crash in the middle of the * restoration, we'll be able to recover by opening the old keydata first * if it exists. * * It is far from critical though: if we reset the keydata database and * we crash in the middle of the restore, then we'll lose all the persisted * keys and values and will simply restart with an empty storage. * * Given the contorsions needed to fix that, and the little value for * the user, just leaving a FIXME note. * --RAM, 2012-11-17 */ hikset_foreach(keys, keys_reset_keydata, NULL); values_init_data(ctx.dbkeys); hset_foreach(ctx.dbkeys, keys_free_dbkey, NULL); hset_free_null(&ctx.dbkeys); hikset_foreach_remove(keys, keys_discard_if_empty, NULL); dbmw_foreach_remove(db_keydata, keys_delete_if_empty, NULL); dbstore_compact(db_keydata); g_soft_assert_log(hikset_count(keys) == dbmw_count(db_keydata), "keys reloaded: %zu, key data persisted: %zu", hikset_count(keys), dbmw_count(db_keydata)); if (GNET_PROPERTY(dht_keys_debug)) { g_debug("DHT KEYS reloaded %zu key%s", hikset_count(keys), plural(hikset_count(keys))); } gnet_stats_set_general(GNR_DHT_KEYS_HELD, hikset_count(keys)); }