Whatever acts on the best routes (export to peers, the forwarding table,
the status page) reads their changes from a per-shard journal through a
table::Reader. Nothing uses it yet.
- The journal records that a prefix's best route changed, not what to. A
reader that comes to an entry is given the route as it is then. A prefix
that changes a thousand times between reads is one event, and the
journal never holds more live entries than the table has prefixes.
- Each row keeps the sequence number of its latest change; an entry with
another number is dead and is dropped when the journal is tidied.
- Entries go once every reader has passed them. A reader that does not
read cannot hold them back for ever: past a limit the oldest half is
dropped, and that reader is given the whole table again (Reset, every
best route, Synced). A new reader starts the same way.
- With no reader nothing is recorded.
- Reader::changed waits for there to be something to read.
Tested against a mirror kept from the events: after any interleaving of
announcements, withdrawals, session flaps, reads with small budgets and
readers coming and going, with journals small enough to be tidied and
overrun constantly, a reader that has caught up holds exactly the table's
best routes. Also with writers on four threads and a reader on a fifth.
The property test found one fault (a pass that resumed part-way claimed
to have caught up) and catches three more put in on purpose.
On the AMS-IX dump, 12 threads: loading takes 2.29 s with a follower
reading throughout against 2.17 s with none, the journal peaking at
170,000 entries for 1.39 million prefixes. With a follower that does not
read, the journal ends at 1.74 million entries, and the follower then
catches up in 0.08 s.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>