Feat: db reconnect
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ golias
|
|||||||
gralias
|
gralias
|
||||||
store.json
|
store.json
|
||||||
config.toml
|
config.toml
|
||||||
|
gralias.db
|
||||||
|
BIN
gralias.db
BIN
gralias.db
Binary file not shown.
@ -75,6 +75,7 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
abortWithError(w, msg)
|
abortWithError(w, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var makeplayer bool
|
||||||
roomID := r.PostFormValue("room_id")
|
roomID := r.PostFormValue("room_id")
|
||||||
// make sure username does not exists
|
// make sure username does not exists
|
||||||
cleanName := utils.RemoveSpacesFromStr(username)
|
cleanName := utils.RemoveSpacesFromStr(username)
|
||||||
@ -90,7 +91,9 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
// userstate, err := loadState(cleanName)
|
// userstate, err := loadState(cleanName)
|
||||||
userstate, err := repo.PlayerGetByName(r.Context(), cleanName)
|
userstate, err := repo.PlayerGetByName(r.Context(), cleanName)
|
||||||
if err != nil || userstate == nil {
|
if err != nil || userstate == nil {
|
||||||
|
|
||||||
userstate = models.InitPlayer(cleanName)
|
userstate = models.InitPlayer(cleanName)
|
||||||
|
makeplayer = true
|
||||||
}
|
}
|
||||||
fi := &models.FullInfo{
|
fi := &models.FullInfo{
|
||||||
State: userstate,
|
State: userstate,
|
||||||
@ -125,11 +128,13 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
// save state to cache
|
// save state to cache
|
||||||
// if err := saveState(cleanName, userstate); err != nil {
|
// if err := saveState(cleanName, userstate); err != nil {
|
||||||
if err := repo.PlayerUpdate(r.Context(), userstate); err != nil {
|
if makeplayer {
|
||||||
// if err := saveFullInfo(r.Context(), fi); err != nil {
|
if err := repo.PlayerAdd(r.Context(), userstate); err != nil {
|
||||||
log.Error("failed to save state", "error", err)
|
// if err := saveFullInfo(r.Context(), fi); err != nil {
|
||||||
abortWithError(w, err.Error())
|
log.Error("failed to save state", "error", err)
|
||||||
return
|
abortWithError(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
// if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
@ -17,7 +19,9 @@ type AllRepos interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RepoProvider struct {
|
type RepoProvider struct {
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
|
mu sync.RWMutex
|
||||||
|
pathToDB string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRepoProvider(pathToDB string) *RepoProvider {
|
func NewRepoProvider(pathToDB string) *RepoProvider {
|
||||||
@ -27,14 +31,65 @@ func NewRepoProvider(pathToDB string) *RepoProvider {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
slog.Info("Successfully connected to database")
|
slog.Info("Successfully connected to database")
|
||||||
return &RepoProvider{
|
rp := &RepoProvider{
|
||||||
DB: db,
|
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 {
|
func getDB(ctx context.Context, db *sqlx.DB) sqlx.ExtContext {
|
||||||
if tx, ok := ctx.Value("tx").(*sqlx.Tx);
|
if tx, ok := ctx.Value("tx").(*sqlx.Tx); ok {
|
||||||
ok {
|
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
return db
|
return db
|
||||||
|
Reference in New Issue
Block a user