Added support for profiling. Removed the pkg module altogether. Everything except old sync is now using code generated by sqlc.
200 lines
5.7 KiB
Go
200 lines
5.7 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)
|
|
}
|
|
Testf()
|
|
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 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)
|
|
}
|
|
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
|
|
}
|