Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

enhancement: WASM error codes, early exit on error or kill switch #1337

Merged
merged 23 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions packages/fuel-indexer-api-server/src/uses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ pub(crate) async fn remove_indexer(
}))
.await?;

// We have early termination on a kill switch. Yet, it is still possible for
ra0x3 marked this conversation as resolved.
Show resolved Hide resolved
// the database entries to be removed before the indexer has time to act on
// kill switch being triggered. Adding a small delay here should prevent
// unnecessary DatabaseError's appearing in the logs.
tokio::time::sleep(std::time::Duration::from_secs(1)).await;

// Allways remove data when removing an indexer.
if let Err(e) =
queries::remove_indexer(&mut conn, &namespace, &identifier, true).await
Expand Down
2 changes: 2 additions & 0 deletions packages/fuel-indexer-database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum IndexerDatabaseError {
Unknown,
#[error("You don't own this indexer.")]
NotYourIndexer,
#[error("No table mapping exists for TypeId({0:?})")]
ra0x3 marked this conversation as resolved.
Show resolved Hide resolved
TableMappingDoesNotExist(i64),
}

#[derive(Debug)]
Expand Down
64 changes: 64 additions & 0 deletions packages/fuel-indexer-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,70 @@ impl ExecutionSource {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WasmIndexerError {
DeserializationError = 1,
SerializationError,
PutObjectError,
UnableToSaveListType,
UninitializedMemory,
UnableToFetchLogString,
KillSwitch,
DatabaseError,
GeneralError,
}

impl From<i32> for WasmIndexerError {
fn from(value: i32) -> Self {
match value {
0 => unreachable!("WasmIndexerError index starts at 1"),
lostman marked this conversation as resolved.
Show resolved Hide resolved
1 => Self::DeserializationError,
2 => Self::SerializationError,
3 => Self::PutObjectError,
4 => Self::UnableToSaveListType,
5 => Self::UninitializedMemory,
6 => Self::UnableToFetchLogString,
7 => Self::KillSwitch,
8 => Self::DatabaseError,
_ => Self::GeneralError,
}
}
}

impl std::fmt::Display for WasmIndexerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SerializationError => {
write!(f, "Failed to serialize")
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
Self::DeserializationError => {
write!(f, "Failed to deserialize")
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
Self::UnableToSaveListType => {
write!(f, "Failed to save list")
}
Self::PutObjectError => {
write!(f, "Failed to save object")
}
Self::UninitializedMemory => {
write!(f, "Failed to create MemoryView for indexer")
}
Self::UnableToFetchLogString => {
write!(f, "Failed to fetch log string")
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
Self::KillSwitch => {
write!(f, "Kill switch has been triggered")
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
Self::DatabaseError => {
write!(f, "Failed performing a database operation")
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
Self::GeneralError => write!(f, "A WASM error occurred"),
lostman marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl std::error::Error for WasmIndexerError {}
lostman marked this conversation as resolved.
Show resolved Hide resolved

/// Return a fully qualified indexer namespace.
pub fn fully_qualified_namespace(namespace: &str, identifier: &str) -> String {
format!("{}_{}", namespace, identifier)
Expand Down
11 changes: 7 additions & 4 deletions packages/fuel-indexer-macros/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ impl From<ObjectDecoder> for TokenStream {
.map(|query| query.to_string())
.collect::<Vec<_>>();

d.lock().await.put_many_to_many_record(queries).await;
d.lock().await.put_many_to_many_record(queries).await.expect(&format!("Entity::save_many_to_many for {} failed.", stringify!(#ident)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I think here we should be logging the error messages (and .expect) as close to the actual culprit call as possible
  • In this case the culprit of this error would be fuel_indexer::database::Database:: put_many_to_many_record
    • If the error is logged/handled there, we don't need to handle it here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is handled in put_many_to_many_record. In WASM, it is a host function that returns Result<>, which triggers early termination.

The code here is for native execution. This expect achieves an equivalent behavior.

}
}
None => {}
Expand All @@ -846,12 +846,15 @@ impl From<ObjectDecoder> for TokenStream {
match &db {
Some(d) => {
match d.lock().await.get_object(Self::TYPE_ID, id.to_string()).await {
Some(bytes) => {
Ok(Some(bytes)) => {
let columns: Vec<FtColumn> = bincode::deserialize(&bytes).expect("Failed to deserialize Vec<FtColumn> for Entity::load.");
let obj = Self::from_row(columns);
Some(obj)
},
None => None,
Ok(None) => None,
Err(e) => {
panic!("Entity::load for {} failed.", stringify!(#ident))
}
}
}
None => None,
Expand All @@ -868,7 +871,7 @@ impl From<ObjectDecoder> for TokenStream {
Self::TYPE_ID,
self.to_row(),
serialize(&self.to_row())
).await;
).await.expect(&format!("Entity::save for {} failed.", stringify!(#ident)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here:

  • I think here we should be logging the error messages (and .expect) as close to the actual culprit call as possible
  • In this case the culprit of this error would be fuel_indexer::database::Database:: put_object
    • If the error is logged/handled there, we don't need to handle it here

Copy link
Contributor Author

@lostman lostman Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is handled in put_object. In WASM, it is a host function that returns Result<>, which triggers early termination.

The code here is for native execution. This expect achieves an equivalent behavior.

}
None => {},
}
Expand Down
Loading
Loading