use liminal::durability::DurabilityError;
use liminal_protocol::{
lifecycle::{ConnectionIncarnationAllocatorRestoreError, DurableIncarnationReferencesError},
wire::ConnectionIncarnation,
};
#[derive(Debug, thiserror::Error)]
pub(in crate::server) enum IncarnationStreamError {
#[error(transparent)]
Durability(#[from] DurabilityError),
#[error("incarnation event sequence mismatch: expected {expected}, got {actual}")]
EventSequence {
expected: u64,
actual: u64,
},
#[error("incarnation append sequence mismatch: expected {expected}, got {actual}")]
AssignedSequence {
expected: u64,
actual: u64,
},
#[error("incarnation event stream sequence exhausted at u64::MAX")]
StreamSequenceExhausted,
#[error("incarnation event truncated: required {required} bytes, got {actual}")]
EventTruncated {
required: usize,
actual: usize,
},
#[error("incarnation event magic mismatch")]
EventMagic,
#[error("unsupported incarnation event schema version {0}")]
EventSchemaVersion(u8),
#[error("unknown incarnation event kind {0}")]
EventKind(u8),
#[error("incarnation event body length mismatch: declared {declared}, actual {actual}")]
EventBodyLength {
declared: u32,
actual: usize,
},
#[error("incarnation event kind {kind} requires schema version 2, got {version}")]
EventKindSchemaVersion {
version: u8,
kind: u8,
},
#[error("connection-fate conversation count {count} exceeds platform usize")]
ConversationCountPlatformOverflow {
count: u64,
},
#[error("connection-fate conversation bound {bound} exceeds platform usize")]
ConversationBoundPlatformOverflow {
bound: u64,
},
#[error("connection-fate conversation count {actual} exceeds declared bound {maximum}")]
ConversationCountExceedsBound {
actual: usize,
maximum: usize,
},
#[error(
"connection-fate Open body length mismatch for {count} conversations: expected {expected}, actual {actual}"
)]
OpenConnectionFateBodyLength {
count: u64,
expected: usize,
actual: usize,
},
#[error("connection-fate Open conversation ids are not strictly increasing at index {index}")]
ConversationsNotStrictlyIncreasing {
index: usize,
},
#[error("unknown connection-fate class {0}")]
ConnectionFateClass(u8),
#[error("connection-fate Complete body length mismatch: expected 8, actual {actual}")]
CompleteConnectionFateBodyLength {
actual: usize,
},
#[error(
"startup generation {startup_sequence} allocation {stored_sequence} changed declared reference bound from {expected} to {actual}"
)]
GenerationReferenceBoundConflict {
startup_sequence: u64,
expected: usize,
actual: usize,
stored_sequence: u64,
},
#[error(
"connection-fate Open {open_sequence} names unknown allocation {connection_incarnation:?}"
)]
UnknownConnectionAllocation {
open_sequence: u64,
connection_incarnation: ConnectionIncarnation,
},
#[error(
"connection-fate Open {open_sequence} resolves to startup generation {actual_startup_sequence}, but unmatched Opens belong to {expected_startup_sequence}"
)]
CrossGenerationOpen {
open_sequence: u64,
expected_startup_sequence: u64,
actual_startup_sequence: u64,
},
#[error(
"connection-fate Open {open_sequence} duplicates connection from unmatched Open {existing_open_sequence}"
)]
DuplicateConnectionOpen {
open_sequence: u64,
existing_open_sequence: u64,
},
#[error("connection-fate Complete {complete_sequence} names absent Open {open_sequence}")]
CompleteForAbsentOpen {
complete_sequence: u64,
open_sequence: u64,
},
#[error(
"startup generation {startup_sequence} connection-fate active count {actual} exceeds persisted bound {bound} at Open {open_sequence}"
)]
HistoricalConnectionFateBoundExceeded {
startup_sequence: u64,
bound: usize,
actual: usize,
open_sequence: u64,
},
#[error("connection-fate recovery names absent Open {open_sequence}")]
RecoveryCompleteForAbsentOpen { open_sequence: u64 },
#[error("connection-fate recovery still has {count} unmatched Opens")]
RecoveryIncomplete { count: usize },
#[error("incarnation startup event carried {actual} body bytes")]
StartupBodyLength {
actual: usize,
},
#[error("incarnation allocation reference count {count} exceeds platform usize")]
ReferenceCountPlatformOverflow {
count: u64,
},
#[error("incarnation allocation reference bound {bound} exceeds platform usize")]
ReferenceBoundPlatformOverflow {
bound: u64,
},
#[error("incarnation allocation reference count {actual} exceeds stored bound {maximum}")]
StoredReferenceCountExceedsBound {
actual: usize,
maximum: usize,
},
#[error(
"incarnation allocation body length mismatch for {count} references: expected {expected}, actual {actual}"
)]
AllocateBodyLength {
count: u64,
expected: usize,
actual: usize,
},
#[error("incarnation event encoded length overflow")]
EventLengthOverflow,
#[error("incarnation allocation event {stored_sequence} precedes first startup")]
AllocateBeforeStartup {
stored_sequence: u64,
},
#[error("incarnation startup event {stored_sequence} follows server exhaustion")]
StartupAfterServerExhaustion {
stored_sequence: u64,
},
#[error("incarnation allocation event {stored_sequence} follows ordinal exhaustion")]
AllocateAfterOrdinalExhaustion {
stored_sequence: u64,
},
#[error("incarnation event history has no startup")]
MissingStartup,
#[error(
"incarnation stream advanced from server incarnation {expected} to {actual}: another process owns it"
)]
ResumeServerIncarnationMoved {
expected: u64,
actual: u64,
},
#[error(
"incarnation epoch {epoch_server_incarnation} replayed to server incarnation {replayed_server_incarnation}"
)]
EpochHeaderMismatch {
epoch_server_incarnation: u64,
replayed_server_incarnation: u64,
},
#[cfg(test)]
#[error("incarnation event history retains {count} unmatched connection-fate Opens")]
UnmatchedConnectionFates { count: usize },
#[error("protocol rejected incarnation allocator header: {0:?}")]
AllocatorRestore(ConnectionIncarnationAllocatorRestoreError),
#[error("protocol rejected durable incarnation references: {0:?}")]
DurableReferences(DurableIncarnationReferencesError),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::server) enum DurableWriteReach {
NotAttempted,
Ambiguous,
}
#[derive(Debug, thiserror::Error)]
#[error("{error}")]
pub(in crate::server) struct IncarnationOperationError {
pub(in crate::server) reach: DurableWriteReach,
pub(in crate::server) error: IncarnationStreamError,
}
impl IncarnationOperationError {
pub(super) const fn not_attempted(error: IncarnationStreamError) -> Self {
Self {
reach: DurableWriteReach::NotAttempted,
error,
}
}
pub(super) const fn ambiguous(error: IncarnationStreamError) -> Self {
Self {
reach: DurableWriteReach::Ambiguous,
error,
}
}
}
impl From<IncarnationStreamError> for IncarnationOperationError {
fn from(error: IncarnationStreamError) -> Self {
Self::not_attempted(error)
}
}
impl From<ConnectionIncarnationAllocatorRestoreError> for IncarnationOperationError {
fn from(error: ConnectionIncarnationAllocatorRestoreError) -> Self {
Self::not_attempted(error.into())
}
}
impl From<DurableIncarnationReferencesError> for IncarnationOperationError {
fn from(error: DurableIncarnationReferencesError) -> Self {
Self::not_attempted(error.into())
}
}
impl From<IncarnationOperationError> for IncarnationStreamError {
fn from(failure: IncarnationOperationError) -> Self {
failure.error
}
}
impl From<ConnectionIncarnationAllocatorRestoreError> for IncarnationStreamError {
fn from(error: ConnectionIncarnationAllocatorRestoreError) -> Self {
Self::AllocatorRestore(error)
}
}
impl From<DurableIncarnationReferencesError> for IncarnationStreamError {
fn from(error: DurableIncarnationReferencesError) -> Self {
Self::DurableReferences(error)
}
}