feat: implement circuit breaker for Google Maps and Stellar RPC calls (#144) - #156
Merged
Merged
Conversation
…SwiftChainn#144) Closes SwiftChainn#144 ## What changed ### New files - src/utils/circuitBreaker.ts Generic createCircuitBreaker<TArgs, TResult> factory built on opossum. Accepts typed CircuitBreakerOptions (errorThresholdPercentage, rollingWindowMs, resetTimeoutMs, volumeThreshold, timeoutMs) and an optional fallback function. Registers every breaker in a module-level registry. Full event hooks: open / halfOpen / close / fallback / timeout / reject / success / failure — all routed through the Winston logger. Exports getAllCircuitBreakerStatuses() for the health endpoint and fireWithBreaker() for action-agnostic fire calls. - src/controllers/circuitBreakerController.ts GET /api/v1/health/circuit-breakers handler. Returns 200 when all breakers are CLOSED, 206 when any is OPEN or HALF-OPEN, with a per- breaker state + rolling stats payload and a summary object. - src/routes/healthRoutes.ts Router for /api/v1/health — mounts the circuit-breaker status endpoint. ### Modified files - src/services/routingService.ts Google Maps Directions API call wrapped in a dedicated 'google-maps' circuit breaker. Fallback: Haversine estimate returned immediately when the circuit is OPEN. ETAResponse now includes isFallback: boolean so callers can distinguish live vs degraded results. axios timeout aligned with CB_GOOGLE_MAPS_TIMEOUT_MS. - src/blockchain/soroban.service.ts All RPC calls (getHealth, getLatestLedger, getNetwork) go through callWithRetryAndBreaker() which stacks the existing exponential-backoff retry inside a 'soroban-rpc' circuit breaker. Fallback returns a typed DegradedLedgerResult sentinel. checkConnectivity() and getLatestLedger() handle the sentinel and never throw on open circuit. - src/services/transactionService.ts Dedicated 'soroban-rpc-tx' circuit breaker for transaction-building RPC calls (getAccount + prepareTransaction). When the circuit is OPEN both methods throw AppError(503) immediately instead of waiting for a TCP timeout, preventing request pile-up during node outages. - src/config/env.ts 10 new circuit-breaker env vars added to both EnvConfig interface and envSchema with production-safe defaults: CB_GOOGLE_MAPS_ERROR_THRESHOLD_PERCENTAGE (50) CB_GOOGLE_MAPS_ROLLING_WINDOW_MS (30000) CB_GOOGLE_MAPS_RESET_TIMEOUT_MS (60000) CB_GOOGLE_MAPS_VOLUME_THRESHOLD (5) CB_GOOGLE_MAPS_TIMEOUT_MS (10000) CB_SOROBAN_ERROR_THRESHOLD_PERCENTAGE (50) CB_SOROBAN_ROLLING_WINDOW_MS (30000) CB_SOROBAN_RESET_TIMEOUT_MS (60000) CB_SOROBAN_VOLUME_THRESHOLD (3) CB_SOROBAN_TIMEOUT_MS (15000) - .env.example — all 10 vars documented with explanations. - package.json — [email protected] (runtime), @types/[email protected] (dev). - src/routes/index.ts — /api/v1/health mounted. ## Circuit-breaker behaviour | State | Google Maps | Soroban RPC | |-----------|------------------------------|-------------------------------------| | CLOSED | Live Directions API call | Live RPC call (with retry) | | OPEN | Haversine fallback (instant) | DegradedLedgerResult / 503 AppError | | HALF-OPEN | Single probe call | Single probe call | ## Health endpoint GET /api/v1/health/circuit-breakers 200 — all breakers CLOSED 206 — one or more breakers OPEN or HALF-OPEN
|
@mmotunrayo 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.
Closes #144
What changed
New files
src/utils/cuitBreaker.ts
Generic
createCircuitBreaker<TArgs, TResult>factory built on opossum.Accepts typed
CircuitBreakerOptions(errorThresholdPercentage,rollingWindowMs,resetTimeoutMs,volumeThreshold,timeoutMs) and an optional fallback function. Registers every breaker in a module-level registry. Full event hooks:open/halfOpen/close/fallback/timeout/reject/success/failure— all routed through the Winston logger. ExportsgetAllCircuitBreakerStatuses()for the health endpoint andfireWithBreaker()for action-agnostic fire calls.src/controllers/circuitBreakerController.ts
GET /api/v1/health/circuit-breakershandler. Returns200when all breakers areCLOSED,206when any isOPENorHALF-OPEN, with a per-breaker state + rolling stats payload and a summary object.src/routes/healthRoutes.ts
Router for
/api/v1/health— mounts the circuit-breaker status endpoint.Modified files
src/services/routingService.ts
Google Maps Directions API call wrapped in a dedicated
google-mapscircuit breaker. Fallback: Haversine estimate returned immediately when the circuit isOPEN.ETAResponsenow includesisFallback: booleanso callers can distinguish live vs degraded results.axiostimeout aligned withCB_GOOGLE_MAPS_TIMEOUT_MS.src/blockchain/soroban.service.ts
All RPC calls (
getHealth,getLatestLedger,getNetwork) go throughcallWithRetryAndBreaker()which stacks the existing exponential-backoff retry inside asoroban-rpccircuit breaker. Fallback returns a typedDegradedLedgerResultsentinel.checkConnectivity()andgetLatestLedger()handle the sentinel and never throw on open circuit.src/services/transactionService.ts
Dedicated
soroban-rpc-txcircuit breaker for transaction-building RPC calls (getAccount+prepareTransaction). When the circuit isOPENboth methods throwAppError(503)immediately instead of waiting for a TCP timeout, preventing request pile-up during node outages.src/config/env.ts
10 new circuit-breaker env vars added to both
EnvConfiginterface andenvSchemawith production-safe defaults:CB_GOOGLE_MAPS_ERROR_THRESHOLD_PERCENTAGE50CB_GOOGLE_MAPS_ROLLING_WINDOW_MS30000CB_GOOGLE_MAPS_RESET_TIMEOUT_MS60000CB_GOOGLE_MAPS_VOLUME_THRESHOLD5CB_GOOGLE_MAPS_TIMEOUT_MS10000CB_SOROBAN_ERROR_THRESHOLD_PERCENTAGE50CB_SOROBAN_ROLLING_WINDOW_MS30000CB_SOROBAN_RESET_TIMEOUT_MS60000CB_SOROBAN_VOLUME_THRESHOLD3CB_SOROBAN_TIMEOUT_MS15000.env.example — all 10 vars documented with explanations.
package.json —
[email protected](runtime),@types/[email protected](dev).src/routes/index.ts —
/api/v1/healthmounted.Circuit-breaker behaviour
CLOSEDOPENDegradedLedgerResult/503 AppErrorHALF-OPENHealth endpoint
GET /api/v1/health/circuit-breakers200— all breakersCLOSED(healthy)206— one or more breakersOPENorHALF-OPEN(degraded)