Files
backend_jupiter/db/connect.go
2026-05-25 11:45:56 +05:30

222 lines
4.3 KiB
Go

package db
import (
"context"
"database/sql"
"fmt"
"nearle/utils"
"os"
"time"
"github.com/redis/go-redis/v9"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
DB *gorm.DB
)
// --------------------
// DATABASE CONNECTION
// --------------------
func Connect() {
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Kolkata",
mustEnv("DB_HOST"),
mustEnv("DB_USER"),
mustEnv("DB_PASSWORD"),
mustEnv("DB_NAME"),
getEnv("DB_PORT", "5432"),
)
var err error
maxRetries := 10
backoff := 2 * time.Second
for i := 1; i <= maxRetries; i++ {
utils.Logger.Infow("Connecting to database", "attempt", i)
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Error),
})
if err == nil {
sqlDB, dbErr := DB.DB()
if dbErr == nil {
// 🔥 Ping with timeout (important)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if pingErr := sqlDB.PingContext(ctx); pingErr == nil {
setupDB(DB)
utils.Logger.Infow("✅ Database connected successfully")
// Start background health check
startDBHealthCheck(sqlDB)
return
} else {
err = pingErr
}
} else {
err = dbErr
}
}
utils.Logger.Errorw("❌ DB connection failed", "attempt", i, "error", err)
time.Sleep(backoff)
backoff *= 2
}
utils.Logger.Fatalw("☢️ CRITICAL: DB connection failed after retries")
}
// --------------------
// CONNECTION POOLING
// --------------------
func setupDB(database *gorm.DB) {
sqlDB, err := database.DB()
if err != nil {
utils.Logger.Errorw("Failed to get sql.DB", "error", err)
return
}
// 🔥 K8s SAFE CONFIG
sqlDB.SetMaxOpenConns(30) // total open connections
sqlDB.SetMaxIdleConns(5) // idle connections
sqlDB.SetConnMaxLifetime(5 * time.Minute) // avoid stale connections
sqlDB.SetConnMaxIdleTime(2 * time.Minute)
stats := sqlDB.Stats()
utils.Logger.Infow("DB Pool configured",
"MaxOpen", stats.MaxOpenConnections,
)
}
// --------------------
// DB HEALTH CHECK
// --------------------
func startDBHealthCheck(sqlDB *sql.DB) {
go func() {
for {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
err := sqlDB.PingContext(ctx)
cancel()
if err != nil {
utils.Logger.Errorw("❌ DB connection lost", "error", err)
}
time.Sleep(30 * time.Second)
}
}()
}
// --------------------
// CLOSE DB
// --------------------
func CloseDB() {
if DB == nil {
return
}
sqlDB, err := DB.DB()
if err != nil {
utils.Logger.Errorw("Error retrieving sql.DB", "error", err)
return
}
utils.Logger.Infow("Closing DB connection")
sqlDB.Close()
}
// --------------------
// ENV HELPERS
// --------------------
func mustEnv(key string) string {
val := os.Getenv(key)
if val == "" {
utils.Logger.Warnw("Missing env variable", "key", key)
}
return val
}
func getEnv(key, fallback string) string {
if val := os.Getenv(key); val != "" {
return val
}
return fallback
}
// --------------------
// REDIS CONNECTION
// --------------------
var Rdb *redis.Client
var Ctx = context.Background()
func InitRedis() {
redisHost := getEnv("REDIS_HOST", "66.116.226.255") // ✅ FIXED IP
redisPort := getEnv("REDIS_PORT", "6379")
addr := fmt.Sprintf("%s:%s", redisHost, redisPort)
Rdb = redis.NewClient(&redis.Options{
Addr: addr,
Username: "default",
Password: "Package@324969#",
DB: 0,
// ✅ TIMEOUTS (VERY IMPORTANT)
DialTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
// ✅ POOL
PoolSize: 50,
MinIdleConns: 10,
// ✅ RETRIES
MaxRetries: 3,
MinRetryBackoff: 500 * time.Millisecond,
MaxRetryBackoff: 2 * time.Second,
})
maxRetries := 5
backoff := 1 * time.Second
for i := 1; i <= maxRetries; i++ {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
_, err := Rdb.Ping(ctx).Result()
cancel()
if err == nil {
utils.Logger.Infow("✅ Redis connected successfully", "addr", addr)
return
}
utils.Logger.Errorw("❌ Redis connection failed",
"attempt", i,
"addr", addr,
"error", err,
)
time.Sleep(backoff)
backoff *= 2
}
utils.Logger.Fatalw("☢️ CRITICAL: Redis connection failed after retries", "addr", addr)
}