Severity: Medium
Type: Bug
Scope: Stellar
Labels: bug, refactoring
Description
fetchTransactionWithRetry and fetchTransactionOperationsWithRetry (src/stellar/stellar-transactions.service.ts, lines ~95–170) use the global fetch API without an AbortController, signal, or explicit timeout. Horizon is a public best-effort service; a stalled TCP socket can keep the promise unresolved indefinitely. The withRetries(3, ...) wrapper only retries on rejection — there is no wall-clock cap.
A Horizon outage will exhaust Node's HTTP agent, eventually timing out adjacent gRPC Soroban polling and the stellar-event.service.ts SSE stream. The donation-create endpoint can therefore hang, and the connection pool fill up.
Recommendation
- Wrap every Horizon
fetch in AbortSignal.timeout(ms) (e.g. 8s) and a per-request AbortController you can pass into the helper.
- Bound the total retry budget with a wall-clock cap (e.g. 25s).
- Surface a
ServiceUnavailableException immediately on timeout instead of retrying on the same socket.
Severity: Medium
Type: Bug
Scope: Stellar
Labels:
bug,refactoringDescription
fetchTransactionWithRetryandfetchTransactionOperationsWithRetry(src/stellar/stellar-transactions.service.ts, lines ~95–170) use the globalfetchAPI without anAbortController,signal, or explicittimeout. Horizon is a public best-effort service; a stalled TCP socket can keep the promise unresolved indefinitely. ThewithRetries(3, ...)wrapper only retries on rejection — there is no wall-clock cap.A Horizon outage will exhaust Node's HTTP agent, eventually timing out adjacent gRPC Soroban polling and the
stellar-event.service.tsSSE stream. The donation-create endpoint can therefore hang, and the connection pool fill up.Recommendation
fetchinAbortSignal.timeout(ms)(e.g. 8s) and a per-requestAbortControlleryou can pass into the helper.ServiceUnavailableExceptionimmediately on timeout instead of retrying on the same socket.