gossipd: use htable, not linked list for peers.

This speeds up nodeid lookups, which is useful for the next simplification.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2023-06-30 04:53:17 +09:30
parent 9bc1a020d0
commit 01670d5e5e
13 changed files with 108 additions and 62 deletions

View File

@ -39,9 +39,6 @@ struct gossip_store {
* should it be needed */
struct routing_state *rstate;
/* This is daemon->peers for handling to update_peers_broadcast_index */
struct list_head *peers;
/* Disable compaction if we encounter an error during a prior
* compaction */
bool disable_compaction;
@ -263,8 +260,7 @@ close_old:
return 0;
}
struct gossip_store *gossip_store_new(struct routing_state *rstate,
struct list_head *peers)
struct gossip_store *gossip_store_new(struct routing_state *rstate)
{
struct gossip_store *gs = tal(rstate, struct gossip_store);
gs->count = gs->deleted = 0;
@ -278,7 +274,6 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate,
gs->rstate = rstate;
gs->disable_compaction = false;
gs->len = sizeof(gs->version);
gs->peers = peers;
tal_add_destructor(gs, gossip_store_destroy);

View File

@ -16,8 +16,7 @@
struct gossip_store;
struct routing_state;
struct gossip_store *gossip_store_new(struct routing_state *rstate,
struct list_head *peers);
struct gossip_store *gossip_store_new(struct routing_state *rstate);
/**
* Load the initial gossip store, if any.

View File

@ -37,6 +37,16 @@
#include <gossipd/seeker.h>
#include <sodium/crypto_aead_chacha20poly1305.h>
const struct node_id *peer_node_id(const struct peer *peer)
{
return &peer->id;
}
bool peer_node_id_eq(const struct peer *peer, const struct node_id *node_id)
{
return node_id_eq(&peer->id, node_id);
}
/*~ A channel consists of a `struct half_chan` for each direction, each of
* which has a `flags` word from the `channel_update`; bit 1 is
* ROUTING_FLAGS_DISABLED in the `channel_update`. But we also keep a local
@ -83,8 +93,8 @@ static void destroy_peer(struct peer *peer)
{
struct node *node;
/* Remove it from the peers list */
list_del_from(&peer->daemon->peers, &peer->list);
/* Remove it from the peers table */
peer_node_id_map_del(peer->daemon->peers, peer);;
/* If we have a channel with this peer, disable it. */
node = get_node(peer->daemon->rstate, &peer->id);
@ -95,12 +105,7 @@ static void destroy_peer(struct peer *peer)
/* Search for a peer. */
struct peer *find_peer(struct daemon *daemon, const struct node_id *id)
{
struct peer *peer;
list_for_each(&daemon->peers, peer, list)
if (node_id_eq(&peer->id, id))
return peer;
return NULL;
return peer_node_id_map_get(daemon->peers, id);
}
/* Increase a peer's gossip_counter, if peer not NULL */
@ -486,8 +491,8 @@ static void connectd_new_peer(struct daemon *daemon, const u8 *msg)
peer->range_replies = NULL;
peer->query_channel_range_cb = NULL;
/* We keep a list so we can find peer by id */
list_add_tail(&peer->daemon->peers, &peer->list);
/* We keep a htable so we can find peer by id */
peer_node_id_map_add(daemon->peers, peer);
tal_add_destructor(peer, destroy_peer);
node = get_node(daemon->rstate, &peer->id);
@ -754,26 +759,26 @@ static void gossip_disable_local_channels(struct daemon *daemon)
local_disable_chan(daemon, c, half_chan_idx(local_node, c));
}
struct peer *random_peer(struct daemon *daemon,
bool (*check_peer)(const struct peer *peer))
struct peer *first_random_peer(struct daemon *daemon,
struct peer_node_id_map_iter *it)
{
u64 target = UINT64_MAX;
struct peer *best = NULL, *i;
return peer_node_id_map_pick(daemon->peers, pseudorand_u64(), it);
}
/* Reservoir sampling */
list_for_each(&daemon->peers, i, list) {
u64 r;
struct peer *next_random_peer(struct daemon *daemon,
const struct peer *first,
struct peer_node_id_map_iter *it)
{
struct peer *p;
if (!check_peer(i))
continue;
p = peer_node_id_map_next(daemon->peers, it);
if (!p)
p = peer_node_id_map_first(daemon->peers, it);
r = pseudorand_u64();
if (r <= target) {
best = i;
target = r;
}
}
return best;
/* Full circle? */
if (p == first)
return NULL;
return p;
}
/* This is called when lightningd or connectd closes its connection to
@ -808,7 +813,6 @@ static void gossip_init(struct daemon *daemon, const u8 *msg)
daemon->rstate = new_routing_state(daemon,
&daemon->id,
&daemon->peers,
&daemon->timers,
take(dev_gossip_time),
dev_fast_gossip,
@ -887,6 +891,7 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg)
memleak_ptr(memtable, msg);
/* Now delete daemon and those which it has pointers to. */
memleak_scan_obj(memtable, daemon);
memleak_scan_htable(memtable, &daemon->peers->raw);
found_leak = dump_memleak(memtable, memleak_status_broken);
daemon_conn_send(daemon->master,
@ -1163,7 +1168,8 @@ int main(int argc, char *argv[])
subdaemon_setup(argc, argv);
daemon = tal(NULL, struct daemon);
list_head_init(&daemon->peers);
daemon->peers = tal(daemon, struct peer_node_id_map);
peer_node_id_map_init(daemon->peers);
daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0);
daemon->node_announce_timer = NULL;
daemon->node_announce_regen_timer = NULL;

