Skip to content

feat: implement circuit breaker for Google Maps and Stellar RPC calls (#144) - #156

Merged
Tybravo merged 2 commits into
SwiftChainn:mainfrom
mmotunrayo:feat/circuit-breaker-apis
Aug 28, 2026
Merged

feat: implement circuit breaker for Google Maps and Stellar RPC calls (#144)#156
Tybravo merged 2 commits into
SwiftChainn:mainfrom
mmotunrayo:feat/circuit-breaker-apis

Conversation

@mmotunrayo

Copy link
Copy Markdown
Contributor

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. 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:

    Variable Default
    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 (healthy)
  • 206 — one or more breakers OPEN or HALF-OPEN (degraded)

…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
@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@Tybravo
Tybravo merged commit 77016d5 into SwiftChainn:main Aug 28, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Implement the Circuit Breaker pattern for Google Maps API and Stellar RPC calls

2 participants