alkblobs 0.2.2

Content-addressed blob storage: multi-hash put/get/verify store with pluggable backends, transport- and protocol-agnostic
Documentation
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
//! The `Store` facade type (store-api.md §What this is): the public
//! surface consumers program against — constructor modes, the
//! size-threshold dispatch seam, the write path (`put_as`, the two
//! internal put paths behind one signature, the `Batch` batching seam
//! with its algorithm parameter, kv-only
//! rejection — implemented below), the read-path ops (`get`/`stat`/
//! `read_range`/`has`/`list` with the fall-through general rule),
//! and the GC protocol's ops (`sweep`, direct `delete`, the liveness
//! registration — arbitration-then-delete per ADR-005/012, implemented
//! below). This module owns the engine representation and the
//! constructor-mode machinery; the pure routing function lives in
//! [`crate::dispatch`].
//!
//! **Named design point — engine representation: concrete `Store`,
//! crate-internal enum dispatch.** A `Store<Kv: Backend, Large: Backend>`
//! generic was the alternative: rejected. The engines are statically
//! known and in-crate (no `dyn` anywhere — wave-1 design point 3), and
//! they are `pub(crate)` behind the crate-internal [`Backend`][crate::backend]
//! trait — a generic facade parameterized over `Backend` implementers
//! would leak engine types through the public surface, force consumers
//! to name engines they must never see (the facade is **engine-blind**,
//! ADR-007/008/010), or push `dyn` onto the surface. The concrete type
//! holds two crate-internal engine-holding enums (`KvEngine`,
//! `LargeEngine`) dispatching over the closed five-engine set; the
//! conformance factory's binding note anticipated exactly this collapse
//! (tests/conformance/factory.rs — "most plausibly the associated type
//! collapsing to the concrete facade type across all factories"). The
//! public surface is engine-blind: consumers name modes and engines
//! only through named constructors, feature-gated with their engine
//! (`mem_only` needs feature `mem`; the sqlite constructors are the
//! default build's — wave 3; the postgres constructors arrived with
//! wave 4 behind feature `postgres`; the pg-lo constructors arrived
//! with wave 5 behind feature `pg-lo` — and each slotting is exactly
//! adding an enum variant and a named constructor, without reshaping).
//!
//! **Constructor modes** (ADR-011 §4; ADR-012 §6.3):
//!
//! - **dual-tier** (the default): kv + large, size-threshold dispatch
//!   via `crate::dispatch::route`. The wave-2 constructible shape is
//!   `kv = mem` + `large = local` — the dispatch-coverage test shape
//!   ADR-012 §6.3 names (both tiers load-bearing); the wave-3
//!   constructible shape is `kv = sqlite` + `large = local` — the
//!   **default-build production shape** (feature `sqlite` is
//!   default-on, ADR-013 §4); the wave-4 shape is `kv = postgres` +
//!   `large = local` — the **fleet-capable-kv production shape** (the
//!   engine is the fleet host, ADR-012 §4; the constructor stays the
//!   non-fleet declaration — the fleet declaration is the `_fleet`
//!   twin constructors, the module doc's fleet-declaration
//!   paragraph). The wave-5 shapes complete
//!   the dual-tier constructor set: `kv = postgres` + `large = pg-lo`
//!   — the **consolidation posture** (ADR-009's "one engine to
//!   operate"; that constructor completes the fleet-valid engine
//!   set, the module doc's recorded forward reference) — and `kv =
//!   mem` + `large = pg-lo` — the **one-window-host rule's named
//!   test topology** (ADR-012 §5: the durable window hosts on the
//!   large engine when the kv engine is not SQL-backed).
//!   Over-threshold content routes to the large tier; at-or-below to
//!   kv.
//! - **kv-only**: no large tier; over-threshold puts reject **at put
//!   time** (ADR-012 §6.2 — known-length immediately; unknown-length at
//!   mid-stream threshold overflow). The rejection behavior itself
//!   lands with the put seam; the threshold this mode carries is the
//!   rejection point's value. The wave-2 engine is mem (testing);
//!   the wave-3 engine is sqlite (the durable single-node medium); the
//!   wave-4 engine is postgres (the fleet host's kv-only shape).
//!   pg-lo is not a kv engine — kv-only modes take kv engines only,
//!   and no `kv_only(pg-lo)` constructor exists.
//! - **mem-only**: the mem engine alone, **no dispatch threshold in
//!   effect** (everything is local and small) — a testing/embedder-
//!   ephemeral posture, never production. Structurally distinct from
//!   kv-only precisely by that threshold absence.
//!
//! pg-lo constructors are dual-tier only (pg-lo is a large engine;
//! the pairing is a valid non-fleet construction per ADR-012 §5),
//! and no sqlite + pg-lo constructor is named this wave — added on
//! demand.
//!
//! Modes are validated structurally — unrepresentable states are
//! unrepresentable by construction: dual-tier's signature constructs
//! the large engine itself, mem-only's constructor exists only with
//! feature `mem` and constructs the mem engine — and every instance
//! carries its mode for introspection ([`Store::mode`]).
//!
//! **Read-path design points (recorded for the review gate):**
//!
//! - **The fall-through general rule (ADR-012 §6.1; POC #1 finding
//!   6).** Every key-addressed op consults `KvEngine` first, then
//!   `LargeEngine` — there is no length routing on the read side and
//!   `Store::tier_for` is never consulted by it (the dispatch seam's
//!   write-half routing is the put path's; `route` feeds nothing on
//!   reads). A miss on both tiers resolves per op's shape: `Ok(None)`
//!   on get/stat, `false` on has, `Err(Missing)` on read_range.
//! - **The public stream type (named design point): `Blob`.** `get`
//!   returns the pinned `(len, stream)` shape as
//!   `Option<(u64, Blob)>` (store-api.md §Read path) — [`crate::facade::Blob`] is a
//!   bounded, re-entrant windowed reader over the crate-internal
//!   `AnyCursor`, which dispatches the engine cursor bodies by tier
//!   — the kv materializing bodies behind `KvCursor` on the one
//!   arm, the large streaming bodies behind `LargeCursor` on the
//!   other (both sub-enums the recorded design points). Windowed
//!   reads are the uniform stream shape across
//!   tiers; no chunk-iterator machinery was adopted — the cursor
//!   bodies *are* the streaming abstraction, and per-chunk `Stream`-
//!   wrapping would re-shape them without changing the underlying
//!   reads. Engine cursors never leak onto the public surface:
//!   `AnyCursor` is `pub(crate)`, `Blob` is opaque but drivable
//!   (constructed only by the store core, ADR-010 §1).
//! - **Verification-on-get (named design point, pinned against
//!   ADR-006's text).** The whole-blob hash-check fires where the
//!   whole content is already in hand — the kv tier's materializing
//!   reads. A kv-tier `get` materializes the stored value and
//!   re-derives the canonical digest over it, comparing against the
//!   key (ADR-006 decision 1: "put and get paths hash-check");
//!   mismatch is `Err(Error::Verification)` and **never falls
//!   through further** — a verified mismatch is a pool-integrity
//!   fact, and consulting another tier past it would be a guess.
//!   The large tier's streaming `get` does *not* re-derive at open:
//!   opening is metadata + handle only, and the tier's integrity
//!   story is the put path's whole-blob verification pass (the
//!   complete staged artifact was checked before commit) plus the
//!   per-range slice digests `read_range` carries out-of-band
//!   (ADR-006 decisions 1-2; the §Context line "git objects
//!   self-verify under git's own model when read whole"). `read_range`
//!   carries no canonical check on either tier — the slice digest is
//!   the convention's whole answer (ADR-006 decision 2). stat/has/
//!   list carry no verification (metadata and enumeration only).
//! - **`list` returns `Vec<Key>` (named design point: Vec vs
//!   stream).** Whole-pool enumeration, complete by contract
//!   (ADR-003 — load-bearing for GC), returned **materialized and
//!   sorted in the keys' total order** (deterministic output). The
//!   sweep consumes the same crate-internal enumeration directly from
//!   the engines (its live-set diffs sort independently), so the
//!   facade shape serves consumers, not internal needs. A lazy stream
//!   was rejected: every shipped engine enumerates fully and eagerly
//!   (the completeness contract *is* the GC-safety property), so a
//!   pseudo-lazy wrapper adds machinery without changing the
//!   underlying walk. Listed bytes failing [`Key::from_bytes`]
//!   reject with `Error::KeyInvalid` — a malformed pool list is
//!   fatal, never a silent skip (ADR-003's malformed-list ruling
//!   applied at the facade boundary).
//! - **`KeyInvalid` at the facade boundary (ADR-002/003).** Read ops
//!   take opaque key *bytes* and convert to typed `Key` at the
//!   boundary via [`Key::from_bytes`]: malformed bytes reject with
//!   `Error::KeyInvalid` on every key-addressed op, never a guess,
//!   and the engines receive only the physical byte encoding — they
//!   never see typed keys.
//!
//! **Write-path design points (recorded for the review gate):**
//!
//! - **The stream parameter shape: `put_as(algorithm, len, stream:
//!   impl AsyncRead + Unpin + Send)`** — generic over the concrete
//!   reader, no boxing. The family's no-`dyn` posture (wave-1 design
//!   point 3: no `dyn` anywhere) carries to the write seam: the reader
//!   is monomorphized, and a caller holding a boxed reader passes the
//!   box (a `Box<dyn AsyncRead + Unpin + Send>` is itself `AsyncRead +
//!   Unpin + Send` under tokio's blanket impl). `Send` (and no
//!   `'static`: nothing is spawned) keeps spawn-site bounds resolvable
//!   for the ops layer (wave 7), matching the ADR-005/ops posture. A
//!   boxed trait-object parameter was rejected as the *default* shape — it
//!   would force an allocation on every call (including the known-
//!   length path's) to carry a posture the family has already ruled
//!   against. Callers who need erasure box at their call site.
//! - **The declared-len mismatch case (`Verification`).** A known-
//!   length stream that yields fewer or more bytes than `len` rejects
//!   with `Error::Verification` on either side: the derivation's
//!   preamble carries the *declared* length, so a mismatched artifact's
//!   canonical address can never equal the stored key (a later get
//!   re-derives over the actual length and hash-checks — storing under
//!   the declared-length derivation would break pool integrity by
//!   construction). Zero residue on either side: the kv path buffers
//!   and publishes nothing; the staged path drives
//!   `crate::backend::backend`'s `StagingCursor::discard`. The error family is
//!   ADR-pinned to five members (ADR-012 §6.5); `Verification` is the
//!   put-path hash-check family member and this is a content-≠-key
//!   fact (the ADR-006 whole-blob posture applied at put time).
//! - **The kv-only over-threshold rejection's error carrier (named
//!   design point).** ADR-012 §6.2's put-time rejection rides
//!   `Error::Io(String)` with a descriptive message: the pinned family
//!   (ADR-012 §6.5) holds no dedicated variant, and `Missing`/
//!   `Verification`/`KeyInvalid`/`GcRefuse` are strictly narrower
//!   families — the rejection is an operational/capability fact about
//!   the store's medium posture, the bucket `Io(String)` covers
//!   (stringly, so the message names mode, threshold, and the
//!   rejected length). Recorded for the review gate: a dedicated
//!   variant for embedders that need typed matchability would be a
//!   new failure family — a new ADR.
//! - **The staged-put seam resolves the key at commit (the wave-2
//!   amendment of the wave-1 handout shape).** The canonical key is
//!   the derivation over preamble + content and completes at stream
//!   end — after the staging cursor exists (both large-tier put paths
//!   stream content once into the cursor while hashing; ADR-003). The
//!   `crate::backend::backend`'s `put_staged` handout is therefore
//!   keyless and the staging cursor's `commit` carries the
//!   store-core-resolved key (CAS first-writer-wins as the whole-value
//!   put). The wave-1 notes' `put_staged(key)` handout predates this
//!   task's flow realization; no ADR text pins the key's arrival
//!   point, and the amendment is recorded for review-wave-2.
//! - **`Batch` surface: `batch()` + `put_in_batch(&mut Batch,
//!   algorithm, ...)`.** The batch-scoped pin group is minted by the
//!   store core ([`crate::gc`]); a member put runs the lone-put flow
//!   and contributes its pin to the group (`Batch::add` — the minted
//!   guard is defused, the group's one-step release owns the
//!   protection). Members publish pin-before-publish exactly as lone
//!   puts do; the group's pins drop together on
//!   [`crate::facade::Batch::commit`] or drop (the `gc-protocol`
//!   machinery drives).
//! - **The fleet put path (store-fleet-put; landed):** under `fleet:
//!   true` the put seam's routing branches on the fleet flag only —
//!   kv-tier puts drive the joint entry+pin tx (`begin_joint_tx` →
//!   `joint_put` (CAS; a dedup re-put commits the pin row alone) →
//!   `pin_commit`; publish + protection in one step), the pg-lo tier's
//!   staged form *is* the joint form (one tx over the shared
//!   instance), and the local tier's staged form is pin-first,
//!   publish-second (the row-only tx, then the commit-rename). The
//!   returned `Pin` carries the row's TTL/3 renewal duty (a per-guard
//!   tokio task, aborted at every guard-life boundary); the non-fleet
//!   paths are the ADR-005 in-process protocol verbatim — byte-
//!   unchanged. The named design points (A1's catch-propagate-
//!   terminate, the renewal lifecycle, the pin-first ordering) are
//!   recorded on the fleet impl block in this module.
//!
//! **The fleet declaration (ADR-012 §3; landed — wave 6's
//! `store-fleet-construction`).** Fleet mode is a constructor
//! declaration, never inferred: every pre-existing constructor is
//! implicitly `fleet: false` (the ADR-005 in-process protocol applies
//! verbatim regardless of engine — pins are in-process guards, the
//! sweep lease is the in-process lock, none of the `EngineFleetState`
//! clauses run — behavior byte-unchanged), and the **`_fleet`
//! twin constructors** declare `fleet: true`:
//! `Store::kv_only_postgres_fleet` (the re-routing client posture —
//! large = none, pool large content rides the landed ops module —
//! wave 7's wiring), `Store::dual_tier_postgres_local_fleet` (the shared-media
//! posture — the declaration requires the sealed
//! `FleetSharedMedia` assertion, ADR-008 §3), and
//! `Store::dual_tier_postgres_pglo_fleet` (the consolidation posture —
//! ADR-012 §4's same-instance clause runtime-probed). **The composite
//! fleet-validity predicate** (`fleet_validity_error` — the one-
//! sentence rule of backends-and-dispatch.md, both tiers) is enforced
//! at the constructor, keyed off the engine representation like every
//! other dispatch in this store: a configuration is fleet-valid iff
//! `kv = postgres` AND (`large = pg-lo` on the same pg instance as the
//! kv engine OR `large = local` on the declared shared media OR
//! `large = none`) — plus the explicit declaration. `fleet: true` is a
//! constructor error for every non-fleet-valid configuration; the
//! predicate's mem/sqlite clauses are enforced structurally (no
//! `_fleet` constructor is named for a mem or sqlite kv engine — the
//! recorded "not fleet-anything" postures) with the predicate's
//! rejection clauses unit-tested per clause at the predicate level,
//! and the pg-lo same-instance clause is the one runtime-checkable
//! clause: the instance's `system_identifier` (via
//! `pg_control_system()`, PG14+) compared across one connection per
//! engine — a mismatch **or a failed probe** is a constructor error,
//! never a silent pass. **The fleet constructor parameters, not API**
//! (ADR-008 §2): the pin TTL (default `DEFAULT_PIN_TTL`, 60 s — the
//! store owns the TTL and the TTL/3 renewal cadence; the engine's
//! `pin_renew` successor-time body reads the TTL it was constructed
//! with) and the large-tier declaration (the mode's name, the sealed
//! shared-media assertion, or the pg-lo shape). The store carries the
//! fleet state once declared — the flag (the put/sweep paths branch on
//! it, never on re-deriving from the engine set), the pin TTL, and the
//! store-instance id (the pin row's `owner` half, minted at
//! construction, 32 raw bytes, crate-internal): `FleetState`, never
//! public API (ADR-008 §3: fleet constructor parameters, not API). The
//! fleet declaration slots in without reshaping the fleet-capable
//! constructors (the wave-4/5 recorded non-event landed as recorded):
//! the pre-existing constructors' signatures and behavior are
//! unchanged and remain implicitly `fleet: false`.
//!
//! **The dispatch seam the ops tasks program through:** tier selection
//! for a given length (`Store::tier_for` composing
//! `crate::dispatch::route` per mode), the fall-through consult
//! order — **kv first, then large** — that every key-addressed op
//! consumes (ADR-012 §6.1's general rule; `Store::kv` then
//! `Store::large`), and the GC state home (`GcHome`,
//! crate-internal)
//! the gc-protocol task fills.
//!
//! **The durable delete window hosts on exactly one SQL-backed engine
//! per store instance (ADR-012 §5's one-window-host rule) — the kv
//! engine when it is SQL-backed, otherwise the large engine.** Landed
//! in three constructor stages. Sqlite and postgres — the SQL-backed
//! kv engines — host: their constructors run the GC schema's
//! engine-neutral call (`ensure_gc_schema` on the `EngineWindowState`
//! seam) at construction, dispatched off the engine representation
//! (`ensure_gc_schema_at_construction`). Pg-lo's dormancy landed
//! with the wave-5 wiring: on a dual-SQL instance (kv =
//! sqlite/postgres + large = pg-lo) the kv host's call runs and
//! pg-lo's window clauses are **dormant** (the pg-lo schema carries
//! no `gc_window`), while on the kv = mem + large = pg-lo shape the
//! **pg-lo host's** call
//! runs (`gc_window` in the pg-lo schema) — the rule's large-host
//! arm, the window's completion. The sweep's form branch keys off
//! the same representation (`sweep_window`'s kv-engine-enum match,
//! whose mem arm consults the large engine precisely when it hosts
//! the window — never a flag): the **durable form** (ADR-008 §2
//! staged re-arbitrated delete; ADR-012 §6.7 the executor is the
//! next sweep) on every window host, the inline form verbatim where
//! the window is in-process (mem/local, ADR-005 protocol inline).
//! Every SQL-backed-host constructor carries the delete-window delay
//! (ADR-008 §2: default 5 min, constructor-tunable per deployment —
//! [`DEFAULT_DELETE_WINDOW_DELAY`]; `0` is a legal degenerate tuning,
//! execute-on-next-sweep) as the cadence knob the durable form's
//! staging consumes (`visible_after = mark_end + window`) — on
//! either host; the mem/local constructors' inline form has no delay
//! in effect.
//!
//! The `#![allow(dead_code)]` below marks the seam's remaining dead
//! scope, not the GC protocol: the fleet machinery's full family and
//! every clause consumer have landed — the fleet declaration (the
//! constructors' predicate, parameters, and fleet state), the fleet
//! put path (`begin_joint_tx` → the engine's contribution →
//! `pin_commit`, with the pin guard's TTL/3 renewal duty), and the
//! fleet sweep (the sweep lease composing the in-process single-
//! flighter, `pin_reap` under it, the SQL-level delete arbitration
//! behind the in-process domain, the fleet liveness verb). What the
//! allow still covers: the fleet predicate/state items in
//! non-postgres builds (their consumers compile only with feature
//! `postgres`), the inline sweep form (mem builds' non-SQL posture —
//! the default and pg builds run the durable forms), the
//! `window_delay` introspection accessor (the construction tests'
//! instrument), and the feature-subset shapes the engine enums carry
//! for the constructor matrix.
//!
//! ## The directory layout (the split-store recording, wave 8)
//!
//! The flat `store.rs` was split into a directory module — pure moves,
//! the public surface (`lib.rs`'s re-export set) byte-unchanged. What
//! lives where:
//!
//! - this file (`mod.rs`): the module doc, the intra-store re-exports,
//!   the `Store` struct itself (the facade type every file programs
//!   against — the directory's spine), and the shared facade-boundary
//!   helpers every dispatch rides (`boundary_key`, `typed_keys`).
//! - `construct` — the constructor family and the introspection
//!   accessors, [`StoreMode`], the constructor-tunable defaults
//!   ([`DEFAULT_DELETE_WINDOW_DELAY`], `DEFAULT_PIN_TTL`), the
//!   shared-media declaration type (`FleetSharedMedia`), the
//!   composite fleet-validity predicate and its descriptors, the
//!   constructor-entry pool-sizing guard, and the construction-time
//!   GC-schema dispatch. The seven construction-themed test sections
//!   ride it.
//! - `engines` — the engine-side enum dispatch: the two tier enums
//!   (the engine-representation design point) and the cursor/staging
//!   dispatch enums (the sub-enum design points) between the facade
//!   and the backends.
//! - `put` — the facade put seam: the derivation-explicit
//!   known-length one-pass path, the unknown-length pre-threshold
//!   buffering path, the staged-put restart derivation, the `Batch`
//!   batching seam, the kv-only rejection point, and the `PUT_CHUNK`
//!   granularity constant.
//! - `read` — the read ops: `get`/`stat`/`read_range`/`has`/`list`,
//!   the fall-through general rule's consult order, verification-on-
//!   get, and the read-side key-boundary decode consumption.
//! - `fleet` — the fleet machinery: the fleet put path's store-core
//!   drivers (the joint entry+pin tx, the pin guard's TTL/3 renewal
//!   duty, the per-shape drivers), `FleetState`, and the
//!   store-instance id minting. The two fleet-put test sections ride
//!   it. **Boundary note (recorded):** the task's proposal put the
//!   fleet put driver in `put.rs` and the shared-media declaration in
//!   `construct.rs` — the landed shape keeps every fleet-put driver in
//!   one file (the drivers and their dispatch callers are severable
//!   only with visibility lifts on half the family) and the shared-
//!   media declaration with its constructor (its only caller).
//! - `sweep` — the GC protocol's facade surface: the liveness
//!   registration verbs, direct `delete`, `sweep`, the window forms
//!   (durable/inline/fleet), the sweep executor, and the GC glue
//!   (`arbitrated_delete`, `delete_everywhere`). **Boundary note
//!   (recorded):** the task's proposal put `delete` in `read.rs` —
//!   the landed shape keeps the whole GC protocol block (including
//!   the direct-delete verb) together: `delete` is not a fall-through
//!   read op but the arbitration domain's direct verb, and severing
//!   it from its arbitration machinery would lift visibility across
//!   the very state machine it refuses through. The two window/sweep
//!   test sections ride it.
//! - `tests` (cfg test) — the mem reference suite. **Boundary note
//!   (recorded):** the task's proposal split test sections per
//!   subject; the mem suite is one test module whose shared helper
//!   set spans every subject (construct/read/put/sweep mocks), so
//!   chopping it would rewrite its call sites — it rides the
//!   directory whole, mirroring the `ops/tests.rs` precedent, while
//!   every engine/feature-gated section rides its subject file.
//!
//! **`Dead-code posture (recorded, replacing the old blanket
//! `allow`)`:** the flat file carried a blanket
//! `#![allow(dead_code)]`; it is gone. The split's files carry no
//! allowance beyond per-item scoped ones whose consumer is named —
//! the standing hygiene discipline.

