From 1f1354e72b8f7e1f3cbfb6de719cb624c8b771cc Mon Sep 17 00:00:00 2001 From: Grendgi Date: Fri, 12 Jun 2026 16:28:02 +0300 Subject: [PATCH] Retry monitoring TG database connection on startup --- cmd/classifier/main.go | 3 ++- cmd/server/main.go | 3 ++- internal/dbretry/dbretry.go | 44 +++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 internal/dbretry/dbretry.go diff --git a/cmd/classifier/main.go b/cmd/classifier/main.go index d68b556..197c617 100644 --- a/cmd/classifier/main.go +++ b/cmd/classifier/main.go @@ -15,6 +15,7 @@ import ( "time" "monitoring-tg/internal/aiservice" + "monitoring-tg/internal/dbretry" "github.com/jackc/pgx/v5/pgxpool" ) @@ -69,7 +70,7 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - pool, err := pgxpool.New(ctx, cfg.databaseURL()) + pool, err := dbretry.Connect(ctx, cfg.databaseURL(), 2*time.Minute) if err != nil { slog.Error("db_connect_failed", "error", err) os.Exit(1) diff --git a/cmd/server/main.go b/cmd/server/main.go index 205b6de..1d9943e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -21,6 +21,7 @@ import ( "time" "monitoring-tg/internal/aiservice" + "monitoring-tg/internal/dbretry" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" @@ -134,7 +135,7 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - pool, err := pgxpool.New(ctx, cfg.databaseURL()) + pool, err := dbretry.Connect(ctx, cfg.databaseURL(), 2*time.Minute) if err != nil { slog.Error("db_connect_failed", "error", err) os.Exit(1) diff --git a/internal/dbretry/dbretry.go b/internal/dbretry/dbretry.go new file mode 100644 index 0000000..a71abd0 --- /dev/null +++ b/internal/dbretry/dbretry.go @@ -0,0 +1,44 @@ +package dbretry + +import ( + "context" + "fmt" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +func Connect(ctx context.Context, databaseURL string, maxWait time.Duration) (*pgxpool.Pool, error) { + deadline := time.Now().Add(maxWait) + var lastErr error + + for attempt := 1; ; attempt++ { + pool, err := pgxpool.New(ctx, databaseURL) + if err == nil { + if pingErr := pool.Ping(ctx); pingErr == nil { + return pool, nil + } else { + err = fmt.Errorf("ping postgres: %w", pingErr) + pool.Close() + } + } else { + err = fmt.Errorf("connect postgres: %w", err) + } + lastErr = err + + if time.Now().After(deadline) { + return nil, fmt.Errorf("connect postgres after retry: %w", lastErr) + } + sleep := time.Duration(attempt) * time.Second + if sleep > 5*time.Second { + sleep = 5 * time.Second + } + timer := time.NewTimer(sleep) + select { + case <-ctx.Done(): + timer.Stop() + return nil, fmt.Errorf("connect postgres cancelled: %w", ctx.Err()) + case <-timer.C: + } + } +}