Description
In src/listeners/horizonBondEvents.ts, subscribeBondCreationEvents calls startStream() twice at the end of the function, opening two concurrent Horizon streams that both process the same operations — causing duplicate upsertIdentity/upsertBond writes and doubled onEvent callbacks. Additionally the onerror handler reconnects with a fixed 5000ms setTimeout and no jitter, no max attempts, and no circuit breaker, which can hammer Horizon during an outage. We need a single stream with a proper bounded exponential-backoff-with-jitter reconnect policy.
Requirements and context
- Remove the duplicate
startStream() invocation in src/listeners/horizonBondEvents.ts.
- Introduce reusable backoff in
src/utils/ (reuse src/utils/retryClassifier.ts where applicable) with jitter, max delay, and reset-on-success.
- Apply the same fix to
src/listeners/horizonWithdrawalEvents.ts and src/listeners/attestationEvents.ts.
- Expose
horizon_reconnect_total and horizon_stream_up gauges via src/observability/.
Suggested execution
Create branch feature/horizon-single-stream-backoff.
- Modify
src/listeners/horizonBondEvents.ts, horizonWithdrawalEvents.ts, attestationEvents.ts.
- Add
src/utils/backoff.ts and src/utils/backoff.test.ts.
- Extend
src/__tests__/horizonBondEvents.test.ts to assert exactly one active stream.
- Update
docs/horizon-listener.md and docs/observability.md.
Test and commit
Run npm run test. Edge cases: rapid error storms, success resetting backoff, stop() during pending reconnect timer. Security: ensure reconnect loop cannot leak unbounded timers.
Example commit message
feat: single Horizon subscription with bounded backoff reconnect
Guidelines
- Minimum 95% test coverage
- Clear documentation in
docs/horizon-listener.md
- Timeframe: 96 hours
Description
In
src/listeners/horizonBondEvents.ts,subscribeBondCreationEventscallsstartStream()twice at the end of the function, opening two concurrent Horizon streams that both process the same operations — causing duplicateupsertIdentity/upsertBondwrites and doubledonEventcallbacks. Additionally theonerrorhandler reconnects with a fixed 5000mssetTimeoutand no jitter, no max attempts, and no circuit breaker, which can hammer Horizon during an outage. We need a single stream with a proper bounded exponential-backoff-with-jitter reconnect policy.Requirements and context
startStream()invocation insrc/listeners/horizonBondEvents.ts.src/utils/(reusesrc/utils/retryClassifier.tswhere applicable) with jitter, max delay, and reset-on-success.src/listeners/horizonWithdrawalEvents.tsandsrc/listeners/attestationEvents.ts.horizon_reconnect_totalandhorizon_stream_upgauges viasrc/observability/.Suggested execution
Create branch
feature/horizon-single-stream-backoff.src/listeners/horizonBondEvents.ts,horizonWithdrawalEvents.ts,attestationEvents.ts.src/utils/backoff.tsandsrc/utils/backoff.test.ts.src/__tests__/horizonBondEvents.test.tsto assert exactly one active stream.docs/horizon-listener.mdanddocs/observability.md.Test and commit
Run
npm run test. Edge cases: rapid error storms, success resetting backoff, stop() during pending reconnect timer. Security: ensure reconnect loop cannot leak unbounded timers.Example commit message
feat: single Horizon subscription with bounded backoff reconnectGuidelines
docs/horizon-listener.md