diff --git a/server/src/engine/error.rs b/server/src/engine/error.rs index 8513fe19..30e4bdfb 100644 --- a/server/src/engine/error.rs +++ b/server/src/engine/error.rs @@ -169,11 +169,11 @@ enumerate_err! { /// Errors that occur when restoring transactional data pub enum TransactionError { /// corrupted txn payload. has more bytes than expected - DecodeCorruptedPayloadMoreBytes = "txn-payload-unexpected-content", + V1DecodeCorruptedPayloadMoreBytes = "txn-payload-unexpected-content", /// transaction payload is corrupted. has lesser bytes than expected - DecodedUnexpectedEof = "txn-payload-unexpected-eof", + V1DecodedUnexpectedEof = "txn-payload-unexpected-eof", /// unknown transaction operation. usually indicates a corrupted payload - DecodeUnknownTxnOp = "txn-payload-unknown-payload", + V1DecodeUnknownTxnOp = "txn-payload-unknown-payload", /// While restoring a certain item, a non-resolvable conflict was encountered in the global state, because the item was /// already present (when it was expected to not be present) OnRestoreDataConflictAlreadyExists = "txn-payload-conflict-already-exists", @@ -246,7 +246,12 @@ enumerate_err! { RawJournalDecodeInvalidEvent = "journal-invalid-event-order", /// corrupted event within a batch RawJournalDecodeCorruptionInBatchMetadata = "journal-batch-corrupted-event-metadata", - /// runtime error: the lightweight heartbeat failed - RawJournalRuntimeCriticalLwtHBFail = "journal-lwt-heartbeat-failed", + /* + ---- + runtime errors + ---- + */ + RawJournalRuntimeHeartbeatFail = "journal-lwt-heartbeat-failed", + RawJournalRuntimeDirty = "journal-in-dirty-state", } } diff --git a/server/src/engine/fractal/mgr.rs b/server/src/engine/fractal/mgr.rs index 71fb33ce..09924415 100644 --- a/server/src/engine/fractal/mgr.rs +++ b/server/src/engine/fractal/mgr.rs @@ -33,7 +33,7 @@ use { EntityIDRef, }, data::uuid::Uuid, - error::ErrorKind, + error::StorageError, fractal::GlobalInstanceLike, storage::{ safe_interfaces::{paths_v1, StdModelBatch}, @@ -528,9 +528,7 @@ impl FractalMgr { if mdl_driver_.status().is_iffy() { // don't mess this up any further return Err(( - super::error::Error::from(ErrorKind::Other( - "model driver is in dirty state".into(), - )), + super::error::Error::from(StorageError::RawJournalRuntimeDirty), BatchStats::into_inner(BatchStats::new()), )); } diff --git a/server/src/engine/storage/v1/raw/journal/mod.rs b/server/src/engine/storage/v1/raw/journal/mod.rs index 23f58368..ef14c5ec 100644 --- a/server/src/engine/storage/v1/raw/journal/mod.rs +++ b/server/src/engine/storage/v1/raw/journal/mod.rs @@ -55,7 +55,7 @@ impl JournalAdapter for GNSAdapter { fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> RuntimeResult<()> { macro_rules! dispatch { ($($item:ty),* $(,)?) => { - [$(<$item as GNSEvent>::decode_and_update_global_state),*, |_, _| Err(TransactionError::DecodeUnknownTxnOp.into())] + [$(<$item as GNSEvent>::decode_and_update_global_state),*, |_, _| Err(TransactionError::V1DecodeUnknownTxnOp.into())] }; } static DISPATCH: [fn(&mut BufferedScanner, &GNSData) -> RuntimeResult<()>; 9] = dispatch!( @@ -69,7 +69,7 @@ impl JournalAdapter for GNSAdapter { gns::model::DropModelTxn ); if payload.len() < 2 { - return Err(TransactionError::DecodedUnexpectedEof.into()); + return Err(TransactionError::V1DecodedUnexpectedEof.into()); } let mut scanner = BufferedScanner::new(&payload); let opc = unsafe { @@ -78,7 +78,7 @@ impl JournalAdapter for GNSAdapter { }; match DISPATCH[(opc as usize).min(DISPATCH.len())](&mut scanner, gs) { Ok(()) if scanner.eof() => return Ok(()), - Ok(_) => Err(TransactionError::DecodeCorruptedPayloadMoreBytes.into()), + Ok(_) => Err(TransactionError::V1DecodeCorruptedPayloadMoreBytes.into()), Err(e) => Err(e), } } diff --git a/server/src/engine/storage/v2/raw/journal/raw/mod.rs b/server/src/engine/storage/v2/raw/journal/raw/mod.rs index 9736b026..03a1dadd 100644 --- a/server/src/engine/storage/v2/raw/journal/raw/mod.rs +++ b/server/src/engine/storage/v2/raw/journal/raw/mod.rs @@ -30,7 +30,7 @@ mod tests; use { crate::{ engine::{ - error::{ErrorKind, StorageError}, + error::{ErrorKind, StorageError, TransactionError}, mem::unsafe_apis::memcpy, storage::common::{ checksum::SCrc64, @@ -577,7 +577,7 @@ impl RawJournalWriter { Ok(()) } else { // so, the on-disk file probably has some partial state. this is bad. throw an error - Err(StorageError::RawJournalRuntimeCriticalLwtHBFail.into()) + Err(StorageError::RawJournalRuntimeHeartbeatFail.into()) } } } @@ -756,14 +756,50 @@ impl RawJournalReader { match e.kind() { ErrorKind::IoError(io) => match io.kind() { IoErrorKind::UnexpectedEof => { - // this is the only kind of error that we can actually repair since it indicates that a part of the - // file is "missing" + /* + this is the only kind of error that we can actually repair since it indicates that a part of the + file is "missing." we can't deal with things like permission errors. that's supposed to be handled + by the admin by looking through the error logs + */ } _ => return Err(e), }, - ErrorKind::Storage(_) => todo!(), - ErrorKind::Txn(_) => todo!(), - ErrorKind::Other(_) => todo!(), + ErrorKind::Storage(e) => match e { + // unreachable errors (no execution path here) + StorageError::RawJournalRuntimeHeartbeatFail // can't reach runtime error before driver start + | StorageError::RawJournalRuntimeDirty + | StorageError::FileDecodeHeaderVersionMismatch // should be caught earlier + | StorageError::FileDecodeHeaderCorrupted // should be caught earlier + | StorageError::V1JournalDecodeLogEntryCorrupted // v1 errors can't be raised here + | StorageError::V1JournalDecodeCorrupted + | StorageError::V1DataBatchDecodeCorruptedBatch + | StorageError::V1DataBatchDecodeCorruptedEntry + | StorageError::V1DataBatchDecodeCorruptedBatchFile + | StorageError::V1SysDBDecodeCorrupted + | StorageError::V1DataBatchRuntimeCloseError => unreachable!(), + // possible errors + StorageError::InternalDecodeStructureCorrupted + | StorageError::InternalDecodeStructureCorruptedPayload + | StorageError::InternalDecodeStructureIllegalData + | StorageError::RawJournalDecodeEventCorruptedMetadata + | StorageError::RawJournalDecodeEventCorruptedPayload + | StorageError::RawJournalDecodeBatchContentsMismatch + | StorageError::RawJournalDecodeBatchIntegrityFailure + | StorageError::RawJournalDecodeInvalidEvent + | StorageError::RawJournalDecodeCorruptionInBatchMetadata => {} + }, + ErrorKind::Txn(txerr) => match txerr { + // unreachable errors + TransactionError::V1DecodeCorruptedPayloadMoreBytes // no v1 errors + | TransactionError::V1DecodedUnexpectedEof + | TransactionError::V1DecodeUnknownTxnOp => unreachable!(), + // possible errors + TransactionError::OnRestoreDataConflictAlreadyExists | + TransactionError::OnRestoreDataMissing | + TransactionError::OnRestoreDataConflictMismatch => {}, + }, + // these errors do not have an execution pathway + ErrorKind::Other(_) => unreachable!(), ErrorKind::Config(_) => unreachable!(), } } @@ -857,7 +893,7 @@ impl RawJournalReader { self.stats.driver_events += 1; // update Self::__refresh_known_txn(self); - // full metadata validated; this is a valid close event but is it actually a close + // full metadata validated; this is a valid close event, but is it actually a close? if self.tr.is_eof() { jtrace_reader!(ClosedAndReachedEof); // yes, we're done diff --git a/server/src/engine/storage/v2/raw/journal/raw/tests.rs b/server/src/engine/storage/v2/raw/journal/raw/tests.rs index 341494aa..42c8d5eb 100644 --- a/server/src/engine/storage/v2/raw/journal/raw/tests.rs +++ b/server/src/engine/storage/v2/raw/journal/raw/tests.rs @@ -177,7 +177,9 @@ impl RawJournalAdapter for SimpleDBJournal { file.tracked_read(&mut keybuf)?; match String::from_utf8(keybuf) { Ok(k) => gs.data.borrow_mut().push(k), - Err(_) => return Err(StorageError::RawJournalDecodeEventCorruptedPayload.into()), + Err(_) => { + return Err(StorageError::RawJournalDecodeEventCorruptedPayload.into()) + } } } EventMeta::Clear => gs.data.borrow_mut().clear(),