storage: Refine error pathways

next
Sayan Nandan 6 months ago
parent 37fc01d81f
commit 6fb3193465
No known key found for this signature in database
GPG Key ID: 0EBD769024B24F0A

@ -169,11 +169,11 @@ enumerate_err! {
/// Errors that occur when restoring transactional data /// Errors that occur when restoring transactional data
pub enum TransactionError { pub enum TransactionError {
/// corrupted txn payload. has more bytes than expected /// 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 /// 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 /// 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 /// 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) /// already present (when it was expected to not be present)
OnRestoreDataConflictAlreadyExists = "txn-payload-conflict-already-exists", OnRestoreDataConflictAlreadyExists = "txn-payload-conflict-already-exists",
@ -246,7 +246,12 @@ enumerate_err! {
RawJournalDecodeInvalidEvent = "journal-invalid-event-order", RawJournalDecodeInvalidEvent = "journal-invalid-event-order",
/// corrupted event within a batch /// corrupted event within a batch
RawJournalDecodeCorruptionInBatchMetadata = "journal-batch-corrupted-event-metadata", 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",
} }
} }

@ -33,7 +33,7 @@ use {
EntityIDRef, EntityIDRef,
}, },
data::uuid::Uuid, data::uuid::Uuid,
error::ErrorKind, error::StorageError,
fractal::GlobalInstanceLike, fractal::GlobalInstanceLike,
storage::{ storage::{
safe_interfaces::{paths_v1, StdModelBatch}, safe_interfaces::{paths_v1, StdModelBatch},
@ -528,9 +528,7 @@ impl FractalMgr {
if mdl_driver_.status().is_iffy() { if mdl_driver_.status().is_iffy() {
// don't mess this up any further // don't mess this up any further
return Err(( return Err((
super::error::Error::from(ErrorKind::Other( super::error::Error::from(StorageError::RawJournalRuntimeDirty),
"model driver is in dirty state".into(),
)),
BatchStats::into_inner(BatchStats::new()), BatchStats::into_inner(BatchStats::new()),
)); ));
} }

@ -55,7 +55,7 @@ impl JournalAdapter for GNSAdapter {
fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> RuntimeResult<()> { fn decode_and_update_state(payload: &[u8], gs: &Self::GlobalState) -> RuntimeResult<()> {
macro_rules! dispatch { macro_rules! dispatch {
($($item:ty),* $(,)?) => { ($($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!( static DISPATCH: [fn(&mut BufferedScanner, &GNSData) -> RuntimeResult<()>; 9] = dispatch!(
@ -69,7 +69,7 @@ impl JournalAdapter for GNSAdapter {
gns::model::DropModelTxn gns::model::DropModelTxn
); );
if payload.len() < 2 { if payload.len() < 2 {
return Err(TransactionError::DecodedUnexpectedEof.into()); return Err(TransactionError::V1DecodedUnexpectedEof.into());
} }
let mut scanner = BufferedScanner::new(&payload); let mut scanner = BufferedScanner::new(&payload);
let opc = unsafe { let opc = unsafe {
@ -78,7 +78,7 @@ impl JournalAdapter for GNSAdapter {
}; };
match DISPATCH[(opc as usize).min(DISPATCH.len())](&mut scanner, gs) { match DISPATCH[(opc as usize).min(DISPATCH.len())](&mut scanner, gs) {
Ok(()) if scanner.eof() => return Ok(()), Ok(()) if scanner.eof() => return Ok(()),
Ok(_) => Err(TransactionError::DecodeCorruptedPayloadMoreBytes.into()), Ok(_) => Err(TransactionError::V1DecodeCorruptedPayloadMoreBytes.into()),
Err(e) => Err(e), Err(e) => Err(e),
} }
} }

@ -30,7 +30,7 @@ mod tests;
use { use {
crate::{ crate::{
engine::{ engine::{
error::{ErrorKind, StorageError}, error::{ErrorKind, StorageError, TransactionError},
mem::unsafe_apis::memcpy, mem::unsafe_apis::memcpy,
storage::common::{ storage::common::{
checksum::SCrc64, checksum::SCrc64,
@ -577,7 +577,7 @@ impl<J: RawJournalAdapter> RawJournalWriter<J> {
Ok(()) Ok(())
} else { } else {
// so, the on-disk file probably has some partial state. this is bad. throw an error // 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<J: RawJournalAdapter> RawJournalReader<J> {
match e.kind() { match e.kind() {
ErrorKind::IoError(io) => match io.kind() { ErrorKind::IoError(io) => match io.kind() {
IoErrorKind::UnexpectedEof => { 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), _ => return Err(e),
}, },
ErrorKind::Storage(_) => todo!(), ErrorKind::Storage(e) => match e {
ErrorKind::Txn(_) => todo!(), // unreachable errors (no execution path here)
ErrorKind::Other(_) => todo!(), 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!(), ErrorKind::Config(_) => unreachable!(),
} }
} }
@ -857,7 +893,7 @@ impl<J: RawJournalAdapter> RawJournalReader<J> {
self.stats.driver_events += 1; self.stats.driver_events += 1;
// update // update
Self::__refresh_known_txn(self); 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() { if self.tr.is_eof() {
jtrace_reader!(ClosedAndReachedEof); jtrace_reader!(ClosedAndReachedEof);
// yes, we're done // yes, we're done

@ -177,7 +177,9 @@ impl RawJournalAdapter for SimpleDBJournal {
file.tracked_read(&mut keybuf)?; file.tracked_read(&mut keybuf)?;
match String::from_utf8(keybuf) { match String::from_utf8(keybuf) {
Ok(k) => gs.data.borrow_mut().push(k), 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(), EventMeta::Clear => gs.data.borrow_mut().clear(),

Loading…
Cancel
Save