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