Fix concurrent access to balancer status map in WRR and P2C strategies

This commit is contained in:
Kevin Pollet 2025-07-10 16:08:04 +02:00 committed by GitHub
parent 9a46d35169
commit ba595bfa98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16 additions and 6 deletions

View File

@ -43,6 +43,7 @@ type rnd interface {
type Balancer struct {
wantsHealthCheck bool
// handlersMu is a mutex to protect the handlers slice, the status and the fenced maps.
handlersMu sync.RWMutex
handlers []*namedHandler
// status is a record of which child services of the Balancer are healthy, keyed
@ -50,11 +51,12 @@ type Balancer struct {
// created via Add, and it is later removed or added to the map as needed,
// through the SetStatus method.
status map[string]struct{}
// fenced is the list of terminating yet still serving child services.
fenced map[string]struct{}
// updaters is the list of hooks that are run (to update the Balancer
// parent(s)), whenever the Balancer status changes.
updaters []func(bool)
// fenced is the list of terminating yet still serving child services.
fenced map[string]struct{}
sticky *loadbalancer.Sticky
@ -181,7 +183,10 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if err != nil {
log.Error().Err(err).Msg("Error while getting sticky handler")
} else if h != nil {
if _, ok := b.status[h.Name]; ok {
b.handlersMu.RLock()
_, ok := b.status[h.Name]
b.handlersMu.RUnlock()
if ok {
if rewrite {
if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil {
log.Error().Err(err).Msg("Writing sticky cookie")

View File

@ -27,6 +27,7 @@ type namedHandler struct {
type Balancer struct {
wantsHealthCheck bool
// handlersMu is a mutex to protect the handlers slice, the status and the fenced maps.
handlersMu sync.RWMutex
handlers []*namedHandler
// status is a record of which child services of the Balancer are healthy, keyed
@ -34,11 +35,12 @@ type Balancer struct {
// created via Add, and it is later removed or added to the map as needed,
// through the SetStatus method.
status map[string]struct{}
// fenced is the list of terminating yet still serving child services.
fenced map[string]struct{}
// updaters is the list of hooks that are run (to update the Balancer
// parent(s)), whenever the Balancer status changes.
updaters []func(bool)
// fenced is the list of terminating yet still serving child services.
fenced map[string]struct{}
sticky *loadbalancer.Sticky
@ -180,7 +182,10 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if err != nil {
log.Error().Err(err).Msg("Error while getting sticky handler")
} else if h != nil {
if _, ok := b.status[h.Name]; ok {
b.handlersMu.RLock()
_, ok := b.status[h.Name]
b.handlersMu.RUnlock()
if ok {
if rewrite {
if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil {
log.Error().Err(err).Msg("Writing sticky cookie")