package db import ( "context" "fmt" "kooltap-updateserver/internal/models" "log" "time" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) // Connect opens the Postgres database and migrates the schema. // It retries for a while because the database container may still be starting. func Connect(dsn string) (*gorm.DB, error) { cfg := &gorm.Config{ // Turns driver specific unique violations into gorm.ErrDuplicatedKey TranslateError: true, Logger: logger.Default.LogMode(logger.Warn), } var ( db *gorm.DB err error ) deadline := time.Now().Add(60 * time.Second) for { db, err = gorm.Open(postgres.Open(dsn), cfg) if err == nil { err = ping(db) } if err == nil || time.Now().After(deadline) { break } log.Printf("database not ready, retrying: %v", err) time.Sleep(2 * time.Second) } if err != nil { return nil, fmt.Errorf("connect to postgres: %w", err) } sqlDB, err := db.DB() if err != nil { return nil, err } sqlDB.SetMaxOpenConns(10) sqlDB.SetMaxIdleConns(5) sqlDB.SetConnMaxLifetime(30 * time.Minute) if err := Migrate(db); err != nil { return nil, err } return db, nil } // Migrate creates or updates the tables for all models. func Migrate(db *gorm.DB) error { if err := db.AutoMigrate(&models.Release{}); err != nil { return fmt.Errorf("migrate: %w", err) } return nil } func ping(db *gorm.DB) error { sqlDB, err := db.DB() if err != nil { return err } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() return sqlDB.PingContext(ctx) }