mod construct;
mod engines;
mod fleet;
mod put;
mod read;
mod sweep;
#[cfg(all(test, feature = "mem"))]
mod tests;

#[cfg(feature = "postgres")]
pub use construct::{FleetSharedMedia, DEFAULT_PIN_TTL};
pub use construct::{StoreMode, DEFAULT_DELETE_WINDOW_DELAY};
pub(crate) use engines::AnyCursor;
pub(crate) use fleet::FleetState;

use std::time::Duration;

use crate::gc::GcHome;
use crate::keys::Key;
use crate::Error;
use engines::{KvEngine, LargeEngine};

#[cfg(all(test, feature = "mem"))]
use crate::dispatch::Tier;
#[cfg(all(test, feature = "mem"))]
use crate::facade::LivenessSource;
#[cfg(all(test, feature = "mem"))]
use crate::facade::{EntryMeta, GcAbortCause, LivenessRegistration, ProtectCallback};
#[cfg(all(test, feature = "mem"))]
use construct::StoreMode::{DualTier, KvOnly, MemOnly};
#[cfg(all(test, feature = "mem"))]
use std::collections::BTreeSet;
#[cfg(all(test, feature = "mem"))]
use std::sync::Arc;
#[cfg(all(test, feature = "mem"))]
use tokio::io::AsyncRead;