View File

@ -14,19 +14,29 @@
#define CONNECTD2_FD 5
struct chan;
struct peer;
struct channel_update_timestamps;
struct broadcastable;
struct lease_rates;
struct seeker;
struct dying_channel;
/* Helpers for htable */
const struct node_id *peer_node_id(const struct peer *peer);
bool peer_node_id_eq(const struct peer *peer, const struct node_id *node_id);
/* Defines struct peer_node_id_map */
HTABLE_DEFINE_TYPE(struct peer,
peer_node_id, node_id_hash, peer_node_id_eq,
peer_node_id_map);
/*~ The core daemon structure: */
struct daemon {
/* Who am I? Helps us find ourself in the routing map. */
struct node_id id;
/* Peers we are gossiping to: id is unique */
struct list_head peers;
struct peer_node_id_map *peers;
/* Current blockheight: 0 means we're not up-to-date. */
u32 current_blockheight;
@ -127,9 +137,14 @@ struct peer *find_peer(struct daemon *daemon, const struct node_id *id);
/* This peer (may be NULL) gave is valid gossip. */
void peer_supplied_good_gossip(struct peer *peer, size_t amount);
/* Pick a random peer which passes check_peer */
struct peer *random_peer(struct daemon *daemon,
bool (*check_peer)(const struct peer *peer));
/* Get a random peer. NULL if no peers. */
struct peer *first_random_peer(struct daemon *daemon,
struct peer_node_id_map_iter *it);
/* Get another... return NULL when we're back at frist. */
struct peer *next_random_peer(struct daemon *daemon,
const struct peer *first,
struct peer_node_id_map_iter *it);
/* Queue a gossip message for the peer: the subdaemon on the other end simply
* forwards it to the peer. */

View File

@ -1052,17 +1052,16 @@ static bool maybe_send_query_responses_peer(struct peer *peer)
void maybe_send_query_responses(struct daemon *daemon)
{
/* Rotate through, so we don't favor a single peer. */
struct list_head used;
struct peer *p;
struct peer *first, *p;
struct peer_node_id_map_iter it;
list_head_init(&used);
while ((p = list_pop(&daemon->peers, struct peer, list)) != NULL) {
list_add(&used, &p->list);
/* Rotate through, so we don't favor a single peer. */
p = first = first_random_peer(daemon, &it);
while (p) {
if (maybe_send_query_responses_peer(p))
break;
p = next_random_peer(daemon, first, &it);
}
list_append_list(&daemon->peers, &used);
}
bool query_channel_range(struct daemon *daemon,

View File

@ -281,7 +281,6 @@ static bool in_txout_failures(struct routing_state *rstate,
struct routing_state *new_routing_state(const tal_t *ctx,
const struct node_id *local_id,
struct list_head *peers,
struct timers *timers,
const u32 *dev_gossip_time TAKES,
bool dev_fast_gossip,
@ -291,7 +290,7 @@ struct routing_state *new_routing_state(const tal_t *ctx,
rstate->nodes = new_node_map(rstate);
rstate->timers = timers;
rstate->local_id = *local_id;
rstate->gs = gossip_store_new(rstate, peers);
rstate->gs = gossip_store_new(rstate);
rstate->local_channel_announced = false;
rstate->last_timestamp = 0;
rstate->dying_channels = tal_arr(rstate, struct dying_channel, 0);

View File

@ -272,7 +272,6 @@ get_channel(const struct routing_state *rstate,
struct routing_state *new_routing_state(const tal_t *ctx,
const struct node_id *local_id,
struct list_head *peers,
struct timers *timers,
const u32 *dev_gossip_time TAKES,
bool dev_fast_gossip,

View File

@ -160,6 +160,8 @@ static struct peer *random_seeker(struct seeker *seeker,
bool (*check_peer)(const struct peer *peer))
{
struct peer *peer = seeker->preferred_peer_softref;
struct peer *first;
struct peer_node_id_map_iter it;
/* 80% chance of immediately choosing a peer who reported the missing
* stuff: they presumably can tell us more about it. We don't
@ -171,7 +173,14 @@ static struct peer *random_seeker(struct seeker *seeker,
return peer;
}
return random_peer(seeker->daemon, check_peer);
/* Rotate through, so we don't favor a single peer. */
peer = first = first_random_peer(seeker->daemon, &it);
while (peer) {
if (check_peer(peer))
break;
peer = next_random_peer(seeker->daemon, first, &it);
}
return peer;
}
static bool peer_made_progress(struct seeker *seeker)
@ -741,7 +750,8 @@ static void probe_many_random_scids(struct seeker *seeker)
static void check_firstpeer(struct seeker *seeker)
{
struct peer *peer = seeker->random_peer_softref, *p;
struct peer *peer = seeker->random_peer_softref;
struct peer_node_id_map_iter it;
/* It might have died, pick another. */
if (!peer) {
@ -764,7 +774,10 @@ static void check_firstpeer(struct seeker *seeker)
/* Other peers can gossip now. */
status_peer_debug(&peer->id, "seeker: startup peer finished");
clear_softref(seeker, &seeker->random_peer_softref);
list_for_each(&seeker->daemon->peers, p, list) {
for (struct peer *p = peer_node_id_map_first(seeker->daemon->peers, &it);
p;
p = peer_node_id_map_next(seeker->daemon->peers, &it)) {
if (p == peer)
continue;

View File

@ -87,8 +87,7 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED,
const struct short_channel_id *scid UNNEEDED)
{ fprintf(stderr, "gossip_store_mark_channel_deleted called!\n"); abort(); }
/* Generated stub for gossip_store_new */
struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED,
struct list_head *peers UNNEEDED)
struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED)
{ fprintf(stderr, "gossip_store_new called!\n"); abort(); }
/* Generated stub for memleak_add_helper_ */
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,

View File

@ -48,6 +48,10 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *e
/* Generated stub for find_peer */
struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "find_peer called!\n"); abort(); }
/* Generated stub for first_random_peer */
struct peer *first_random_peer(struct daemon *daemon UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "first_random_peer called!\n"); abort(); }
/* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */
bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED)
{ fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); }
@ -94,6 +98,11 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
struct timerel expire UNNEEDED,
void (*cb)(void *) UNNEEDED, void *arg UNNEEDED)
{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); }
/* Generated stub for next_random_peer */
struct peer *next_random_peer(struct daemon *daemon UNNEEDED,
const struct peer *first UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "next_random_peer called!\n"); abort(); }
/* Generated stub for node_has_broadcastable_channels */
bool node_has_broadcastable_channels(const struct node *node UNNEEDED)
{ fprintf(stderr, "node_has_broadcastable_channels called!\n"); abort(); }

View File

@ -45,6 +45,10 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED,
/* Generated stub for decode_short_ids */
struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED)
{ fprintf(stderr, "decode_short_ids called!\n"); abort(); }
/* Generated stub for first_random_peer */
struct peer *first_random_peer(struct daemon *daemon UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "first_random_peer called!\n"); abort(); }
/* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */
bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED)
{ fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); }
@ -65,6 +69,11 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED,
/* Generated stub for master_badmsg */
void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg)
{ fprintf(stderr, "master_badmsg called!\n"); abort(); }
/* Generated stub for next_random_peer */
struct peer *next_random_peer(struct daemon *daemon UNNEEDED,
const struct peer *first UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "next_random_peer called!\n"); abort(); }
/* Generated stub for peer_supplied_good_gossip */
void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED)
{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); }

