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:
@@ -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, " ")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user