connor4312/cockatiel
CountBreaker state exposes samples array by reference, causing shared state and negative counters
Summary
Context:
CountBreakeris a circuit breaker policy that tracks failures in a sliding window using an internal array of boolean samples, with success/failure counters that must stay synchronized with the array contents.Bug: The
stategetter returns the internalsamplesarray by reference instead of a copy, and thestatesetter usesObject.assignwhich copies the reference, causing multiple breaker instances to share the same underlying array.Actual vs. expected: When state is copied between two
CountBreakerinstances without JSON serialization, they share the samesamplesarray, causing modifications to one breaker to corrupt the other’s internal state (thesamplesarray changes but counters don’t update). Expected behavior is that each breaker instance has independent state.Impact: When one breaker modifies the shared array, the other breaker’s
samplesarray is modified without updating itssuccesses/failurescounters, leading to negative counter values and incorrect circuit-breaking decisions.
Code with bug
Failing test
Test output (key observations):
breaker1Samples === breaker2Samples-> true (same array reference)After
breaker1.failure(...),breaker1SuccessesAfter-> -1 (assertion passes)
Recommended fix
The state getter should return a copy of the samples array instead of a reference:
Alternatively, the state setter could defensively clone the incoming array:
The getter fix is preferred because:
It prevents the issue at the source
It maintains the invariant that the returned state is independent
It’s consistent with principle of encapsulation
It has minimal performance impact (shallow copy of a fixed-size array)