1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Replay-on-reconnect: everything persisted after `since_seq`, in order.
//!
//! Why: DOC-73 §4.3 — "a viewer that opens mid-session reads the log to build
//! the tree it missed, then switches to the live ring at the log's last
//! `seq`." A gap must never read as silence: retention can have discarded
//! everything before `since_seq` asked for, and the writer's bounded channel
//! (`super::config::WRITE_CHANNEL_CAPACITY`) can have dropped an event under
//! backpressure, leaving a hole inside what IS retained. Both cases surface
//! as an explicit [`ReplayItem::Gap`] rather than the caller silently getting
//! fewer events than it expected.
//! What: [`replay_since`] lists day files, reads every event with
//! `seq > since_seq` across them in seq order (file order already matches
//! seq order, since files rotate forward in time and seq only increases), and
//! interleaves gap markers: one BEFORE the first event if `since_seq` predates
//! `earliest_retained_seq`, and one wherever two consecutive events' seqs are
//! not adjacent (a backpressure drop). Every returned event is wrapped
//! `persisted: true` — read from the log, it always is, by construction.
//! Test: `super::tests::replay_since_zero_returns_everything_in_order`,
//! `super::tests::replay_since_a_mid_seq_returns_only_the_remainder`,
//! `super::tests::replay_since_before_retention_yields_a_leading_gap`,
//! `super::tests::replay_since_across_a_backpressure_drop_yields_an_interior_gap`,
//! `super::tests::replay_since_a_drop_on_the_first_replayed_seq_yields_a_gap`,
//! `super::tests::replay_since_caught_up_returns_no_events_and_no_gap`,
//! `super::tests::replay_since_on_an_empty_log_returns_nothing`.
use Path;
use HarnessEvent;
use LogError;
use read_events;
use list_log_files;
/// One item of a replay: either a persisted event, or an explicit marker that
/// events between `after_seq` and `before_seq` (both exclusive) are gone and
/// will never be replayed.
pub
/// Replay everything persisted after `since_seq`, in seq order.
///
/// `earliest_retained_seq` is [`super::DurableLog`]'s current lower bound —
/// `None` when nothing has ever been retained. When `since_seq` predates it,
/// the first item returned is a [`ReplayItem::Gap`] covering
/// `(since_seq, earliest_retained_seq)` before any event.
///
/// # Errors
///
/// [`LogError::Io`] if a day file cannot be listed or read — a single file
/// disappearing between listing and reading degrades to "no events in that
/// file" (see [`read_events`]), not a hard failure of the whole replay.
///
/// Test: see module docs.
pub async