Skip to main content

ic_memory/ledger/
error.rs

1use crate::{
2    declaration::DeclarationSnapshotError,
3    key::{StableKey, StableKeyError},
4    ledger::LedgerPayloadEnvelopeError,
5    physical::CommitRecoveryError,
6    slot::MemoryManagerSlot,
7};
8
9///
10/// LedgerIntegrityError
11///
12/// Decoded ledger violates structural allocation-history invariants.
13///
14
15#[non_exhaustive]
16#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
17pub enum LedgerIntegrityError {
18    /// Current structural recovery ceiling exceeded.
19    #[error("ledger exceeds {resource} limit {limit}")]
20    LimitExceeded {
21        resource: &'static str,
22        limit: usize,
23    },
24    /// Stable key appears in more than one allocation record.
25    #[error("stable key '{stable_key}' appears in more than one allocation record")]
26    DuplicateStableKey {
27        /// Duplicate stable key.
28        stable_key: StableKey,
29    },
30    /// Allocation slot appears in more than one allocation record.
31    #[error("allocation slot '{slot:?}' appears in more than one allocation record")]
32    DuplicateSlot {
33        /// Duplicate allocation slot.
34        slot: MemoryManagerSlot,
35    },
36    /// Allocation record generation ordering is invalid.
37    #[error("stable key '{stable_key}' has first_generation after last_seen_generation")]
38    InvalidRecordGenerationOrder {
39        /// Stable key whose record is invalid.
40        stable_key: StableKey,
41        /// First generation in the record.
42        first_generation: u64,
43        /// Last seen generation in the record.
44        last_seen_generation: u64,
45    },
46    /// Allocation record points past the current generation.
47    #[error(
48        "stable key '{stable_key}' references generation {generation} after current generation {current_generation}"
49    )]
50    FutureRecordGeneration {
51        /// Stable key whose record is invalid.
52        stable_key: StableKey,
53        /// Generation referenced by the record.
54        generation: u64,
55        /// Current ledger generation.
56        current_generation: u64,
57    },
58    /// Retired generation predates the allocation record.
59    #[error("stable key '{stable_key}' was retired before first_generation")]
60    RetiredBeforeFirstGeneration {
61        /// Stable key whose record is invalid.
62        stable_key: StableKey,
63        /// First generation in the record.
64        first_generation: u64,
65        /// Retired generation in the record.
66        retired_generation: u64,
67    },
68    /// Retirement does not occur after the record's final observation.
69    #[error("stable key '{stable_key}' was retired at or before last_seen_generation")]
70    RetirementNotAfterLastSeen {
71        /// Stable key whose record is invalid.
72        stable_key: StableKey,
73        /// Latest generation that observed the allocation.
74        last_seen_generation: u64,
75        /// Generation that retired the allocation.
76        retired_generation: u64,
77    },
78    /// Allocation record has no schema metadata history.
79    #[error("stable key '{stable_key}' has empty schema metadata history")]
80    EmptySchemaHistory {
81        /// Stable key whose record is invalid.
82        stable_key: StableKey,
83    },
84    /// First schema metadata record does not begin with the allocation record.
85    #[error(
86        "stable key '{stable_key}' has schema metadata that does not begin at first_generation"
87    )]
88    SchemaHistoryStartMismatch {
89        /// Stable key whose record is invalid.
90        stable_key: StableKey,
91        /// Allocation's first committed generation.
92        first_generation: u64,
93        /// First schema metadata generation.
94        schema_generation: u64,
95    },
96    /// Schema metadata generation history is not strictly increasing.
97    #[error("stable key '{stable_key}' has non-increasing schema metadata generation history")]
98    NonIncreasingSchemaHistory {
99        /// Stable key whose record is invalid.
100        stable_key: StableKey,
101    },
102    /// Schema metadata generation is outside the allocation record lifetime.
103    #[error("stable key '{stable_key}' has schema metadata generation outside the ledger bounds")]
104    SchemaHistoryOutOfBounds {
105        /// Stable key whose record is invalid.
106        stable_key: StableKey,
107        /// Schema metadata generation.
108        generation: u64,
109    },
110    /// Schema metadata was recorded after the allocation was last observed.
111    #[error("stable key '{stable_key}' has schema metadata after last_seen_generation")]
112    SchemaHistoryAfterLastSeen {
113        /// Stable key whose record is invalid.
114        stable_key: StableKey,
115        /// Schema metadata generation.
116        generation: u64,
117        /// Latest generation that observed the allocation.
118        last_seen_generation: u64,
119    },
120    /// Generation record appears more than once.
121    #[error("generation {generation} appears more than once")]
122    DuplicateGeneration {
123        /// Duplicate generation.
124        generation: u64,
125    },
126    /// Generation record points past the current generation.
127    #[error("generation {generation} is after current generation {current_generation}")]
128    FutureGeneration {
129        /// Generation record value.
130        generation: u64,
131        /// Current ledger generation.
132        current_generation: u64,
133    },
134    /// Generation parent does not precede the child generation.
135    #[error("generation {generation} has invalid parent generation {parent_generation}")]
136    InvalidParentGeneration {
137        /// Generation record value.
138        generation: u64,
139        /// Invalid parent generation.
140        parent_generation: u64,
141    },
142    /// Current ledger generation has no committed generation record.
143    #[error("current generation {current_generation} has no committed generation record")]
144    MissingCurrentGenerationRecord {
145        /// Current ledger generation.
146        current_generation: u64,
147    },
148    /// Generation records are not strictly increasing in durable order.
149    #[error("generation records are not strictly increasing at generation {generation}")]
150    NonIncreasingGenerationRecords {
151        /// Non-increasing generation.
152        generation: u64,
153    },
154    /// Generation record parent does not match the previous committed generation.
155    #[error(
156        "generation {generation} does not link to previous committed generation {expected_parent}"
157    )]
158    BrokenGenerationChain {
159        /// Generation whose parent link is invalid.
160        generation: u64,
161        /// Expected parent generation.
162        expected_parent: u64,
163        /// Actual parent generation.
164        actual_parent: u64,
165    },
166    /// Allocation record refers to a generation absent from committed history.
167    #[error("stable key '{stable_key}' references unknown generation {generation}")]
168    UnknownRecordGeneration {
169        /// Stable key whose record is invalid.
170        stable_key: StableKey,
171        /// Unknown generation.
172        generation: u64,
173    },
174    /// Generation diagnostic metadata is invalid.
175    #[error(transparent)]
176    DiagnosticMetadata(DeclarationSnapshotError),
177}
178
179///
180/// LedgerCommitError
181///
182/// Failure to recover or commit a logical allocation ledger.
183#[non_exhaustive]
184#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
185pub enum LedgerCommitError {
186    /// Protected physical commit recovery failed.
187    #[error(transparent)]
188    Recovery(CommitRecoveryError),
189    /// Logical ledger payload envelope could not be decoded.
190    #[error(transparent)]
191    PayloadEnvelope(LedgerPayloadEnvelopeError),
192    /// Physical slot generation and decoded logical ledger generation disagree.
193    #[error(
194        "physical generation {physical_generation} does not match logical ledger generation {logical_generation}"
195    )]
196    PhysicalLogicalGenerationMismatch {
197        /// Generation encoded in the physical commit slot.
198        physical_generation: u64,
199        /// Generation decoded from the logical allocation ledger.
200        logical_generation: u64,
201    },
202    /// Built-in ledger decoding failed.
203    #[error("allocation ledger codec failed: {0}")]
204    Codec(String),
205    /// Decoded ledger violates structural allocation-history invariants.
206    #[error(transparent)]
207    Integrity(LedgerIntegrityError),
208}
209
210///
211/// AllocationStageError
212///
213/// Failure to stage a validated allocation generation.
214///
215
216#[non_exhaustive]
217#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
218pub enum AllocationStageError {
219    #[error(transparent)]
220    Integrity(#[from] LedgerIntegrityError),
221    /// Validated declarations were produced against a different ledger generation.
222    #[error(
223        "validated allocations were produced at generation {validated_generation}, but ledger is at generation {ledger_generation}"
224    )]
225    StaleValidatedAllocations {
226        /// Generation carried by the validated allocation capability.
227        validated_generation: u64,
228        /// Current ledger generation.
229        ledger_generation: u64,
230    },
231    /// Stable key was historically bound to a different slot.
232    #[error("stable key '{stable_key}' was historically bound to a different allocation slot")]
233    StableKeySlotConflict {
234        /// Stable key being declared.
235        stable_key: StableKey,
236        /// Historical slot for the stable key.
237        historical_slot: MemoryManagerSlot,
238        /// Slot claimed by the declaration.
239        declared_slot: MemoryManagerSlot,
240    },
241    /// Slot was historically bound to a different stable key.
242    #[error("allocation slot '{slot:?}' was historically bound to stable key '{historical_key}'")]
243    SlotStableKeyConflict {
244        /// Slot being declared.
245        slot: MemoryManagerSlot,
246        /// Historical stable key for the slot.
247        historical_key: StableKey,
248        /// Stable key claimed by the declaration.
249        declared_key: StableKey,
250    },
251    /// Current declaration attempted to revive a retired allocation.
252    #[error("stable key '{stable_key}' was explicitly retired and cannot be redeclared")]
253    RetiredAllocation {
254        /// Retired stable key.
255        stable_key: StableKey,
256        /// Retired allocation slot.
257        slot: MemoryManagerSlot,
258    },
259}
260
261///
262/// AllocationReservationError
263///
264/// Failure to stage a reservation generation.
265#[non_exhaustive]
266#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
267pub enum AllocationReservationError {
268    #[error(transparent)]
269    Integrity(#[from] LedgerIntegrityError),
270    /// Ledger generation cannot be advanced without overflow.
271    #[error("ledger generation {generation} cannot be advanced without overflow")]
272    GenerationOverflow {
273        /// Current ledger generation.
274        generation: u64,
275    },
276    /// Reservation count exceeds the usable allocation-slot domain.
277    #[error("generation contains {count} reservations, exceeding the 255-allocation limit")]
278    TooManyReservations {
279        /// Number of reservations in the staged generation.
280        count: usize,
281    },
282    /// A staged reservation declaration violates declaration invariants.
283    #[error("reservation declaration is invalid")]
284    InvalidDeclaration(#[source] DeclarationSnapshotError),
285    /// Stable key was historically bound to a different slot.
286    #[error("stable key '{stable_key}' was historically bound to a different allocation slot")]
287    StableKeySlotConflict {
288        /// Stable key being reserved.
289        stable_key: StableKey,
290        /// Historical slot for the stable key.
291        historical_slot: MemoryManagerSlot,
292        /// Slot claimed by the reservation.
293        reserved_slot: MemoryManagerSlot,
294    },
295    /// Slot was historically bound to a different stable key.
296    #[error("allocation slot '{slot:?}' was historically bound to stable key '{historical_key}'")]
297    SlotStableKeyConflict {
298        /// Slot being reserved.
299        slot: MemoryManagerSlot,
300        /// Historical stable key for the slot.
301        historical_key: StableKey,
302        /// Stable key claimed by the reservation.
303        reserved_key: StableKey,
304    },
305    /// Allocation already exists as an active record.
306    #[error("stable key '{stable_key}' is already active and cannot be reserved")]
307    ActiveAllocation {
308        /// Active stable key.
309        stable_key: StableKey,
310        /// Active allocation slot.
311        slot: MemoryManagerSlot,
312    },
313    /// Allocation was already retired and cannot be reserved.
314    #[error("stable key '{stable_key}' was explicitly retired and cannot be reserved")]
315    RetiredAllocation {
316        /// Retired stable key.
317        stable_key: StableKey,
318        /// Retired allocation slot.
319        slot: MemoryManagerSlot,
320    },
321}
322
323///
324/// AllocationRetirementError
325///
326/// Failure to stage an explicit retirement generation.
327#[non_exhaustive]
328#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
329pub enum AllocationRetirementError {
330    #[error(transparent)]
331    Integrity(#[from] LedgerIntegrityError),
332    /// Stable-key grammar failure.
333    #[error(transparent)]
334    Key(StableKeyError),
335    /// Ledger generation cannot be advanced without overflow.
336    #[error("ledger generation {generation} cannot be advanced without overflow")]
337    GenerationOverflow {
338        /// Current ledger generation.
339        generation: u64,
340    },
341    /// Stable key has no historical allocation record.
342    #[error("stable key '{0}' has no allocation record to retire")]
343    UnknownStableKey(StableKey),
344    /// Stable key was historically bound to a different slot.
345    #[error("stable key '{stable_key}' cannot be retired for a different allocation slot")]
346    SlotMismatch {
347        /// Stable key being retired.
348        stable_key: StableKey,
349        /// Historical slot for the stable key.
350        historical_slot: MemoryManagerSlot,
351        /// Slot named by the retirement request.
352        retired_slot: MemoryManagerSlot,
353    },
354    /// Allocation was already retired.
355    #[error("stable key '{stable_key}' was already retired")]
356    AlreadyRetired {
357        /// Retired stable key.
358        stable_key: StableKey,
359        /// Retired allocation slot.
360        slot: MemoryManagerSlot,
361    },
362}