From dc56a9c2e28b0319b5ba30e982712d52dd23f437 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sat, 10 Oct 2026 19:09:33 +0300 Subject: [PATCH] =?UTF-8?q?fix(iot):=20run()=20int=20=D0=B2=D0=BC=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=BE=20os.Exit=20=E2=80=94=20=D1=84=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20flush=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20(=D0=BD?= =?UTF-8?q?=D0=B0=D1=85=D0=BE=D0=B4=D0=BA=D0=B0=20#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit os.Exit обходит defer-ы: на fatal-пути и при ошибке HTTP-сервера iotStore.Close() (финальный flushAll последнего батча телеметрии) не выполнялся. Логика вынесена в run() int, main = os.Exit(run()); все выходы через return — defer-ы отрабатывают. --- cmd/iot-service/main.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/iot-service/main.go b/cmd/iot-service/main.go index 5c1febe..f24ac33 100644 --- a/cmd/iot-service/main.go +++ b/cmd/iot-service/main.go @@ -33,11 +33,18 @@ import ( // version — подставляется через ldflags при сборке. var version = "dev" +// main — тонкая обёртка: os.Exit обходит defer-ы, поэтому вся логика в run(), +// которая возвращает код выхода и выполняет свои defer-ы (devices/iotStore +// Close, финальный flush) ДО выхода (ревью 🔴5: «os.Exit обходит defer-ы»). func main() { + os.Exit(run()) +} + +func run() int { cfg, err := config.Load() if err != nil { slog.Error("config", "err", err) - os.Exit(1) + return 1 } log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: cfg.LogLevel})) @@ -54,7 +61,7 @@ func main() { storeCancel() if err != nil { log.Error("devices store", "err", err) - os.Exit(1) + return 1 } defer devices.Close() log.Info("devices store ready") @@ -65,14 +72,14 @@ func main() { ensureCancel() if err != nil { log.Error("ensure bridge device", "err", err) - os.Exit(1) + return 1 } log.Info("bridge device ensured", "username", cfg.MQTTUsername) iotStore, err := iotpg.New(cfg.IOTPGDSN, log) if err != nil { log.Error("iotpg store", "err", err) - os.Exit(1) + return 1 } defer iotStore.Close() log.Info("telemetry store ready") @@ -146,13 +153,18 @@ func main() { log.Info("iot-service listening", "addr", server.Addr) if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Error("http server", "err", err) - os.Exit(1) + cancel() // останавливаем bridge/consumer + wg.Wait() + return 1 } wg.Wait() if fatal.Load() { + // Не os.Exit: возвращаем код, чтобы отработали defer-ы (iotStore.Close → + // финальный flush последнего батча). os.Exit их бы обошёл. log.Error("iot-service: подсистема завершилась с ошибкой — выход ≠ 0") - os.Exit(1) + return 1 } log.Info("iot-service stopped") + return 0 }