/// The store instance — the public facade above the tier engines
/// (ADR-011 §1's vocabulary: *one running instantiation of the store
/// facade — one engine set per the constructor mode, plus dispatch and
/// GC state*; **the GC arbitration domain** for single-instance
/// topologies).
///
/// Engine-blind by construction: the engines are held in crate-internal
/// enums dispatched statically (see the module's engine-representation
/// design point); consumers name modes only. The GC state home
/// (`GcHome`, crate-internal) rides as a field — the store instance owns
/// all GC state (ADR-010 §2), which the gc-protocol task fills.
pub struct Store {
    /// The kv-tier engine — the first consult in every fall-through.
    kv: KvEngine,
    /// The large-tier engine — `None` in single-tier modes.
    large: Option<LargeEngine>,
    /// The dispatch threshold in effect where the mode has one; mem-only
    /// carries the shipped default but never consults it — "no dispatch
    /// threshold in effect" is the mode's ruling (ADR-011 §4), not a
    /// value absence.
    threshold: u64,
    /// The delete-window delay the constructor took (ADR-008 §2) —
    /// the durable window's cadence knob the sweep's durable form
    /// consumes (`window_stage`'s `visible_after = mark_end + window`
    /// on the `EngineWindowState` seam, on either host — the kv
    /// engine's or the large engine's). The in-process inline form
    /// has no delay in effect: those constructors (mem-only,
    /// kv-only(mem), dual-tier mem+local) carry
    /// [`DEFAULT_DELETE_WINDOW_DELAY`], never consulted.
    // consumer: the durable window's staging cadence (`sweep`'s
    // `visible_after` on the SQL-backed hosts); the mem-only build has
    // no SQL host, so the field rides unread there.
    #[allow(dead_code)]
    window_delay: Duration,
    /// The constructor mode; the dispatch/put/read/gc paths branch on it.
    mode: StoreMode,
    /// The GC state home (ADR-010 §2: the store core owns GC state)
    /// — the gc-protocol task's machinery lands here.
    gc: GcHome,
    /// The constructor's fleet declaration — `Some` exactly when a
    /// `_fleet` constructor declared `fleet: true` (never inferred,
    /// ADR-012 §3): the flag the put/sweep paths branch on (crate-
    /// internally, never re-derived from the engine set), the pin
    /// TTL, and the store-instance id (`FleetState`). Under
    /// `fleet: false` — every pre-existing constructor, behavior
    /// byte-unchanged — the ADR-005 in-process protocol applies
    /// verbatim regardless of engine.
    fleet: Option<FleetState>,
}

/// The facade's key-bytes decode: typed `Key` conversion at the store
/// boundary (ADR-002/003) — the one place on the read path where key
/// bytes are interpreted; anything malformed is `Error::KeyInvalid`,
/// never a guess, and never reaches an engine typed.
fn boundary_key(key: &[u8]) -> Result<Key, Error> {
    Key::from_bytes(key)
}

/// Constitute the facade's typed keys from an engine's opaque list
/// rows: a failed decode is fatal (`Error::KeyInvalid`) — a malformed
/// pool list is as deadly as an incomplete one and is never silently
/// skipped (ADR-003).
fn typed_keys(raws: Vec<Vec<u8>>) -> Result<Vec<Key>, Error> {
    raws.into_iter().map(|raw| boundary_key(&raw)).collect()
}