static inline void hash_list_regression(const hash_list_t * const hl) { g_assert(NULL != hl->ht); g_assert(elist_count(&hl->list) == hikset_count(hl->ht)); g_assert(elist_count(&hl->list) == elist_length(elist_first(&hl->list))); }
/** * @return amount of entries held in aging table. */ size_t aging_count(const aging_table_t *ag) { size_t count; aging_check(ag); aging_synchronize(ag); count = hikset_count(ag->table); aging_return(ag, count); }
/** * Callout queue periodic event for request load updates. * Also reclaims dead keys holding no values. */ static bool keys_periodic_load(void *unused_obj) { struct load_ctx ctx; (void) unused_obj; ctx.values = 0; ctx.now = tm_time(); hikset_foreach_remove(keys, keys_update_load, &ctx); g_assert(values_count() == ctx.values); if (GNET_PROPERTY(dht_storage_debug)) { size_t keys_count = hikset_count(keys); g_debug("DHT holding %zu value%s spread over %zu key%s", ctx.values, plural(ctx.values), keys_count, plural(keys_count)); } return TRUE; /* Keep calling */ }
/** * Periodic garbage collecting routine. */ static bool aging_gc(void *obj) { aging_table_t *ag = obj; time_t now = tm_time(); struct aging_value *aval; aging_check(ag); aging_synchronize(ag); g_assert(elist_count(&ag->list) == hikset_count(ag->table)); while (NULL != (aval = elist_head(&ag->list))) { if (delta_time(now, aval->last_insert) <= ag->delay) break; /* List is sorted, oldest items first */ hikset_remove(ag->table, aval->key); aging_free(aval, ag); } aging_return(ag, TRUE); /* Keep calling */ }
/* * 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)); }