// Package db — подключение к PostgreSQL и применение миграций. package db import ( "context" "database/sql" "fmt" "github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/stdlib" "github.com/pressly/goose/v3" "github.com/pressly/goose/v3/lock" "pipu/api/migrations" ) // Connect открывает пул соединений и проверяет доступность БД. func Connect(ctx context.Context, url string) (*pgxpool.Pool, error) { pool, err := pgxpool.New(ctx, url) if err != nil { return nil, fmt.Errorf("db: open pool: %w", err) } if err := pool.Ping(ctx); err != nil { pool.Close() return nil, fmt.Errorf("db: ping: %w", err) } return pool, nil } // Migrate применяет встроенные миграции goose. Advisory-lock в PostgreSQL не даёт // нескольким экземплярам api применять миграции одновременно. func Migrate(ctx context.Context, pool *pgxpool.Pool) error { sqlDB := stdlib.OpenDBFromPool(pool) defer func(d *sql.DB) { _ = d.Close() }(sqlDB) locker, err := lock.NewPostgresSessionLocker() if err != nil { return fmt.Errorf("db: migrations lock: %w", err) } provider, err := goose.NewProvider(goose.DialectPostgres, sqlDB, migrations.FS, goose.WithSessionLocker(locker)) if err != nil { return fmt.Errorf("db: migrations: %w", err) } if _, err := provider.Up(ctx); err != nil { return fmt.Errorf("db: migrate up: %w", err) } return nil }