package services import ( "context" "fmt" "time" "github.com/jackc/pgx/v5/pgxpool" "jd-book-uploader/config" ) // DB holds the database connection pool var DB *pgxpool.Pool // NewDBPool creates a new database connection pool func NewDBPool(cfg *config.Config) (*pgxpool.Pool, error) { // Build connection string connString := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", cfg.DBHost, cfg.DBPort, cfg.DBUser, cfg.DBPassword, cfg.DBName, ) // Parse connection string poolConfig, err := pgxpool.ParseConfig(connString) if err != nil { return nil, fmt.Errorf("failed to parse connection string: %w", err) } // Configure connection pool poolConfig.MaxConns = 10 poolConfig.MinConns = 2 poolConfig.MaxConnLifetime = time.Hour poolConfig.MaxConnIdleTime = time.Minute * 30 poolConfig.HealthCheckPeriod = time.Minute // Set connection timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Create connection pool pool, err := pgxpool.NewWithConfig(ctx, poolConfig) if err != nil { return nil, fmt.Errorf("failed to create connection pool: %w", err) } // Test connection if err := pool.Ping(ctx); err != nil { pool.Close() return nil, fmt.Errorf("failed to ping database: %w", err) } DB = pool return pool, nil } // CloseDB closes the database connection pool func CloseDB() error { if DB != nil { DB.Close() } return nil } // RetryConnection attempts to reconnect to the database with retries func RetryConnection(cfg *config.Config, maxRetries int, retryDelay time.Duration) (*pgxpool.Pool, error) { var pool *pgxpool.Pool var err error for i := 0; i < maxRetries; i++ { pool, err = NewDBPool(cfg) if err == nil { return pool, nil } if i < maxRetries-1 { time.Sleep(retryDelay) } } return nil, fmt.Errorf("failed to connect after %d attempts: %w", maxRetries, err) }