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
// SPDX-License-Identifier: BUSL-1.1
//! Synchronous hard-purge used by the re-CREATE path.
//!
//! `CREATE COLLECTION` of a name that already exists but is
//! soft-deleted (`is_active == false`) is an explicit request for a
//! fresh collection — distinct from `UNDROP` recovery. Soft-delete
//! keeps the old rows under the same `{db}:{tenant}:{name}:` storage
//! prefix for the retention window; re-creating the name before GC
//! runs would resurrect those stale rows. Before the new collection
//! can register over the reused prefix, the old catalog row AND the
//! old Data Plane storage keys must be gone.
//!
//! This composes the two halves the `DROP COLLECTION ... PURGE`
//! applier runs on every node — the catalog-row removal
//! (`apply::collection::purge`) and the storage reclaim
//! (`post_apply::async_dispatch::collection::reclaim_collection_storage`) —
//! into one awaitable the
//! Control Plane can drive inline before it proceeds to persist the
//! new collection. No engine is special-cased: the reclaim dispatch
//! covers every engine exactly as `DROP ... PURGE` does.
use crateReclaimFailure;
use crateSharedState;
/// Hard-purge `(tenant_id, name)`: remove the catalog metadata row
/// (primary `StoredCollection` + owner + surrogate map) and reclaim
/// every engine's Data Plane storage for the collection, awaiting
/// completion so both are done before the caller proceeds.
///
/// `purge_lsn` is the WAL tombstone boundary: writes with
/// `lsn < purge_lsn` for this collection are shadowed on replay, so
/// callers pass the current WAL `next_lsn` — every pre-drop row sits
/// below it while every post-CREATE row sits at or above it.
///
/// Returns `Err` if the catalog-row removal OR the engine storage
/// reclaim fails, so the re-CREATE caller can ABORT rather than register
/// a new collection over un-purged data (the failure-path resurrection
/// hole). This interactive path is fail-closed on both halves.
///
/// The storage-reclaim half (`reclaim_collection_storage`) is
/// result-checked: its correctness-critical redb + versioned engine
/// purge propagates a failure here (and also records a durable
/// `_system.pending_reclaim` entry so the purge is retried
/// at-least-once even though this caller aborts). Its best-effort
/// substeps (WAL tombstone, redb tombstone, L2 enqueue, quiesce drain,
/// Lite broadcast) still log-and-continue.
pub async