Skip to content

Commit f12cf72

Browse files
fix: data race circuit breakers (#171)
1 parent 7fe398d commit f12cf72

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

services/blockvalidation/catchup/circuit_breaker_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,61 @@ func TestCircuitBreaker_ConcurrentAccess(t *testing.T) {
259259
state := cb.GetState()
260260
assert.Contains(t, []CircuitBreakerState{StateClosed, StateOpen, StateHalfOpen}, state)
261261
}
262+
263+
func TestPeerCircuitBreakers_GetPeerState_DataRace(t *testing.T) {
264+
config := CircuitBreakerConfig{
265+
FailureThreshold: 5,
266+
SuccessThreshold: 2,
267+
Timeout: 10 * time.Millisecond,
268+
MaxHalfOpenRequests: 1,
269+
}
270+
pcb := NewPeerCircuitBreakers(config)
271+
peerID := "test-peer-001"
272+
273+
// Create the breaker
274+
breaker := pcb.GetBreaker(peerID)
275+
276+
// Start goroutines that modify breaker.state
277+
done := make(chan bool, 3)
278+
279+
// Goroutine 1: Record failures
280+
go func() {
281+
for i := 0; i < 1000; i++ {
282+
breaker.RecordFailure()
283+
}
284+
done <- true
285+
}()
286+
287+
// Goroutine 2: Record successes
288+
go func() {
289+
for i := 0; i < 1000; i++ {
290+
breaker.RecordSuccess()
291+
}
292+
done <- true
293+
}()
294+
295+
// Goroutine 3: Call CanCall (which also modifies state)
296+
go func() {
297+
for i := 0; i < 1000; i++ {
298+
breaker.CanCall()
299+
}
300+
done <- true
301+
}()
302+
303+
// Main goroutine: Continuously read state via GetPeerState
304+
// This reads breaker.state via thread-safe GetState() method
305+
readCount := 0
306+
for {
307+
select {
308+
case <-done:
309+
readCount++
310+
if readCount >= 3 {
311+
return // All writers done
312+
}
313+
default:
314+
// This should be safe now - accessing breaker.state
315+
// via GetState() which holds breaker.mu
316+
_ = pcb.GetPeerState(peerID)
317+
}
318+
}
319+
}

services/blockvalidation/catchup/peer_circuit_breakers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (pcb *PeerCircuitBreakers) GetPeerState(peerID string) CircuitBreakerState
6060
defer pcb.mu.RUnlock()
6161

6262
if breaker, exists := pcb.breakers[peerID]; exists {
63-
return breaker.state
63+
return breaker.GetState()
6464
}
6565

6666
// Return StateClosed if breaker doesn't exist yet

0 commit comments

Comments
 (0)