From 4f0a42d1b4f7da3f9738b8647f1b6af22fd0f4b0 Mon Sep 17 00:00:00 2001 From: Nate Morse Date: Tue, 30 Dec 2025 10:27:34 -0600 Subject: [PATCH] Fix goroutine leak This PR modifies the `Inform()` function to take in a [Context](https://pkg.go.dev/context) and exit when a signal comes in on the `ctx.Done()` channel. This fixes an issue where the goroutine is not cleaned up when a container restarts or is redeployed. Without this change, additional goroutines are spawned whenever a container restarts, and the previously spawned goroutine still remains active. --- maintenance.go | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/maintenance.go b/maintenance.go index 76433b8..d233403 100644 --- a/maintenance.go +++ b/maintenance.go @@ -47,32 +47,38 @@ func CreateConfig() *Config { } // Inform if there are hosts in maintenance -func Inform(config *Config) { +func Inform(ctx context.Context, config *Config) { t := time.NewTicker(time.Second * config.InformInterval) defer t.Stop() - for ; true; <-t.C { - client := http.Client{ - Timeout: time.Second * config.InformTimeout, - } + for { + select { + case <-t.C: + client := http.Client{ + Timeout: time.Second * config.InformTimeout, + } - req, _ := http.NewRequest(http.MethodGet, config.InformUrl, nil) - res, doErr := client.Do(req) - if doErr != nil { - log.Printf("Inform: %v", doErr) // Don't fatal, just go further - continue - } + req, _ := http.NewRequest(http.MethodGet, config.InformUrl, nil) + res, doErr := client.Do(req) + if doErr != nil { + log.Printf("Inform: %v", doErr) // Don't fatal, just go further + continue + } - defer res.Body.Close() + defer res.Body.Close() - decoder := json.NewDecoder(res.Body) - decodeErr := decoder.Decode(&hosts) - if decodeErr != nil { - log.Printf("Inform: %v", decodeErr) // Don't fatal, just go further - continue - } + decoder := json.NewDecoder(res.Body) + decodeErr := decoder.Decode(&hosts) + if decodeErr != nil { + log.Printf("Inform: %v", decodeErr) // Don't fatal, just go further + continue + } - log.Printf("Inform response: %v", hosts) + log.Printf("Inform response: %v", hosts) + case <-ctx.Done(): + log.Printf("Inform complete") + return + } } } @@ -137,8 +143,8 @@ func (rw *ResponseWriter) WriteHeader(statusCode int) { rw.ResponseWriter.WriteHeader(http.StatusServiceUnavailable) } -func New(_ context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { - go Inform(config) +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + go Inform(ctx, config) return &Maintenance{ name: name,