gohorsejobs/backend/internal/database/database.go
2026-02-12 20:13:46 -03:00

96 lines
2.6 KiB
Go
Executable file

package database
import (
"database/sql"
"fmt"
"log"
"os"
"strings"
"time"
_ "github.com/lib/pq"
)
var DB *sql.DB
func InitDB() {
var err error
connStr, err := BuildConnectionString()
if err != nil {
log.Fatalf("Configuration error: %v", err)
}
DB, err = sql.Open("postgres", connStr)
if err != nil {
log.Fatalf("Error opening database: %v", err)
}
// Connection Pool Settings
// Adjust these values based on your production load and database resources
DB.SetMaxOpenConns(25) // Limit total connections
DB.SetMaxIdleConns(25) // Keep connections open for reuse
DB.SetConnMaxLifetime(5 * time.Minute) // Recycle connections every 5 min (avoids stale connections dropped by firewalls)
DB.SetConnMaxIdleTime(5 * time.Minute) // Close idle connections after 5 min
if err = DB.Ping(); err != nil {
log.Fatalf("Error connecting to database: %v", err)
}
log.Println("✅ Successfully connected to the database")
}
func BuildConnectionString() (string, error) {
if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" {
log.Println("Using DATABASE_URL for connection")
return dbURL, nil
}
return "", fmt.Errorf("DATABASE_URL environment variable not set")
}
func RunMigrations() {
files, err := os.ReadDir("migrations")
if err != nil {
// Try fallback to relative path if running from cmd/api
files, err = os.ReadDir("../../migrations")
if err != nil {
log.Printf("⚠️ Warning: Could not list migrations directory: %v", err)
return
}
}
// Sort files by name to ensure order (001, 002, ...)
// ReadDir returns sorted by name automatically, but let's be safe if logic changes?
// Actually ReadDir result is sorted by filename.
for _, file := range files {
if file.IsDir() {
continue
}
log.Printf("📦 Running migration: %s", file.Name())
content, err := os.ReadFile("migrations/" + file.Name())
if err != nil {
// Try fallback
content, err = os.ReadFile("../../migrations/" + file.Name())
if err != nil {
log.Fatalf("❌ Error reading migration file %s: %v", file.Name(), err)
}
}
_, err = DB.Exec(string(content))
if err != nil {
errStr := err.Error()
// Check if it's an "already exists" error - these are safe to skip
if strings.Contains(errStr, "already exists") {
log.Printf("⏭️ Migration %s skipped (already applied)", file.Name())
} else {
// Real error - log it
log.Printf("❌ Error running migration %s: %v", file.Name(), err)
}
} else {
log.Printf("✅ Migration %s executed successfully", file.Name())
}
}
log.Println("All migrations processed")
}