From fdd8f6af44640dd0f976a83f9d2c7c5e006567c6 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 30 Sep 2026 14:19:49 +0700 Subject: [PATCH] fix(server): wait for polling and the final metrics flush on shutdown --- cmd/server/main.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index cba4e56..8b49616 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -124,9 +124,14 @@ func main() { log.Fatal("missing required env", "key", "TELEGRAM_BOT_TOKEN") } - // Periodic metrics flush. Cancels with rootCtx and emits one final - // flush on shutdown so the trailing window isn't lost. - go metrics.Run(rootCtx) + // Periodic metrics flush. Cancels with rootCtx; shutdown below waits for it + // and flushes once more after polling stops, so the trailing window and the + // last in-flight update are not lost when main returns. + metricsDone := make(chan struct{}) + go func() { + metrics.Run(rootCtx) + close(metricsDone) + }() provider, closeProvider, err := buildProvider(rootCtx, cfg) if err != nil { @@ -224,7 +229,9 @@ func main() { // Long polling is the sole Telegram transport (no webhook, no public // ingress). Telegram permits exactly one getUpdates consumer per bot token, // so deploy exactly one replica. The webhook was cleared at startup above. + pollingDone := make(chan struct{}) go func() { + defer close(pollingDone) log.Info("telegram long polling started") b.Start(rootCtx) // returns when rootCtx is cancelled log.Info("telegram long polling stopped") @@ -237,6 +244,13 @@ func main() { if err := srv.Shutdown(shutdownCtx); err != nil { log.Error("graceful shutdown failed", "err", err) } + select { + case <-pollingDone: + case <-shutdownCtx.Done(): + log.Warn("telegram long polling did not stop before shutdown timeout") + } + <-metricsDone + metrics.Flush() } func initStockStore(ctx context.Context, provider storage.Provider) error {