DB optimization: pool, golang-migrate, consolidate to single Postgres

- Fix DB_NAME=db_yal -> mydb in api_yal .env
- Add connection pool (MaxOpenConns 25, MaxIdleConns 10, ConnMaxLifetime 30m)
- Replace GORM AutoMigrate with golang-migrate in api_yal and api_bb
- Create embedded SQL migrations for both APIs
- Add DB_SCHEMA support to api_bb config
- Consolidate to single Postgres: db_bb -> schema 'bb' on db container
- Remove db_bb service, bb-network, db_bb volume from compose
- Remove api_tp targets from Makefile
- Clean up old migrate.go
This commit is contained in:
valitovgaziz
2026-06-12 10:47:41 +05:00
parent ec83b97c25
commit b0350abfbe
18 changed files with 610 additions and 206 deletions
+60 -16
View File
@@ -1,9 +1,15 @@
package database
import (
"api_bb/migrations"
"database/sql"
"fmt"
"strings"
"time"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
"github.com/golang-migrate/migrate/v4/source/iofs"
"go.uber.org/zap"
"gorm.io/driver/postgres"
"gorm.io/gorm"
@@ -17,26 +23,34 @@ type Database struct {
}
type Config struct {
URL string
URL string
Schema string
}
func NewDatabase(cfg *Config) *Database {
if cfg.Schema == "" {
cfg.Schema = "public"
}
return &Database{
cfg: cfg,
}
}
// Connect устанавливает соединение с базой данных
func (d *Database) Connect() error {
zapLogger := logger.Get()
// Логирование попытки подключения к БД
zapLogger.Info("attempting to connect to database",
zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
zap.String("schema", d.cfg.Schema),
)
db, err := gorm.Open(postgres.Open(d.cfg.URL), &gorm.Config{})
dsn := d.cfg.URL
if d.cfg.Schema != "public" {
dsn = dsn + fmt.Sprintf(" search_path=%s", d.cfg.Schema)
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
zapLogger.Error("failed to connect to database",
zap.Error(err),
@@ -47,7 +61,21 @@ func (d *Database) Connect() error {
d.DB = db
// Логирование успешного подключения к БД
zapLogger.Info("Configure connection pool")
sqlDB, err := d.DB.DB()
if err != nil {
return fmt.Errorf("failed to get underlying sql.DB: %w", err)
}
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(30 * time.Minute)
zapLogger.Info("Run database migrations")
if err := d.runMigrations(sqlDB); err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
zapLogger.Info("Migrations completed successfully")
zapLogger.Info("successfully connected to database",
zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
@@ -56,7 +84,32 @@ func (d *Database) Connect() error {
return nil
}
// Ping проверяет соединение с базой данных
func (d *Database) runMigrations(sqlDB *sql.DB) error {
zapLogger := logger.Get()
source, err := iofs.New(migrations.FS, ".")
if err != nil {
return fmt.Errorf("failed to create migration source: %w", err)
}
driver, err := postgres.WithInstance(sqlDB, &postgres.Config{})
if err != nil {
return fmt.Errorf("failed to create postgres driver: %w", err)
}
m, err := migrate.NewWithInstance("iofs", source, "postgres", driver)
if err != nil {
return fmt.Errorf("failed to create migrate instance: %w", err)
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
zapLogger.Error("Migration error", zap.Error(err))
return fmt.Errorf("failed to apply migrations: %w", err)
}
return nil
}
func (d *Database) Ping() error {
zapLogger := logger.Get()
@@ -75,7 +128,6 @@ func (d *Database) Ping() error {
return nil
}
// Close закрывает соединение с базой данных
func (d *Database) Close() error {
zapLogger := logger.Get()
@@ -99,11 +151,7 @@ func (d *Database) Close() error {
return nil
}
// Вспомогательные функции для работы с DSN
// ExtractHostFromDSN извлекает хост из DSN строки
func ExtractHostFromDSN(dsn string) string {
// Простая реализация для PostgreSQL DSN
parts := strings.Split(dsn, " ")
for _, part := range parts {
if strings.HasPrefix(part, "host=") {
@@ -113,9 +161,7 @@ func ExtractHostFromDSN(dsn string) string {
return "unknown"
}
// ExtractDBNameFromDSN извлекает имя базы данных из DSN строки
func ExtractDBNameFromDSN(dsn string) string {
// Простая реализация для PostgreSQL DSN
parts := strings.Split(dsn, " ")
for _, part := range parts {
if strings.HasPrefix(part, "dbname=") {
@@ -125,9 +171,7 @@ func ExtractDBNameFromDSN(dsn string) string {
return "unknown"
}
// MaskPassword маскирует пароль в DSN строке для безопасного логирования
func MaskPassword(dsn string) string {
// Простая реализация - заменяет пароль на ***
parts := strings.Split(dsn, " ")
for i, part := range parts {
if strings.HasPrefix(part, "password=") {
@@ -136,4 +180,4 @@ func MaskPassword(dsn string) string {
}
}
return strings.Join(parts, " ")
}
}