From 675729d50eaaec7397b24a300ea6b152465f7957 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 25 Apr 2024 20:30:14 +0000 Subject: [PATCH 1/4] refactor: retry once when receiving some sporadic JSON-RPC errors --- crates/edr_eth/src/remote/client.rs | 66 +++++++++++++++++++---------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/crates/edr_eth/src/remote/client.rs b/crates/edr_eth/src/remote/client.rs index dcbef5018..8ea07540c 100644 --- a/crates/edr_eth/src/remote/client.rs +++ b/crates/edr_eth/src/remote/client.rs @@ -8,7 +8,7 @@ use std::{ time::{Duration, Instant}, }; -use futures::stream::StreamExt; +use futures::{future, stream::StreamExt, TryFutureExt}; use hyper::header::HeaderValue; pub use hyper::{header, http::Error as HttpError, HeaderMap}; use reqwest::Client as HttpClient; @@ -220,27 +220,39 @@ impl RpcClient { }) } - fn parse_response_str(response: &str) -> Result { - serde_json::from_str(response).map_err(|error| RpcClientError::InvalidResponse { - response: response.to_string(), - expected_type: std::any::type_name::(), + fn parse_response_str( + response: String, + ) -> Result, RpcClientError> { + serde_json::from_str(&response).map_err(|error| RpcClientError::InvalidResponse { + response, + expected_type: std::any::type_name::>(), error, }) } - fn extract_result( + async fn retry_on_sporadic_failure( + &self, + error: jsonrpc::Error, request: SerializedRequest, - response: String, ) -> Result { - let response: jsonrpc::Response = Self::parse_response_str(&response)?; + fn is_missing_trie_node_error(error: &jsonrpc::Error) -> bool { + error.code == -32000 && error.message.contains("missing trie node") + } - response - .data - .into_result() - .map_err(|error| RpcClientError::JsonRpcError { - error, - request: request.to_json_string(), - }) + let result = if is_missing_trie_node_error(&error) { + self.send_request_body(&request) + .await + .and_then(Self::parse_response_str)? + .data + .into_result() + } else { + Err(error) + }; + + result.map_err(|error| RpcClientError::JsonRpcError { + error, + request: request.to_json_string(), + }) } async fn make_cache_path(&self, cache_key: &str) -> Result { @@ -466,6 +478,21 @@ impl RpcClient { Ok(()) } + async fn send_request_and_extract_result( + &self, + request: SerializedRequest, + ) -> Result { + future::ready( + self.send_request_body(&request) + .await + .and_then(Self::parse_response_str)? + .data + .into_result(), + ) + .or_else(|error| async { self.retry_on_sporadic_failure(error, request).await }) + .await + } + #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))] async fn send_request_body( &self, @@ -549,10 +576,7 @@ impl RpcClient { #[cfg(feature = "tracing")] tracing::trace!("Cache miss: {}", method.name()); - let result: T = self - .send_request_body(&request) - .await - .and_then(|response| Self::extract_result(request, response))?; + let result: T = self.send_request_and_extract_result(request).await?; self.try_write_response_to_cache(&method, &result, &resolve_block_number) .await?; @@ -569,9 +593,7 @@ impl RpcClient { ) -> Result { let request = self.serialize_request(&method)?; - self.send_request_body(&request) - .await - .and_then(|response| Self::extract_result(request, response)) + self.send_request_and_extract_result(request).await } /// Calls `eth_blockNumber` and returns the block number. From 0d5d6ace0eb3c3a8cdf4a3d8969596b3484f57de Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 25 Apr 2024 21:08:54 +0000 Subject: [PATCH 2/4] chore: add changeset --- .changeset/old-crews-remain.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/old-crews-remain.md diff --git a/.changeset/old-crews-remain.md b/.changeset/old-crews-remain.md new file mode 100644 index 000000000..b93af64cd --- /dev/null +++ b/.changeset/old-crews-remain.md @@ -0,0 +1,5 @@ +--- +"@nomicfoundation/edr": patch +--- + +Added retry for sporadic "missing trie node" JSON-RPC errors From 0519bc68a5dbed5ac60ba299d30712f5cb63e233 Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 26 Apr 2024 22:11:01 +0000 Subject: [PATCH 3/4] misc: apply review suggestions --- crates/edr_eth/src/remote/client.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/edr_eth/src/remote/client.rs b/crates/edr_eth/src/remote/client.rs index 8ea07540c..238f25e17 100644 --- a/crates/edr_eth/src/remote/client.rs +++ b/crates/edr_eth/src/remote/client.rs @@ -235,11 +235,10 @@ impl RpcClient { error: jsonrpc::Error, request: SerializedRequest, ) -> Result { - fn is_missing_trie_node_error(error: &jsonrpc::Error) -> bool { - error.code == -32000 && error.message.contains("missing trie node") - } + let is_missing_trie_node_error = + error.code == -32000 && error.message.contains("missing trie node"); - let result = if is_missing_trie_node_error(&error) { + let result = if is_missing_trie_node_error { self.send_request_body(&request) .await .and_then(Self::parse_response_str)? @@ -489,6 +488,8 @@ impl RpcClient { .data .into_result(), ) + // We retry at the application level because Alchemy has sporadic failures that are returned + // in the JSON-RPC layer .or_else(|error| async { self.retry_on_sporadic_failure(error, request).await }) .await } From 0449e1b1f8b2e453d48c8be7a1db3069c734514f Mon Sep 17 00:00:00 2001 From: Wodann Date: Mon, 6 May 2024 17:51:47 +0000 Subject: [PATCH 4/4] misc: apply review suggestion --- crates/edr_eth/src/remote/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/edr_eth/src/remote/client.rs b/crates/edr_eth/src/remote/client.rs index 238f25e17..6c3609052 100644 --- a/crates/edr_eth/src/remote/client.rs +++ b/crates/edr_eth/src/remote/client.rs @@ -236,7 +236,7 @@ impl RpcClient { request: SerializedRequest, ) -> Result { let is_missing_trie_node_error = - error.code == -32000 && error.message.contains("missing trie node"); + error.code == -32000 && error.message.to_lowercase().contains("missing trie node"); let result = if is_missing_trie_node_error { self.send_request_body(&request)