mirror of
https://github.com/nprasad2077/NBA_Go.git
synced 2026-09-22 22:15:13 +00:00
145 lines
4.0 KiB
Go
145 lines
4.0 KiB
Go
// File: config/database.go
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/nprasad2077/NBA_Go/models"
|
|
"github.com/nprasad2077/NBA_Go/utils/metrics"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
"gorm.io/plugin/dbresolver"
|
|
)
|
|
|
|
type DBConfig struct {
|
|
User string
|
|
Password string
|
|
DBName string
|
|
SSLMode string
|
|
WriteHost string
|
|
WritePort string
|
|
ReadHost string
|
|
ReadPort string
|
|
}
|
|
|
|
func LoadDBConfig() DBConfig {
|
|
user := getEnvOrDefault("DB_USER", "postgres")
|
|
pass := getEnvOrDefault("DB_PASSWORD", "postgrespassword")
|
|
dbname := getEnvOrDefault("DB_NAME", "appdb")
|
|
ssl := getEnvOrDefault("DB_SSLMODE", "disable")
|
|
|
|
// Base fallback
|
|
baseHost := getEnvOrDefault("DB_HOST", "178.105.149.129")
|
|
basePort := os.Getenv("DB_PORT")
|
|
|
|
writeHost := getEnvOrDefault("DB_WRITE_HOST", baseHost)
|
|
writePort := getEnvOrDefault("DB_WRITE_PORT", "5437")
|
|
if os.Getenv("DB_WRITE_PORT") == "" && basePort != "" {
|
|
writePort = basePort
|
|
}
|
|
|
|
readHost := getEnvOrDefault("DB_READ_HOST", baseHost)
|
|
readPort := getEnvOrDefault("DB_READ_PORT", "5438")
|
|
if os.Getenv("DB_READ_PORT") == "" && basePort != "" {
|
|
readPort = basePort
|
|
}
|
|
|
|
return DBConfig{
|
|
User: user,
|
|
Password: pass,
|
|
DBName: dbname,
|
|
SSLMode: ssl,
|
|
WriteHost: writeHost,
|
|
WritePort: writePort,
|
|
ReadHost: readHost,
|
|
ReadPort: readPort,
|
|
}
|
|
}
|
|
|
|
func buildDSN(host, port, user, pass, dbname, ssl string) string {
|
|
return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=UTC",
|
|
host, port, user, pass, dbname, ssl)
|
|
}
|
|
|
|
func InitDB(shouldMigrate bool) *gorm.DB {
|
|
cfg := LoadDBConfig()
|
|
writeDSN := buildDSN(cfg.WriteHost, cfg.WritePort, cfg.User, cfg.Password, cfg.DBName, cfg.SSLMode)
|
|
readDSN := buildDSN(cfg.ReadHost, cfg.ReadPort, cfg.User, cfg.Password, cfg.DBName, cfg.SSLMode)
|
|
|
|
slog.Info("🔌 Connecting to PostgreSQL Cluster via HAProxy",
|
|
"write_ingress", fmt.Sprintf("%s:%s", cfg.WriteHost, cfg.WritePort),
|
|
"read_ingress", fmt.Sprintf("%s:%s", cfg.ReadHost, cfg.ReadPort),
|
|
"database", cfg.DBName,
|
|
)
|
|
|
|
// Primary GORM instance connects to Write Ingress (:5437)
|
|
db, err := gorm.Open(postgres.Open(writeDSN), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Warn),
|
|
NowFunc: func() time.Time { return time.Now().UTC() },
|
|
})
|
|
if err != nil {
|
|
slog.Error("❌ Failed to connect to Primary/Write database ingress", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Register DBResolver for automatic Read/Write splitting
|
|
resolverCfg := dbresolver.Config{
|
|
Sources: []gorm.Dialector{postgres.Open(writeDSN)},
|
|
Replicas: []gorm.Dialector{postgres.Open(readDSN)},
|
|
Policy: dbresolver.RandomPolicy{}, // HAProxy handles round-robin balancing across replicas
|
|
}
|
|
|
|
err = db.Use(dbresolver.Register(resolverCfg).
|
|
SetConnMaxIdleTime(30 * time.Minute).
|
|
SetConnMaxLifetime(2 * time.Hour).
|
|
SetMaxIdleConns(10).
|
|
SetMaxOpenConns(50),
|
|
)
|
|
if err != nil {
|
|
slog.Error("❌ Failed to configure DBResolver plugin", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
metrics.DBOperationsTotal.WithLabelValues("connect", "database").Inc()
|
|
|
|
// Start asynchronous connection pool metrics collection
|
|
metrics.StartDBPoolMetricsCollector(db)
|
|
|
|
if shouldMigrate {
|
|
slog.Info("🚀 Executing database AutoMigrate on Primary (:5437)...")
|
|
modelsToMigrate := []interface{}{
|
|
&models.APIKey{},
|
|
&models.PlayerAdvancedStat{},
|
|
&models.PlayerTotalStat{},
|
|
&models.PlayerShotChart{},
|
|
&models.Game{},
|
|
&models.LineScore{},
|
|
&models.PlayerGameBasicStat{},
|
|
&models.PlayerGameAdvStat{},
|
|
&models.TeamGameBasicStat{},
|
|
&models.TeamGameAdvStat{},
|
|
}
|
|
for _, m := range modelsToMigrate {
|
|
if err := db.Clauses(dbresolver.Write).AutoMigrate(m); err != nil {
|
|
slog.Error("❌ Migration failed", "model", fmt.Sprintf("%T", m), "error", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
metrics.DBOperationsTotal.WithLabelValues("migrate", "database").Inc()
|
|
slog.Info("✅ Database schema migration completed successfully")
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func getEnvOrDefault(key, fallback string) string {
|
|
if val := os.Getenv(key); val != "" {
|
|
return val
|
|
}
|
|
return fallback
|
|
} |