228 lines
6.3 KiB
Go
228 lines
6.3 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"embed"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/golang-migrate/migrate/v4"
|
|
"github.com/golang-migrate/migrate/v4/database/postgres"
|
|
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
var Dbpool *pgxpool.Pool
|
|
var Ctx = context.Background()
|
|
|
|
//go:embed "migrations/*.sql"
|
|
var MigrationsFs embed.FS
|
|
|
|
func InitDB(host string, port string, user string, password string, dbname string) {
|
|
|
|
psqlInfo := fmt.Sprintf("host=%s port=%s user=%s "+
|
|
"password=%s dbname=%s sslmode=disable",
|
|
host, port, user, password, dbname)
|
|
|
|
fmt.Println(psqlInfo)
|
|
|
|
var err error
|
|
Dbpool, err = pgxpool.New(Ctx, psqlInfo)
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
var success string
|
|
err = Dbpool.QueryRow(Ctx, "select 'Successfully connected!'").Scan(&success)
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println(success)
|
|
}
|
|
|
|
func CloseDb() {
|
|
fmt.Println("Closing connection to database")
|
|
Dbpool.Close()
|
|
}
|
|
|
|
func Testf() {
|
|
rows, dbErr := Dbpool.Query(Ctx, "select game_name from game")
|
|
if dbErr != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
|
os.Exit(1)
|
|
}
|
|
for rows.Next() {
|
|
var gameName string
|
|
dbErr = rows.Scan(&gameName)
|
|
if dbErr != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
|
}
|
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", gameName)
|
|
}
|
|
}
|
|
|
|
func ResetGameIdSeq() {
|
|
_, err := Dbpool.Query(Ctx, "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func createDb(host string, port string, user string, password string, dbname string) {
|
|
conninfo := fmt.Sprintf("host=%s port=%s user=%s password=%s sslmode=disable", host, port, user, password)
|
|
db, err := sql.Open("postgres", conninfo)
|
|
defer db.Close()
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
_, err = db.Exec("create database " + dbname)
|
|
if err != nil {
|
|
//handle the error
|
|
log.Fatal(err)
|
|
}
|
|
log.Println("Finished creating database")
|
|
}
|
|
|
|
func Migrate_db(host string, port string, user string, password string, dbname string) {
|
|
migrationInfo := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable",
|
|
user, password, host, port, dbname)
|
|
|
|
fmt.Println("Migration Info: ", migrationInfo)
|
|
|
|
db, err := sql.Open("postgres", migrationInfo)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
_, err = db.Query("select * from game")
|
|
if err != nil {
|
|
log.Println(err)
|
|
createDb(host, port, user, password, dbname)
|
|
|
|
db, err = sql.Open("postgres", migrationInfo)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
}
|
|
|
|
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
files, err := iofs.New(MigrationsFs, "migrations")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
m, err := migrate.NewWithInstance("iofs", files, "postgres", driver)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
/*m, err := migrate.NewWithDatabaseInstance(
|
|
"file://./db/migrations/",
|
|
"postgres", driver)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}*/
|
|
|
|
version, _, err := m.Version()
|
|
if err != nil {
|
|
log.Println("Migration version err: ", err)
|
|
}
|
|
|
|
fmt.Println("Migration version before: ", version)
|
|
|
|
//err = m.Force(1)
|
|
//err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to run
|
|
//if err != nil {
|
|
// log.Println("Force err: ", err)
|
|
//}
|
|
|
|
err = m.Migrate(2)
|
|
//err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to run
|
|
if err != nil {
|
|
log.Println("Migration err: ", err)
|
|
}
|
|
|
|
versionAfter, _, err := m.Version()
|
|
if err != nil {
|
|
log.Println("Migration version err: ", err)
|
|
}
|
|
|
|
fmt.Println("Migration version after: ", versionAfter)
|
|
|
|
fmt.Println("Migration done")
|
|
|
|
db.Close()
|
|
}
|
|
|
|
// Health checks the health of the database connection by pinging the database.
|
|
// It returns a map with keys indicating various health statistics.
|
|
func Health() map[string]string {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
stats := make(map[string]string)
|
|
|
|
// Ping the database
|
|
//err := s.db.PingContext(ctx)
|
|
err := Dbpool.Ping(ctx)
|
|
if err != nil {
|
|
stats["status"] = "down"
|
|
stats["error"] = fmt.Sprintf("db down: %v", err)
|
|
log.Fatalf("db down: %v", err) // Log the error and terminate the program
|
|
return stats
|
|
}
|
|
|
|
// Database is up, add more statistics
|
|
stats["status"] = "up"
|
|
stats["message"] = "It's healthy"
|
|
|
|
// Get database stats (like open connections, in use, idle, etc.)
|
|
//dbStats := s.db.Stats()
|
|
dbStats := Dbpool.Stat()
|
|
//stats["open_connections"] = strconv.Itoa(dbStats.OpenConnections)
|
|
stats["open_connections"] = strconv.Itoa(int(dbStats.NewConnsCount()))
|
|
//stats["in_use"] = strconv.Itoa(dbStats.InUse)
|
|
stats["in_use"] = strconv.Itoa(int(dbStats.AcquiredConns()))
|
|
//stats["idle"] = strconv.Itoa(dbStats.Idle)
|
|
stats["idle"] = strconv.Itoa(int(dbStats.IdleConns()))
|
|
//stats["wait_count"] = strconv.FormatInt(dbStats.WaitCount, 10)
|
|
stats["wait_count"] = strconv.FormatInt(dbStats.AcquireCount(), 10)
|
|
//stats["wait_duration"] = dbStats.WaitDuration.String()
|
|
stats["wait_duration"] = dbStats.AcquireDuration().String()
|
|
//stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleClosed, 10)
|
|
stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleDestroyCount(), 10)
|
|
//stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeClosed, 10)
|
|
stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeDestroyCount(), 10)
|
|
|
|
// Evaluate stats to provide a health message
|
|
if int(dbStats.NewConnsCount()) > 40 { // Assuming 50 is the max for this example
|
|
stats["message"] = "The database is experiencing heavy load."
|
|
}
|
|
|
|
if dbStats.AcquireCount() > 1000 {
|
|
stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks."
|
|
}
|
|
|
|
if dbStats.MaxIdleDestroyCount() > int64(dbStats.NewConnsCount())/2 {
|
|
stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings."
|
|
}
|
|
|
|
if dbStats.MaxLifetimeDestroyCount() > int64(dbStats.NewConnsCount())/2 {
|
|
stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern."
|
|
}
|
|
|
|
return stats
|
|
}
|