feat: add retry with exponential backoff to Soroban RPC client - #309
Open
rilwanubala wants to merge 1 commit into
Open
Conversation
Add retry with exponential backoff to all Soroban RPC calls in backend/src/soroban.rs. Changes: - Add is_transient() to classify errors: retries on timeouts, connection failures and HTTP 5xx; never retries on 4xx or contract-level errors (NOT_FOUND, UNAUTHORIZED, NOT_PAID) - Add with_retry() generic async helper with exponential backoff: up to 3 retries, initial delay 200ms doubling each attempt, capped at 30s total wait - Add rpc_post() internal helper that wraps every RPC HTTP call with the retry wrapper and treats HTTP 5xx as transient - Wrap check_horizon_health() with retry for 5xx responses - Add 10 tests in soroban::tests covering: * is_transient() classification (unit tests, no network) * retries_on_5xx_and_eventually_succeeds (mock server, 2 failures + 1 success) * gives_up_after_max_retries_on_persistent_5xx (all attempts fail) * does_not_retry_on_4xx (only 1 attempt made) * get_invoice_retries_on_5xx_and_succeeds * not_found_error_is_not_retried Bug fixes (pre-existing compile/test failures): - Fix health.rs mock: incompatible if/else return types + missing into_make_service() in axum::serve calls - Fix cancel.rs, refund.rs: SorobanClient::new called with 2 args instead of 3 (missing horizon_url) - Fix pay/cancel/refund tests: accept 415 OR 422 for missing-body requests (axum-test v14 sends no Content-Type → server returns 415) - Add cancel module to routes/mod.rs - Fix main.rs imports (cancel_invoice, refund_invoice were unresolved) - Rename routes.rs → routes_auth_legacy.rs to remove ambiguity with routes/mod.rs (Rust compiler error E0761) All 20 tests pass. Closes WHEELBACK#199
|
@rilwanubala Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #199
Adds retry with exponential backoff to all Soroban RPC calls in
backend/src/soroban.rs. Transient failures (timeouts, connection resets, HTTP 5xx) are automatically retried up to 3 times with doubling delays capped at 30 seconds total. Non-transient errors (4xx, contract-level NOT_FOUND / UNAUTHORIZED / NOT_PAID) are surfaced immediately without retrying.What changed
Core implementation (
backend/src/soroban.rs)is_transient(err)— classifies errors as retryable vs. permanentwith_retry(op)— generic async retry helper (max 3 retries, 200 ms → 400 ms → 800 ms backoff, 30 s total cap)rpc_post(req)— internal HTTP helper wrapping every RPC call with retry; treats HTTP 5xx as transientcheck_horizon_health()— also wrapped with retry for 5xx responsesBug fixes (pre-existing compile/test failures)
health.rs: Fix incompatible if/else return types in mock server + missinginto_make_service()inaxum::servecancel.rs,refund.rs:SorobanClient::newcalled with 2 args instead of 3pay/cancel/refundtests: Accept 415 or 422 for missing-body requests (axum-test v14 sends no Content-Type)routes/mod.rs: Add missingcancelmodule exportmain.rs: Add missingcancel_invoice/refund_invoiceimportsroutes.rs→routes_auth_legacy.rsto remove Rust E0761 ambiguity withroutes/mod.rsTests
All 20 tests pass (
cargo test):New tests added (
soroban::tests)transient_false_for_not_foundtransient_false_for_unauthorizedtransient_false_for_not_paidtransient_false_for_empty_rpc_resulttransient_true_for_5xx_messagetransient_true_for_timeout_messagetransient_true_for_connection_messageretries_on_5xx_and_eventually_succeedsgives_up_after_max_retries_on_persistent_5xxdoes_not_retry_on_4xxget_invoice_retries_on_5xx_and_succeedsget_invoiceretries correctlynot_found_error_is_not_retriedRetry behaviour