package database import ( "context" "fmt" "log" "os" "time" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) func Open(ctx context.Context, databaseURL string) (*gorm.DB, error) { db, err := gorm.Open(postgres.Open(databaseURL), &gorm.Config{ Logger: gormLogger(), }) if err != nil { return nil, fmt.Errorf("open database: %w", err) } sqlDB, err := db.DB() if err != nil { return nil, fmt.Errorf("get sql database: %w", err) } sqlDB.SetMaxOpenConns(20) sqlDB.SetMaxIdleConns(10) sqlDB.SetConnMaxLifetime(30 * time.Minute) pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if err := sqlDB.PingContext(pingCtx); err != nil { _ = sqlDB.Close() return nil, fmt.Errorf("ping database: %w", err) } return db, nil } // gormLogger keeps normal "not found" lookups out of the error log. Missing // rows are expected during access checks and stale callback handling. func gormLogger() logger.Interface { return logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: 200 * time.Millisecond, LogLevel: logger.Warn, IgnoreRecordNotFoundError: true, }, ) }