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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use Builder;
use ;
use ;
pub const DEFAULT_PERSIST_EVENTS_BATCH_SIZE: usize = 5000;
/// How long the persistent cache waits before the first gap-fill attempt
/// for a stalled broadcast sequence. Most gaps are in-flight transactions
/// that committed out of sequence-allocation order and resolve on their own
/// within a few ms (and this process's own commit-failed allocations are
/// compensated reactively, never waiting for a fill attempt at all);
/// attempting immediately wastes a page re-read per gap. At high posting
/// rates (hundreds of sequences/s) there is nearly always an in-flight
/// frontier gap, so a sub-second grace is guaranteed-too-short and turns
/// the fill into a permanent background load — hence seconds, not
/// milliseconds. Attempts are read-only until the missing sequences are
/// *provably abandoned* (every transaction that could have produced them
/// has ended — see `MailboxTables::abandonment_proof_passed`), so a fill
/// can never collide with a live writer regardless of this setting.
///
/// This grace no longer paces draining a backlog of *committed* rows — the
/// feeder drains those from memory, or reads them at DB speed. It governs the
/// hole case exactly as before.
pub const DEFAULT_GAP_FILL_GRACE: Duration = from_secs;
/// Ceiling on the rows a single backfill page read may return, and the width
/// of a gap-fill stall episode's window (an episode's re-read is a page read,
/// so one episode covers what one page read can see).
///
/// A query-shape knob, distinct from `event_cache_size` (how far behind a
/// listener may fall and still be served from memory) and `event_buffer_size`
/// (how much may be in flight): this bounds how long a catch-up read holds a
/// pooled connection and how large a result set it materialises. Keep it well
/// under `event_cache_size`, so a listener that consumes a page lands back
/// inside the memory window instead of paging forever.
///
/// It is also the depth of the channel a backfill delivers through, so it
/// bounds how far the reader may run ahead of its consumer — one page in
/// flight while the next is fetched. Sizing that channel by `event_buffer_size`
/// instead would let the reader outrun a slow consumer by a whole second
/// buffer's worth of events, all of it read from the database and then evicted.
pub const DEFAULT_BACKFILL_PAGE_SIZE: usize = 1000;
/// Maximum number of placeholder rows a single gap-fill query may insert.
/// Bounds the worst case (a mass rollback or long outage leaving thousands
/// of lost sequences) to a small, predictable statement instead of one
/// giant insert; the fixed 1s retry cadence picks up the remainder, so a
/// cap delays recovery of a pathological backlog without ever losing
/// sequences.
///
/// This caps one *insert*, not an episode: the episode window is the
/// [`backfill page`](DEFAULT_BACKFILL_PAGE_SIZE), so a gap wider than this cap
/// fills across several passes of one episode — one grace period, one marker,
/// batches at the loop cadence.
pub const DEFAULT_GAP_FILL_BATCH_LIMIT: usize = 1000;
/// How long the per-process notifier coalesces committed-batch reports
/// before emitting one `pg_notify` wake-up hint. Notify-bearing commits
/// serialize on a cluster-wide lock, so app transactions no longer notify;
/// this bounds the added cross-process wake-up latency (in-process delivery
/// is unaffected).
pub const DEFAULT_NOTIFY_DEBOUNCE: Duration = from_millis;
/// How long the persistent cache goes without authoritative progress (a
/// newly-seen committed row or a confirmed head read) before polling the
/// sequence head (the O(1) `last_value` query). Backstops lost wake-ups: a
/// writer crashing between commit and notify, a dead remote notifier, or
/// external writers that never notify.
pub const DEFAULT_IDLE_RESYNC_INTERVAL: Duration = from_secs;
/// Width, in `sequence` units, of each `persistent_outbox_events` partition.
///
/// A fixed schema constant, deliberately **not** runtime-configurable: it must
/// equal the range of the initial `p0` partition hard-coded in the migration
/// (`[0, 2_000_000)`), or the maintainer would create partitions that overlap
/// `p0`. Changing the size means editing both this constant and the migration
/// together.
///
/// Sized from real event data: outbox rows measure ~760 B each (payloads
/// average a few hundred bytes and rarely TOAST), so 2M rows ≈ ~1.5 GB per
/// partition — small enough that the hot partition stays cache-resident and a
/// per-partition vacuum is quick, while keeping the partition count low.
pub const DEFAULT_PARTITION_WIDTH: u64 = 2_000_000;
/// How many partitions ahead of the current sequence head the maintainer
/// keeps created — including on the initial synchronous `ensure` at
/// registration, so a fresh install starts with a multi-partition runway
/// (the migration itself ships only `p0` + `DEFAULT`). `premake * width` must
/// comfortably exceed the events produced between two maintainer ticks so the
/// head never reaches the last pre-made boundary (which would spill into the
/// `DEFAULT` partition). Empty partitions are cheap, so this errs generous:
/// 5 * 2M = 10M sequences of headroom by default.
pub const DEFAULT_PARTITION_PREMAKE: u64 = 5;
/// How often the partition maintainer wakes to pre-create partitions ahead of
/// the head. Each tick is idempotent (`CREATE ... IF NOT EXISTS`), so this is
/// a cheap steady-state poll; premake margin, not cadence, is the safety
/// budget against bursts.
pub const DEFAULT_PARTITION_MAINTAINER_INTERVAL: Duration =
from_secs;
/// How many groups the commit-lane fold emits between sparse checkpoints. The
/// order is computed, so a checkpoint only bounds how far a restart or a
/// lagging subscriber's backfill has to re-fold.
pub const DEFAULT_COMMIT_CHECKPOINT_EVERY: usize = 1_000;
/// Longest the commit-lane fold goes without a checkpoint while emitting,
/// regardless of group count; see [`DEFAULT_COMMIT_CHECKPOINT_EVERY`].
pub const DEFAULT_COMMIT_CHECKPOINT_INTERVAL: Duration =
from_secs;
/// Whether this outbox runs the commit-ordered lane. Opt-in: the fold passes
/// over every event, in every process that runs the outbox.
/// Why a lane's frontier could not be read.
/// The commit lane is off for this outbox. Raised at registration, before any
/// job is spawned, so a consumer that needs the lane fails at startup.
;