use super::{
ConversationOutboxError, DurabilityError, Instant, OperationLogError, OutboxLogError,
ParticipantSemanticError, StateError, WriteIssuance,
};
pub(in super::super) fn elapsed_ms(started: Instant) -> u64 {
u64::try_from(started.elapsed().as_millis()).unwrap_or(u64::MAX)
}
const fn unsettled_write(error: &DurabilityError) -> bool {
match error {
DurabilityError::EmptyOperationBatch
| DurabilityError::BatchUnsupported { .. }
| DurabilityError::AppendUnresolved { .. }
| DurabilityError::PendingAppendUnreadable { .. }
| DurabilityError::OutcomeUnknown { .. }
| DurabilityError::NotStarted { .. }
| DurabilityError::StoreError(_)
| DurabilityError::SequenceConflict { .. }
| DurabilityError::CursorRegression { .. }
| DurabilityError::DedupCollision { .. }
| DurabilityError::ConfigError(_)
| DurabilityError::EphemeralStoreOpen(_)
| DurabilityError::EphemeralStoreDetached
| DurabilityError::TruncateUnsupported { .. }
| DurabilityError::RoutedScanUnsupported { .. }
| DurabilityError::RoutedRemoveUnsupported { .. }
| DurabilityError::RoutedDeleteUnsupported { .. }
| DurabilityError::EnvelopeError(_) => true,
}
}
const fn unsettled_outbox_write(error: &OutboxLogError) -> bool {
matches!(error, OutboxLogError::Durability(error) if unsettled_write(error))
}
pub(in super::super) fn state_error(error: &StateError) -> ParticipantSemanticError {
let issued = match error {
StateError::Log(error) => {
matches!(error, OperationLogError::Durability(error) if unsettled_write(error))
}
StateError::OutboxLog(error) => unsettled_outbox_write(error),
StateError::Outbox(error) => {
matches!(error, ConversationOutboxError::Log(error) if unsettled_outbox_write(error))
}
StateError::ExtensionRowsOwed { .. } | StateError::ExtensionRowUnconfirmed { .. } => true,
StateError::Bridge(_)
| StateError::Facts(_)
| StateError::ObserverProgressConformance(_)
| StateError::FateOccurrenceConflict(_)
| StateError::ShellRefused { .. }
| StateError::ShellUnavailable
| StateError::FrontierUnavailable
| StateError::ObligationDebtOwner { .. }
| StateError::ReplayedEventDrift { .. }
| StateError::ReplayedDiedTerminalHistory { .. }
| StateError::BindingTerminalAdmissionRefused { .. }
| StateError::ConversationSealed { .. }
| StateError::PresentedRefusal(_)
| StateError::Invariant { .. }
| StateError::AllocationExhausted { .. } => false,
};
classify_state_error(error, issued)
}
pub(super) fn state_error_with_issuance(
error: &StateError,
issuance: &WriteIssuance,
) -> ParticipantSemanticError {
classify_state_error(error, issuance.was_submitted())
}
fn classify_state_error(error: &StateError, issued: bool) -> ParticipantSemanticError {
let uncertain = match error {
StateError::ReplayedDiedTerminalHistory { .. } => false,
StateError::BindingTerminalAdmissionRefused { error } => {
return ParticipantSemanticError::BindingTerminalAdmissionRefused { error: *error };
}
StateError::ConversationSealed { conversation_id }
| StateError::Log(OperationLogError::ConversationRetired { conversation_id }) => {
return ParticipantSemanticError::ConversationSealed {
conversation_id: *conversation_id,
};
}
StateError::ExtensionRowsOwed { .. }
| StateError::ExtensionRowUnconfirmed { .. }
| StateError::Log(_)
| StateError::Outbox(_)
| StateError::OutboxLog(_)
| StateError::Bridge(_)
| StateError::Facts(_)
| StateError::ObserverProgressConformance(_)
| StateError::FateOccurrenceConflict(_)
| StateError::ShellRefused { .. }
| StateError::ShellUnavailable
| StateError::FrontierUnavailable
| StateError::ObligationDebtOwner { .. }
| StateError::ReplayedEventDrift { .. }
| StateError::PresentedRefusal(_)
| StateError::Invariant { .. }
| StateError::AllocationExhausted { .. } => issued,
};
let message = format!("participant production operation failed: {error}");
if uncertain {
ParticipantSemanticError::WriteOutcomeUnknown { message }
} else {
ParticipantSemanticError::Internal { message }
}
}
pub(in super::super) fn log_error(error: &OperationLogError) -> ParticipantSemanticError {
if let OperationLogError::ConversationRetired { conversation_id } = error {
return ParticipantSemanticError::ConversationSealed {
conversation_id: *conversation_id,
};
}
let message = format!("participant production log failed: {error}");
if matches!(error, OperationLogError::Durability(error) if unsettled_write(error)) {
return ParticipantSemanticError::WriteOutcomeUnknown { message };
}
ParticipantSemanticError::Internal { message }
}
#[cfg(test)]
pub(in super::super) fn outbox_log_error(error: &OutboxLogError) -> ParticipantSemanticError {
let message = format!("participant Unit 2 extension log failed: {error}");
if unsettled_outbox_write(error) {
return ParticipantSemanticError::WriteOutcomeUnknown { message };
}
ParticipantSemanticError::Internal { message }
}
pub(in super::super) fn restore_error(
error: &super::super::restore_checkpoint_rebuild::BootCheckpointError,
) -> ParticipantSemanticError {
if let super::super::restore_checkpoint_rebuild::BootCheckpointError::Retired {
conversation_id,
} = error
{
return ParticipantSemanticError::ConversationSealed {
conversation_id: *conversation_id,
};
}
ParticipantSemanticError::Internal {
message: format!("participant checkpoint restore failed: {error}"),
}
}
pub(in super::super) fn bridge_error(
error: &liminal::durability::bridge::BridgeError,
) -> ParticipantSemanticError {
ParticipantSemanticError::Internal {
message: format!("participant durability bridge failed: {error}"),
}
}