feat: Node.js CircuitBreaker with exponential OPEN cooldown — GH#4775 - #4793
feat: Node.js CircuitBreaker with exponential OPEN cooldown — GH#4775#4793balhar-jakub wants to merge 17 commits into
Conversation
Create standalone CircuitBreaker.js with CLOSED/OPEN/HALF_OPEN states, exponential backoff capped at 300s, and EventEmitter-based state events. Comprehensive unit tests (40 cases) cover all state transitions, cooldown timing, backoff computation, edge cases, and lifecycle. Part of #4775 (Area A) Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
…ry fetch scheduling - Add circuitBreaker config block to defaultConfig.js (enabled, maxFailures=5, cooldownTime=60000, backoffMax=300000) - Import CircuitBreaker and instantiate in EurekaClient constructor with event-driven logging (WARN for OPEN, INFO for CLOSE/HALF_OPEN) - Rewrite startHeartbeats() and startRegistryFetches() to use setTimeout-based scheduling with circuit breaker gating via allowRequest() - On success: call recordSuccess(); on failure: call recordFailure() with backoff-driven rescheduling - Add optional callback parameter to renew() for success/failure notification - Rewrite stop() to cancel pending setTimeout handles - Fall back to legacy setInterval when circuitBreaker.enabled=false - Add 11 integration tests (AC1-AC9 + disabled fallback + registry fetch gate) - Fix pre-existing lint issues in index.js and CircuitBreaker.test.js Refs: #4775 Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
- Rework CircuitBreaker for exponential OPEN cooldown per AC5+AC6 - Remove baseCooldown; use cooldownTime as sole base for all backoff - Track _openCycleCount for OPEN cooldown doubling (capped at backoffMax) - HALF_OPEN→CLOSED resets _openCycleCount - 49 unit tests covering construction, states, events, lifecycle, edge cases - EPL-2.0 license only (remove Apache/MIT from prior art) Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
- Remove Apache 2.0 and MIT license headers from prior art - Keep only EPL-2.0 license as required by project Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
- Update EurekaClient integration tests for cooldownTime-based exponential backoff - AC2-AC8 tests updated: timings now use cooldownTime * 2^(N-1) instead of baseCooldown - EPL-2.0 license only (remove Apache/MIT from prior art) - CircuitBreaker constructor already compatible (no baseCooldown passed) Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
QA + Security Review — PR #4793 (Closes #4775)Verdict: ✅ APPROVED (pending CI)Architecture ValidationImplementation matches the architect's solution design exactly:
Pavel's Lens — All 8 Rules
Codebase Grep Results
Build & Tests
Minor Observations (non-blocking)
|
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
CI Status Update — PR #4793BuildAndTest FAILURE: Pre-existing CI infrastructure issue (NOT caused by this PR)The BuildAndTest CI check fails at Root cause: The CI runner's Node.js version (v24.10.0) is older than the engine requirement in Impact: ANY PR against No changes to package.json or package-lock.json were made in this PR (verified: InfinispanJGroupStabilityTest: Cancelled (transient GitHub Actions issue)This job was cancelled before running any steps. Unrelated to Node.js changes. All other checks: ✅ PASS
|
|
balhar-jakub
left a comment
There was a problem hiding this comment.
Multi-Reviewer Review
Verdict: Approve with one HIGH finding (monotonic time).
Findings:
- 🚨 HIGH —
_cooldownExpired()usesDate.now()(non-monotonic). Useprocess.hrtime.bigint()orperformance.now()for monotonic time. - ℹ️ NIT — CircuitBreaker is always constructed even when disabled. Document or move to lazy init.
- ℹ️ NIT — Heartbeat drift: next heartbeat scheduled after callback fires (interval = heartbeatInterval + responseTime).
- ℹ️ NIT —
register()runs concurrently with circuit breaker success (404 path). - ℹ️ NIT —
getNextCooldown()returnscooldownTimeon first failure (operators may be surprised). Document. - ℹ️ NIT —
#state/#openCycleCountprivate fields (TC39 stage 3+) for future-proofing. - ℹ️ NIT —
error | ''is a pre-existing bitwise-OR bug (line 403). Fix while in the file. - ✅ Feature-flagged default + test coverage is exceptional.
Good:
- Comprehensive state machine with exponential backoff and
backoffMaxcap. - EventEmitter pattern for observability.
sinon.useFakeTimers()for deterministic tests.- Integration tests cover the lifecycle end-to-end.
callback(null)on 404 addresses achmelo's prior comment.
Required before merge (HIGH):
Fix _cooldownExpired() to use monotonic time. The current implementation is correct under normal conditions but breaks on clock changes.
Optional follow-up:
- Add inline comments to
defaultConfig.js(suggested in inline comment). - Document the heartbeat drift behavior in the operator docs.
- Consider
#stateprivate fields for encapsulation.
Required before merge: 1 HIGH (monotonic time). The rest are NIT-level polish.
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
Return full licenses as required for Enabler Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
5dabca3 to
aa0921c
Compare
|
The circuit breaker design is sound, yet the integration with the eureka client is flawed and the client does not benefit from the circuit breaker. Run the enabler (with no apiml): The linear backoff comes from the error handling in the eureka client: |
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
Signed-off-by: Jakub Balhar <jakub@balhar.net>
|
|
Similarly as for registrations, Eureka client heartbeat failures does not trigger the circuit breaker and go with linear backoff with 1 second base. This also means there is a gap in tests.
|
|
The registry fetch requests suffer from same condition like registration and heartbeats - the circuit breaker is not applied indicating a flawed integration of the circuit breaker and the eureka client and major miss in tests. |



Closes #4775
Adds a CircuitBreaker state machine to the Node.js onboarding enabler EurekaClient with exponential OPEN cooldown.
Changes