View File

@ -26,12 +26,21 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED,
const struct sha256 *h UNNEEDED,
struct pubkey *next UNNEEDED)
{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); }
/* Generated stub for first_random_peer */
struct peer *first_random_peer(struct daemon *daemon UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "first_random_peer called!\n"); abort(); }
/* Generated stub for new_reltimer_ */
struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
const tal_t *ctx UNNEEDED,
struct timerel expire UNNEEDED,
void (*cb)(void *) UNNEEDED, void *arg UNNEEDED)
{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); }
/* Generated stub for next_random_peer */
struct peer *next_random_peer(struct daemon *daemon UNNEEDED,
const struct peer *first UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "next_random_peer called!\n"); abort(); }
/* Generated stub for query_channel_range */
bool query_channel_range(struct daemon *daemon UNNEEDED,
struct peer *peer UNNEEDED,
@ -52,10 +61,6 @@ bool query_short_channel_ids(struct daemon *daemon UNNEEDED,
/* Generated stub for queue_peer_msg */
void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED)
{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); }
/* Generated stub for random_peer */
struct peer *random_peer(struct daemon *daemon UNNEEDED,
bool (*check_peer)(const struct peer *peer))
{ fprintf(stderr, "random_peer called!\n"); abort(); }
/* Generated stub for random_select */
bool random_select(double weight UNNEEDED, double *tot_weight UNNEEDED)
{ fprintf(stderr, "random_select called!\n"); abort(); }

View File

@ -100,8 +100,7 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED,
/* AUTOGENERATED MOCKS END */
/* NOOP stub for gossip_store_new */
struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED,
struct list_head *peers UNNEEDED)
struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED)
{
return NULL;
}
@ -118,7 +117,7 @@ int main(int argc, char *argv[])
timers_init(&timers, time_mono());
/* Random uninitalized node_id, we don't reference it. */
rstate = new_routing_state(tmpctx, tal(tmpctx, struct node_id),
NULL, &timers, NULL, false, false);
&timers, NULL, false, false);
scid1.u64 = 100;
scid2.u64 = 200;