feat(backend): replace fixed getTransaction polling with exponential backoff#189
Conversation
…backoff Closes Stellar-Ecosystem#26 simulateAndSubmit polled getTransaction 20 times with a fixed 1.5s delay (30s total). Under network congestion this could time out on transactions that succeed after ~35s, while also hammering the RPC every 1.5s regardless of load. Changes: - contract.js: while-loop with deadline instead of for-loop with fixed count. Delay starts at initialDelayMs and doubles each round up to maxDelayMs. Remaining time is respected: the last sleep is capped to not overshoot the deadline. Defaults: 1.5s initial, 10s max, 60s total — same first poll as before, but far fewer calls under congestion. - config.js: txPoll.initialDelayMs / maxDelayMs / maxWaitMs (parsePositiveInt with sane defaults; warns on NaN so a typo cannot silently disable limits) - .env.example: documented TX_POLL_INITIAL_DELAY_MS / MAX_DELAY_MS / MAX_WAIT_MS
📝 WalkthroughWalkthroughThe fixed 20-iteration, 1.5s-delay polling loop in ChangesExponential Backoff for getTransaction Polling
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/lib/contract.js (1)
64-87:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftAdd automated tests for the new backoff/deadline behavior before merge.
This path changed significantly, but no accompanying test updates are included for deadline stop, exponential growth, max-delay cap, and timeout emission behavior.
As per coding guidelines, the stated acceptance criteria require “Automated coverage is added or updated for the touched backend path.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/lib/contract.js` around lines 64 - 87, Add automated tests for the polling logic in the transaction handling code. Create tests that verify the deadline stop behavior (the while loop exits when Date.now() >= deadline), exponential backoff growth (delay doubles each iteration up to the max), the max-delay cap enforcement (delay is capped at pollMaxMs), and timeout behavior when the polling deadline is exceeded. The tests should mock server.getTransaction to simulate various response scenarios and verify that the remaining time calculation accounts for elapsed time correctly. These tests are required per the coding guidelines to ensure the touched polling path with deadline and exponential backoff logic is properly covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/src/lib/contract.js`:
- Around line 64-70: The delay variable is initialized to pollInitialMs without
checking if it exceeds the configured maximum. When initialDelayMs is greater
than maxDelayMs, the first polling wait will violate the configured maximum
delay cap. Clamp the delay variable initialization by taking the minimum of
pollInitialMs and pollMaxMs to ensure the first wait respects the configured
maximum delay threshold.
---
Outside diff comments:
In `@backend/src/lib/contract.js`:
- Around line 64-87: Add automated tests for the polling logic in the
transaction handling code. Create tests that verify the deadline stop behavior
(the while loop exits when Date.now() >= deadline), exponential backoff growth
(delay doubles each iteration up to the max), the max-delay cap enforcement
(delay is capped at pollMaxMs), and timeout behavior when the polling deadline
is exceeded. The tests should mock server.getTransaction to simulate various
response scenarios and verify that the remaining time calculation accounts for
elapsed time correctly. These tests are required per the coding guidelines to
ensure the touched polling path with deadline and exponential backoff logic is
properly covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d58f7d9b-705c-4cde-b78a-dd1e442e0284
📒 Files selected for processing (3)
backend/.env.examplebackend/src/config.jsbackend/src/lib/contract.js
| const pollInitialMs = config.txPoll?.initialDelayMs ?? 1_500; | ||
| const pollMaxMs = config.txPoll?.maxDelayMs ?? 10_000; | ||
| const pollTimeoutMs = config.txPoll?.maxWaitMs ?? 60_000; | ||
|
|
||
| let getResult; | ||
| for (let i = 0; i < 20; i++) { | ||
| let delay = pollInitialMs; | ||
| const deadline = Date.now() + pollTimeoutMs; |
There was a problem hiding this comment.
Clamp the initial delay to the configured max before first sleep.
Line 69 initializes delay to pollInitialMs, so when initialDelayMs > maxDelayMs, the first wait exceeds the configured max cap.
Suggested fix
- let delay = pollInitialMs;
+ let delay = Math.min(pollInitialMs, pollMaxMs);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/lib/contract.js` around lines 64 - 70, The delay variable is
initialized to pollInitialMs without checking if it exceeds the configured
maximum. When initialDelayMs is greater than maxDelayMs, the first polling wait
will violate the configured maximum delay cap. Clamp the delay variable
initialization by taking the minimum of pollInitialMs and pollMaxMs to ensure
the first wait respects the configured maximum delay threshold.
Fixes #26
Problem
simulateAndSubmitpolledgetTransactionwith a hardcodedfor (let i = 0; i < 20; i++)loop and a fixed 1500ms delay — 30s total, no configurability. Under Soroban network congestion transactions can take longer to confirm, causing prematureTRANSACTION_TIMEOUTon valid transactions. Additionally, hitting the RPC every 1.5s at a fixed rate wastes capacity when most waits happen in the first few seconds.Solution
Replaced the fixed loop with a deadline-based exponential backoff:
Default behavior: 1.5s → 3s → 6s → 10s → 10s … (capped) for up to 60s total. Same first-poll latency as before; 7× fewer RPC calls under congestion vs. 1.5s fixed.
Configurable via env vars (documented in
.env.example):TX_POLL_INITIAL_DELAY_MS(default 1500)TX_POLL_MAX_DELAY_MS(default 10000)TX_POLL_MAX_WAIT_MS(default 60000)🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes