package database import ( "database/sql" "fmt" "time" "github.com/rs/zerolog" "gorm.io/driver/postgres" "gorm.io/gorm" "base/config" "base/pkg/metrics" ) // NewRWDatabaseConnection creates a new database connection func NewRWDatabaseConnection(cfg *config.AppConfig, logger zerolog.Logger, metric *metrics.Metrics) (*gorm.DB, error) { start := time.Now() lg := logger. With(). Str("module", "database"). Int("maxOpenConnection", cfg.Database.MaxOpenConns). Int("maxIdleConnection", cfg.Database.MaxIdleConns). Logger() gormConfig := &gorm.Config{Logger: NewGormLogger(logger, time.Second*5)} wrDB, sqlDB, err := wr(cfg, gormConfig) if err != nil { fmt.Println("[DATABASE CONNECTION ERROR]Failed to connect to database", err.Error()) metric.RecordDatabaseQuery("ConnectWR", "database", time.Since(start), err) lg.Error(). Err(err). Msg("failed to connect to database") return nil, err } metric.RecordDatabaseQuery("ConnectWR", "database", time.Since(start), nil) // Start monitoring connection pool metrics go monitorConnectionPool(sqlDB, metric, logger) duration := time.Since(start) metric.RecordDatabaseQuery("Connect", "database", duration, nil) lg.Info().Msg("Database connection established") return wrDB, nil } func wr(config *config.AppConfig, gormConfig *gorm.Config) (*gorm.DB, *sql.DB, error) { // PostgreSQL DSN format: postgres://user:password@host:port/dbname?sslmode=disable dsn := fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=UTC", config.PgDatabaseConfig.Host, config.PgDatabaseConfig.User, config.PgDatabaseConfig.Password, config.PgDatabaseConfig.Name, config.PgDatabaseConfig.Port, config.PgDatabaseConfig.SSLMode, ) db, err := gorm.Open(postgres.Open(dsn), gormConfig) if err != nil { return nil, nil, err } // Get the underlying sql.DB sqlDB, err := db.DB() if err != nil { return nil, nil, err } sqlDB.SetMaxIdleConns(int(config.PgDatabaseConfig.PoolConfig.MinConn)) sqlDB.SetMaxOpenConns(int(config.PgDatabaseConfig.PoolConfig.MaxConn)) // Parse and set connection timeouts from config // TODO: this is not type safe if config.PgDatabaseConfig.PoolConfig.MaxConnIdleTime.String() != "" { if idleTime, parseDurationErr := time.ParseDuration(config.PgDatabaseConfig.PoolConfig.MaxConnIdleTime.String()); parseDurationErr == nil { sqlDB.SetConnMaxIdleTime(idleTime) } else { sqlDB.SetConnMaxIdleTime(5 * time.Minute) } } if config.PgDatabaseConfig.PoolConfig.MaxConnLifetime.String() != "" { if lifetime, parseDurationErr := time.ParseDuration(config.PgDatabaseConfig.PoolConfig.MaxConnLifetime.String()); parseDurationErr == nil { sqlDB.SetConnMaxLifetime(lifetime) } else { sqlDB.SetConnMaxLifetime(30 * time.Minute) } } return db, sqlDB, nil }