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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
//! One source of truth for publication, verification, provider, and
//! garbage-collection timing bounds.
//!
//! The GC grace window's safety proof (format spec, "Garbage collection",
//! rule 1) is an inequality over these constants: every publication measures
//! itself against a budget here and refuses to publish its root once the
//! budget is spent, provider operations consume one deadline across retries,
//! and the minimum grace window is derived — not tuned — from those bounds
//! plus a margin for provider-timestamp skew. Callers may configure a larger
//! grace window, never a smaller one.
use ;
/// Maximum semantic operations in one explicit commit, bounding how long one
/// request can occupy the serialized publisher during planning and
/// materialization.
pub const MAX_COMMIT_OPERATIONS: usize = 4096;
/// Maximum content-token or prepared-proof entries carried by one explicit
/// commit, bounding preparation work for a new primary. An oversized
/// candidate may occupy a publisher queue slot until candidate preparation
/// rejects it.
pub const MAX_COMMIT_CONTENT_TOKENS: usize = 4096;
/// Maximum distinct new external content refs in one explicit commit, bounding
/// its in-memory coverage work while it occupies the serialized publisher.
pub const MAX_COMMIT_EXTERNAL_CONTENT_REFS: usize = 4096;
/// Maximum byte length of a commit's optional `message` annotation, which is
/// stored in every durable WAL record, hashed into the mutation fingerprint,
/// and replayed by the change feed. This is the only bound on the message:
/// no transport-level body limit is relied on.
pub const MAX_COMMIT_MESSAGE_BYTES: usize = 4096;
/// Maximum attempts for a bounded compare-and-swap or allocation contention loop.
pub const CONTENTION_RETRY_LIMIT: usize = 8;
/// Provider operation deadline, in milliseconds (`loonfs-objectstore`
/// consumes it across every retry of one single-request operation).
/// Multipart transfers of large immutable payloads carry no
/// whole-operation clock — their parts are individually time- and
/// retry-bounded — which leaves the floor derivation below untouched:
/// every object it times (WAL segments inside the publish budget,
/// checkpoint records, the root compare-and-swap) is a small control
/// object on the single-request path, and publications self-enforce their
/// budgets by wall clock regardless of per-operation deadlines.
pub const PROVIDER_OPERATION_DEADLINE_MS: u64 = PROVIDER_OPERATION_DEADLINE.as_millis as u64;
/// One control-plane provider HTTP attempt's request timeout, in
/// milliseconds. An operation's total wall time is bounded by
/// `PROVIDER_OPERATION_DEADLINE_MS + PROVIDER_ATTEMPT_TIMEOUT_MS`, because the
/// deadline gates starting an attempt rather than preempting one.
pub const PROVIDER_ATTEMPT_TIMEOUT_MS: u64 = PROVIDER_ATTEMPT_TIMEOUT.as_millis as u64;
/// Self-enforced budget between starting a WAL segment PUT and initiating
/// the head compare-and-swap. Overrunning it abandons the segment instead of
/// publishing a stale-timed one. Local monotonic elapsed time only — never a
/// validity input (format spec, "WAL head").
pub const WAL_PUBLISH_BUDGET_MS: u64 = 60_000;
/// Self-enforced budget between writing a checkpoint record and completing
/// its post-write basis verification. Overrunning it counts as verification
/// failure: the record may have raced the grace window, so it must not stand
/// as a root.
pub const CHECKPOINT_VERIFY_BUDGET_MS: u64 = 60_000;
/// Self-enforced budget for one metadata publication — WAL flush or
/// reorganization — measured from before the first table object is written
/// until the root compare-and-swap is initiated. A publication that exceeds
/// it aborts without publishing; its immutable outputs remain unreachable
/// garbage-collection candidates.
pub const METADATA_PUBLICATION_BUDGET_MS: u64 = 15 * 60 * 1000;
/// Margin absorbing provider-timestamp skew against the GC caller's clock,
/// plus scheduling slop around the budget checks.
pub const GC_SAFETY_MARGIN_MS: u64 = 3 * 60 * 1000;
/// Default candidate budget for one step-driven garbage-collection pass.
pub const DEFAULT_GC_MAX_OBJECTS: u64 = 1024;
const
/// The derived minimum GC grace window and explicit namespace-repair safety
/// window (format spec, "Garbage collection", rule 1). Every acknowledged
/// publication starts its final compare-and-swap within a publication budget
/// measured from its first object write, and that compare-and-swap completes
/// within one provider operation bound. So an object older than this window
/// that is still unreachable at delete time cannot belong to a publication
/// that might yet succeed. `GcConfig::validate` rejects smaller windows, and
/// namespace repair uses the same bound before reaping non-completable install
/// debris.
pub const GC_MIN_GRACE_WINDOW_MS: u64 = max_u64 + PROVIDER_OPERATION_DEADLINE_MS
+ PROVIDER_ATTEMPT_TIMEOUT_MS
+ GC_SAFETY_MARGIN_MS;
/// Most parts one direct multipart upload may cut into. This is the
/// S3-compatible ceiling, so with the session's part size it fixes the
/// largest object that session can carry: `part_size_bytes × 10_000`.
pub const MAX_MULTIPART_PARTS: u32 = 10_000;
/// Smallest part size a `direct_multipart` session may be opened with.
/// Every supported provider refuses a non-final part below 5 MiB.
pub const MIN_MULTIPART_PART_BYTES: u64 = 5 * 1024 * 1024;
/// Largest part size a `direct_multipart` session may be opened with.
/// Every supported provider refuses a part above 5 GiB.
pub const MAX_MULTIPART_PART_BYTES: u64 = 5 * 1024 * 1024 * 1024;
/// Most part-upload capabilities one request may ask for. A client asks in
/// waves as it works through a file, so this bounds one response rather than
/// one upload.
pub const MAX_SIGNED_PARTS_PER_REQUEST: usize = 1_000;
/// Lease every fork attempt takes on the fork-owned source checkpoint it
/// creates. An attempt that never installs its target head lets the lease
/// pass, and garbage collection releases the record on that alone — no
/// provider timestamp, no fork-specific age rule.
///
/// Two grace floors, because a fork attempt is two of the things that floor
/// already bounds. The first covers creating the record: the WAL flush and
/// manifest publication it may perform, the post-write basis verification,
/// and the provider bounds and clock skew around them — which is exactly
/// what `GC_MIN_GRACE_WINDOW_MS` is the bound for. The second covers
/// everything after: reading the pinned manifest and the source head,
/// installing the target head, and the guard read below. Each half is one
/// publication plus provider bounds plus skew, so each is one floor.
pub const FORK_CHECKPOINT_LEASE_MS: u64 = 2 * GC_MIN_GRACE_WINDOW_MS;
/// Lease an upload session takes when it opens. A session is the only place
/// retry idempotency lives, so the lease has to outlast any single transfer a
/// client may reasonably be in the middle of — a proxied body, or a presigned
/// direct write and the completion call after it. Once it passes, the session
/// is abandoned by definition and upload garbage collection aborts it: unlike
/// a namespace, a session is a lease, so reclaiming one on age alone is the
/// correct reading and not a guess about the client.
pub const UPLOAD_SESSION_LEASE_MS: u64 = 24 * 60 * 60 * 1000;
/// How long one minted content receipt admits the content it names at commit.
///
/// Short on purpose: durability lives in the completed upload session, which
/// is durable and re-mints, so the receipt only has to cover the gap between
/// finishing an upload and committing the metadata that references it.
pub const CONTENT_RECEIPT_TTL_MS: u64 = 60 * 60 * 1000;
/// How long a completed session goes on minting receipts for its content.
///
/// This is the "a lost publish response never costs a retransfer" promise
/// expressed as a number: for this long after completion, reading the
/// session's status hands back a fresh receipt for bytes that are already
/// durable. After it, the content is either referenced by metadata — which
/// protects it on its own — or reclaimable.
pub const COMPLETED_UPLOAD_RECEIPT_WINDOW_MS: u64 = 7 * 24 * 60 * 60 * 1000;
/// Grace a completed upload's content object gets before content garbage
/// collection may reclaim it as unreferenced. Derived, not tuned.
///
/// Three spans have to be over before "no metadata references this" can be
/// trusted to stay true. The session may mint a receipt at any point in its
/// receipt window; the last such receipt admits a commit for one more receipt
/// TTL; and a commit admitted at that last instant still has a publication
/// budget plus provider bounds and clock skew to land its root — which is
/// exactly what `GC_MIN_GRACE_WINDOW_MS` bounds. Past their sum no new
/// reference can appear, so a reference set collected earlier in the pass is
/// still sound at delete time.
pub const CONTENT_RECLAMATION_GRACE_MS: u64 =
COMPLETED_UPLOAD_RECEIPT_WINDOW_MS + CONTENT_RECEIPT_TTL_MS + GC_MIN_GRACE_WINDOW_MS;
/// The grace floor's inequality, shared by the compile-time assertion below
/// and the test that proves the assertion has teeth.
const
// Content reclamation is the one sweep that deletes bytes a user handed us,
// and its safety is an inequality over the constants above rather than a
// judgement call, so it is checked where a broken derivation is a compile
// error instead of a test failure.
const _: = assert!;
/// Margin the post-publish fork guard requires between now and the source
/// record's lease expiry before it lets a target stand.
///
/// The guard's evidence is one record read, and one provider operation's
/// total wall time is `PROVIDER_OPERATION_DEADLINE_MS +
/// PROVIDER_ATTEMPT_TIMEOUT_MS` (the deadline gates starting an attempt
/// rather than preempting one). A record observed with more lease than that
/// left cannot have been legally expiry-released while the guard was
/// looking at it, so the observation still holds when the guard acts on it.
pub const FORK_GUARD_MARGIN_MS: u64 = PROVIDER_OPERATION_DEADLINE_MS + PROVIDER_ATTEMPT_TIMEOUT_MS;
// The fork lease has two jobs, and both are inequalities over the constants
// above rather than judgement calls, so they are checked where a broken
// derivation is a compile error instead of a test failure: it must cover a
// whole fork attempt, and it must leave the guard something to check.
const _: = assert!;
const _: = assert!;