Feat: db reconnect

This commit is contained in:
Grail Finder
2025-07-03 10:39:14 +03:00
parent e02554b181
commit c82439d43a
4 changed files with 71 additions and 10 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ golias
gralias
store.json
config.toml
gralias.db

Binary file not shown.

View File

@ -75,6 +75,7 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
abortWithError(w, msg)
return
}
var makeplayer bool
roomID := r.PostFormValue("room_id")
// make sure username does not exists
cleanName := utils.RemoveSpacesFromStr(username)
@ -90,7 +91,9 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
// userstate, err := loadState(cleanName)
userstate, err := repo.PlayerGetByName(r.Context(), cleanName)
if err != nil || userstate == nil {
userstate = models.InitPlayer(cleanName)
makeplayer = true
}
fi := &models.FullInfo{
State: userstate,
@ -125,11 +128,13 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
}
// save state to cache
// if err := saveState(cleanName, userstate); err != nil {
if err := repo.PlayerUpdate(r.Context(), userstate); err != nil {
// if err := saveFullInfo(r.Context(), fi); err != nil {
log.Error("failed to save state", "error", err)
abortWithError(w, err.Error())
return
if makeplayer {
if err := repo.PlayerAdd(r.Context(), userstate); err != nil {
// if err := saveFullInfo(r.Context(), fi); err != nil {
log.Error("failed to save state", "error", err)
abortWithError(w, err.Error())
return
}
}
}
// if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {

View File

@ -4,6 +4,8 @@ import (
"context"
"log/slog"
"os"
"sync"
"time"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
@ -17,7 +19,9 @@ type AllRepos interface {
}
type RepoProvider struct {
DB *sqlx.DB
DB *sqlx.DB
mu sync.RWMutex
pathToDB string
}
func NewRepoProvider(pathToDB string) *RepoProvider {
@ -27,14 +31,65 @@ func NewRepoProvider(pathToDB string) *RepoProvider {
os.Exit(1)
}
slog.Info("Successfully connected to database")
return &RepoProvider{
DB: db,
rp := &RepoProvider{
DB: db,
pathToDB: pathToDB,
}
go rp.pingLoop()
return rp
}
func (rp *RepoProvider) pingLoop() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
if err := rp.pingDB(); err != nil {
slog.Error("Database ping failed, attempting to reconnect...", "error", err)
rp.reconnect()
}
}
}
func (rp *RepoProvider) pingDB() error {
rp.mu.RLock()
defer rp.mu.RUnlock()
if rp.DB == nil {
return os.ErrClosed
}
return rp.DB.Ping()
}
func (rp *RepoProvider) reconnect() {
rp.mu.Lock()
defer rp.mu.Unlock()
// Double-check if connection is still down
if rp.DB != nil {
if err := rp.DB.Ping(); err == nil {
slog.Info("Database connection already re-established.")
return
}
// if ping fails, we continue to reconnect
rp.DB.Close() // close old connection
}
slog.Info("Reconnecting to database...")
db, err := sqlx.Connect("sqlite3", rp.pathToDB)
if err != nil {
slog.Error("Failed to reconnect to database", "error", err)
rp.DB = nil // make sure DB is nil if connection failed
return
}
rp.DB = db
slog.Info("Successfully reconnected to database")
}
func getDB(ctx context.Context, db *sqlx.DB) sqlx.ExtContext {
if tx, ok := ctx.Value("tx").(*sqlx.Tx);
ok {
if tx, ok := ctx.Value("tx").(*sqlx.Tx); ok {
return tx
}
return db