From 7ee5d7c04352f43b1ccf9009d891296b27bd876e Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:28:25 +0100 Subject: [PATCH 1/6] Create http_rpc.rs --- core/execution/rpc/http_rpc.rs | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 core/execution/rpc/http_rpc.rs diff --git a/core/execution/rpc/http_rpc.rs b/core/execution/rpc/http_rpc.rs new file mode 100644 index 00000000..249c628d --- /dev/null +++ b/core/execution/rpc/http_rpc.rs @@ -0,0 +1,74 @@ +use std::time::Duration; +use wasmtimer::tokio::sleep; + +// Adding a structure for retry configuration +#[derive(Clone)] +struct RetryConfig { + max_attempts: u32, + initial_backoff: Duration, + max_backoff: Duration, +} + +impl Default for RetryConfig { + fn default() -> Self { + Self { + max_attempts: 3, + initial_backoff: Duration::from_millis(100), + max_backoff: Duration::from_secs(5), + } + } +} + +impl HttpProvider { + // Adding a new method to perform a request with retries + async fn execute_with_retry(&self, request: Request) -> Result, Error> + where + T: serde::Serialize + Send + Sync, + { + let config = RetryConfig::default(); + let mut attempts = 0; + let mut backoff = config.initial_backoff; + + loop { + attempts += 1; + match self.execute(request.clone()).await { + Ok(response) => return Ok(response), + Err(err) => { + // Check if a retry should be attempted + if !should_retry(&err) || attempts >= config.max_attempts { + return Err(err); + } + + // Wait before the next attempt + sleep(backoff).await; + + // Increase the backoff time exponentially + backoff = std::cmp::min( + backoff * 2, + config.max_backoff + ); + } + } + } + } +} + +// Helper function to determine if a retry should be attempted +fn should_retry(error: &Error) -> bool { + matches!( + error, + Error::RateLimitExceeded(_) | + Error::ConnectionError(_) | + Error::TimeoutError + ) +} + +// Updating the existing execute method to use the retry mechanism +impl Provider for HttpProvider { + async fn execute(&self, request: Request) -> Result, Error> + where + T: serde::Serialize + Send + Sync, + { + self.execute_with_retry(request).await + } +} From 990f33409bdacba24a11566cb4c737916081d609 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:29:31 +0100 Subject: [PATCH 2/6] Create http_rpc_tests.rs --- core/execution/rpc/tests/http_rpc_tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 core/execution/rpc/tests/http_rpc_tests.rs diff --git a/core/execution/rpc/tests/http_rpc_tests.rs b/core/execution/rpc/tests/http_rpc_tests.rs new file mode 100644 index 00000000..0c612fec --- /dev/null +++ b/core/execution/rpc/tests/http_rpc_tests.rs @@ -0,0 +1,8 @@ +#[tokio::test] +async fn test_retry_mechanism() { + // Тест успешного повтора после временной ошибки + + // Тест максимального количества попыток + + // Тест для ошибок, которые не должны повторяться +} From c713b85384b797ea8f6ea57815970cdfd2bbdb99 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:30:06 +0100 Subject: [PATCH 3/6] Update http_rpc_tests.rs was sleepy and accidentally wrote comments not on english --- core/execution/rpc/tests/http_rpc_tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/execution/rpc/tests/http_rpc_tests.rs b/core/execution/rpc/tests/http_rpc_tests.rs index 0c612fec..ee0e9477 100644 --- a/core/execution/rpc/tests/http_rpc_tests.rs +++ b/core/execution/rpc/tests/http_rpc_tests.rs @@ -1,8 +1,8 @@ #[tokio::test] async fn test_retry_mechanism() { - // Тест успешного повтора после временной ошибки + // Test successful retry after a temporary error - // Тест максимального количества попыток + // Test the maximum number of attempts - // Тест для ошибок, которые не должны повторяться -} + // Test for errors that should not be retried +} From f6dc528163a27d618cd2470397d607dfa5eb0fca Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:31:48 +0100 Subject: [PATCH 4/6] Update http_rpc.rs --- core/execution/rpc/http_rpc.rs | 68 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/core/execution/rpc/http_rpc.rs b/core/execution/rpc/http_rpc.rs index 249c628d..22eec318 100644 --- a/core/execution/rpc/http_rpc.rs +++ b/core/execution/rpc/http_rpc.rs @@ -1,51 +1,52 @@ use std::time::Duration; use wasmtimer::tokio::sleep; -// Adding a structure for retry configuration -#[derive(Clone)] -struct RetryConfig { - max_attempts: u32, - initial_backoff: Duration, - max_backoff: Duration, +/// Retry mechanism configuration +#[derive(Clone, Debug)] +pub struct RetryConfig { + /// Maximum number of attempts to execute the request + pub max_attempts: u32, + /// Initial delay between retry attempts + pub initial_backoff: Duration, + /// Maximum delay between retry attempts + pub max_backoff: Duration, } -impl Default for RetryConfig { - fn default() -> Self { - Self { - max_attempts: 3, - initial_backoff: Duration::from_millis(100), - max_backoff: Duration::from_secs(5), - } +impl HttpProvider { + // Add the ability to configure retry settings + pub fn with_retry_config(mut self, config: RetryConfig) -> Self { + self.retry_config = config; + self } -} -impl HttpProvider { - // Adding a new method to perform a request with retries async fn execute_with_retry(&self, request: Request) -> Result, Error> where T: serde::Serialize + Send + Sync, { - let config = RetryConfig::default(); let mut attempts = 0; - let mut backoff = config.initial_backoff; + let mut backoff = self.retry_config.initial_backoff; loop { attempts += 1; - match self.execute(request.clone()).await { + match self.execute_internal(request.clone()).await { Ok(response) => return Ok(response), Err(err) => { - // Check if a retry should be attempted - if !should_retry(&err) || attempts >= config.max_attempts { - return Err(err); + if !should_retry(&err) || attempts >= self.retry_config.max_attempts { + return Err(err.into()); } - // Wait before the next attempt + tracing::debug!( + "Request failed with error: {:?}. Retrying ({}/{})", + err, + attempts, + self.retry_config.max_attempts + ); + sleep(backoff).await; - // Increase the backoff time exponentially backoff = std::cmp::min( backoff * 2, - config.max_backoff + self.retry_config.max_backoff ); } } @@ -53,17 +54,18 @@ impl HttpProvider { } } -// Helper function to determine if a retry should be attempted +// Extend the list of errors that can trigger a retry fn should_retry(error: &Error) -> bool { - matches!( - error, - Error::RateLimitExceeded(_) | - Error::ConnectionError(_) | - Error::TimeoutError - ) + match error { + Error::RateLimitExceeded(_) => true, + Error::ConnectionError(_) => true, + Error::TimeoutError => true, + Error::ServerError(status) => status.as_u16() >= 500, + _ => false, + } } -// Updating the existing execute method to use the retry mechanism +// Update the existing execute method to use the retry mechanism impl Provider for HttpProvider { async fn execute(&self, request: Request) -> Result, Error> where From 299fff7a12218bbc2c75236af4b7c4ac97b97bb7 Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:32:31 +0100 Subject: [PATCH 5/6] Update http_rpc_tests.rs --- core/execution/rpc/tests/http_rpc_tests.rs | 35 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/core/execution/rpc/tests/http_rpc_tests.rs b/core/execution/rpc/tests/http_rpc_tests.rs index ee0e9477..0020f20c 100644 --- a/core/execution/rpc/tests/http_rpc_tests.rs +++ b/core/execution/rpc/tests/http_rpc_tests.rs @@ -1,8 +1,39 @@ +use mockito::{mock, Server}; +use std::time::Duration; + #[tokio::test] async fn test_retry_mechanism() { + let mut server = Server::new(); + // Test successful retry after a temporary error + let mock = server.mock("POST", "/") + .with_status(429) // Rate limit error + .create(); + + let provider = HttpProvider::new(server.url().as_str()); + let request = Request::new("eth_blockNumber", ()); + + let result = provider.execute(request).await; + assert!(result.is_err()); + mock.assert_hits(1); + + // Test the maximum number of retry attempts + let mock = server.mock("POST", "/") + .with_status(429) + .expect(3) // Expect exactly 3 attempts + .create(); + + let result = provider.execute(request).await; + assert!(result.is_err()); + mock.assert(); - // Test the maximum number of attempts + // Test for errors that should not trigger retries + let mock = server.mock("POST", "/") + .with_status(400) // Bad Request + .expect(1) // Expect only 1 attempt + .create(); - // Test for errors that should not be retried + let result = provider.execute(request).await; + assert!(result.is_err()); + mock.assert(); } From 18ec67b246d9620b784920c23d9435fe9f7ef96e Mon Sep 17 00:00:00 2001 From: Ragnar Date: Fri, 27 Dec 2024 02:32:55 +0100 Subject: [PATCH 6/6] Update Cargo.toml --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 55113ae6..da62104a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,8 @@ helios-opstack = { path = "./opstack" } tokio = { version = "1", features = ["full"] } dotenv = "0.15.0" serde = { version = "1.0.154", features = ["derive"] } +mockito = "0.31" +tracing = "0.1" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] alloy = { version = "0.2.1", features = ["full"] }