Skip to content

Commit c303d7d

Browse files
rustyrussellniftynei
authored andcommitted
gossipd: only do (automatic) store compaction at startup.
Rewriting the gossip_store is much more trivial when we don't have any pointers into it, so add some simple offline compaction code and disable the automatic compaction code. Signed-off-by: Rusty Russell <[email protected]>
1 parent c15d9ed commit c303d7d

File tree

2 files changed

+93
-16
lines changed

2 files changed

+93
-16
lines changed

gossipd/gossip_store.c

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,95 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len)
7777
return writev(fd, iov, ARRAY_SIZE(iov)) == sizeof(hdr) + msglen;
7878
}
7979

80+
/* Read gossip store entries, copy non-deleted ones. This code is written
81+
* as simply and robustly as possible! */
82+
static void gossip_store_compact_offline(void)
83+
{
84+
size_t count = 0, deleted = 0;
85+
int old_fd, new_fd;
86+
struct gossip_hdr hdr;
87+
u8 version;
88+
89+
old_fd = open(GOSSIP_STORE_FILENAME, O_RDONLY);
90+
if (old_fd == -1)
91+
return;
92+
new_fd = open(GOSSIP_STORE_TEMP_FILENAME, O_RDWR|O_TRUNC|O_CREAT, 0600);
93+
if (new_fd < 0) {
94+
status_broken(
95+
"Could not open file for gossip_store compaction");
96+
goto close_old;
97+
}
98+
99+
if (!read_all(old_fd, &version, sizeof(version))
100+
|| version != GOSSIP_STORE_VERSION) {
101+
status_broken("gossip_store_compact: bad version");
102+
goto close_and_delete;
103+
}
104+
105+
if (!write_all(new_fd, &version, sizeof(version))) {
106+
status_broken("gossip_store_compact_offline: writing version to store: %s",
107+
strerror(errno));
108+
goto close_and_delete;
109+
}
110+
111+
/* Read everything, write non-deleted ones to new_fd */
112+
while (read_all(old_fd, &hdr, sizeof(hdr))) {
113+
size_t msglen;
114+
u8 *msg;
115+
116+
msglen = (be32_to_cpu(hdr.len) & ~GOSSIP_STORE_LEN_DELETED_BIT);
117+
msg = tal_arr(NULL, u8, msglen);
118+
if (!read_all(old_fd, msg, msglen)) {
119+
status_broken("gossip_store_compact_offline: reading msg len %zu from store: %s",
120+
msglen, strerror(errno));
121+
tal_free(msg);
122+
goto close_and_delete;
123+
}
124+
125+
if (be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_DELETED_BIT) {
126+
deleted++;
127+
tal_free(msg);
128+
continue;
129+
}
130+
131+
if (!write_all(new_fd, &hdr, sizeof(hdr))
132+
|| !write_all(new_fd, msg, msglen)) {
133+
status_broken("gossip_store_compact_offline: writing msg len %zu to new store: %s",
134+
msglen, strerror(errno));
135+
tal_free(msg);
136+
goto close_and_delete;
137+
}
138+
tal_free(msg);
139+
count++;
140+
}
141+
if (close(new_fd) != 0) {
142+
status_broken("gossip_store_compact_offline: closing new store: %s",
143+
strerror(errno));
144+
goto close_old;
145+
}
146+
close(old_fd);
147+
if (rename(GOSSIP_STORE_TEMP_FILENAME, GOSSIP_STORE_FILENAME) != 0) {
148+
status_broken("gossip_store_compact_offline: rename failed: %s",
149+
strerror(errno));
150+
}
151+
status_debug("gossip_store_compact_offline: %zu deleted, %zu copied",
152+
deleted, count);
153+
return;
154+
155+
close_and_delete:
156+
close(new_fd);
157+
close_old:
158+
close(old_fd);
159+
unlink(GOSSIP_STORE_TEMP_FILENAME);
160+
}
161+
80162
struct gossip_store *gossip_store_new(struct routing_state *rstate,
81163
struct list_head *peers)
82164
{
83165
struct gossip_store *gs = tal(rstate, struct gossip_store);
84166
gs->count = gs->deleted = 0;
85167
gs->writable = true;
168+
gossip_store_compact_offline();
86169
gs->fd = open(GOSSIP_STORE_FILENAME, O_RDWR|O_APPEND|O_CREAT, 0600);
87170
gs->rstate = rstate;
88171
gs->disable_compaction = false;
@@ -352,19 +435,6 @@ bool gossip_store_compact(struct gossip_store *gs)
352435
return false;
353436
}
354437

355-
static void gossip_store_maybe_compact(struct gossip_store *gs)
356-
{
357-
/* Don't compact while loading! */
358-
if (!gs->writable)
359-
return;
360-
if (gs->count < 1000)
361-
return;
362-
if (gs->deleted < gs->count / 4)
363-
return;
364-
365-
gossip_store_compact(gs);
366-
}
367-
368438
u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg,
369439
u32 timestamp,
370440
const u8 *addendum)
@@ -460,8 +530,6 @@ void gossip_store_delete(struct gossip_store *gs,
460530
if (type == WIRE_CHANNEL_ANNOUNCEMENT)
461531
delete_by_index(gs, next_index,
462532
WIRE_GOSSIP_STORE_CHANNEL_AMOUNT);
463-
464-
gossip_store_maybe_compact(gs);
465533
}
466534

467535
const u8 *gossip_store_get(const tal_t *ctx,

tests/test_gossip.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ def test_gossip_store_load_announce_before_update(node_factory):
922922

923923
l1.start()
924924
# May preceed the Started msg waited for in 'start'.
925-
wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(1 deleted\) in 912 bytes'))
925+
wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 770 bytes'))
926926
assert not l1.daemon.is_in_log('gossip_store.*truncating')
927927

928928
# Extra sanity check if we can.
@@ -1283,3 +1283,12 @@ def test_gossip_store_load_no_channel_update(node_factory):
12831283

12841284
with open(os.path.join(l1.daemon.lightning_dir, 'gossip_store'), "rb") as f:
12851285
assert bytearray(f.read()) == bytearray.fromhex("07")
1286+
1287+
1288+
def test_gossip_store_compact_on_load(node_factory, bitcoind):
1289+
l2 = setup_gossip_store_test(node_factory, bitcoind)
1290+
1291+
l2.restart()
1292+
1293+
wait_for(lambda: l2.daemon.is_in_log('gossip_store_compact_offline: 9 deleted, 9 copied'))
1294+
wait_for(lambda: l2.daemon.is_in_log(r'gossip_store: Read 1/4/2/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 1446 bytes'))

0 commit comments

Comments
 (0)