mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-24 06:48:09 +00:00
fix(retry): guard RetryBudget mutable config against data race
UpdateConfig writes tokensPerSecond/maxTokens/enabled as plain fields while the refill goroutine and AllowRetry readers touch them concurrently. Guard all reads/writes of the mutable config with an RWMutex. Adds TestRetryBudget_ConcurrentUpdateConfig, which races UpdateConfig against AllowRetry/GetStats and is verified clean under -race. Also drop the package-level min() that shadowed the Go 1.25 builtin.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -310,3 +311,52 @@ func TestRetryBudget_GlobalInstance(t *testing.T) {
|
||||
rb2 := GetRetryBudget()
|
||||
assert.Equal(t, rb, rb2)
|
||||
}
|
||||
|
||||
// TestRetryBudget_ConcurrentUpdateConfig exercises UpdateConfig racing with
|
||||
// AllowRetry/refill readers. Run with -race to detect the data race on the
|
||||
// mutable config fields.
|
||||
func TestRetryBudget_ConcurrentUpdateConfig(t *testing.T) {
|
||||
rb := NewRetryBudget(RetryBudgetConfig{TokensPerSecond: 100.0, MaxTokens: 1000, Enabled: true}, nil)
|
||||
rb.cancel() // stop the refill goroutine so it doesn't advance tokens
|
||||
rb.currentTokens.Store(1000)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
stop := make(chan struct{})
|
||||
|
||||
// Concurrent writers calling UpdateConfig
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go func(n int) {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
rb.UpdateConfig(RetryBudgetConfig{TokensPerSecond: float64(n), MaxTokens: n, Enabled: true})
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Concurrent readers calling AllowRetry and GetStats
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
rb.AllowRetry()
|
||||
rb.GetStats()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
close(stop)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user