From c82439d43a53b9433c63133c9bfe36eaf8385d75 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Thu, 3 Jul 2025 10:39:14 +0300 Subject: [PATCH] Feat: db reconnect --- .gitignore | 1 + gralias.db | Bin 57344 -> 0 bytes handlers/auth.go | 15 +++++++---- repos/main.go | 65 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 10 deletions(-) delete mode 100644 gralias.db diff --git a/.gitignore b/.gitignore index f8e7284..145574a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ golias gralias store.json config.toml +gralias.db diff --git a/gralias.db b/gralias.db deleted file mode 100644 index d8916876f36b403c49681c9a70baa744e1f25102..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57344 zcmeI*T~FIq7zc2>p$Q4l>}tNKm`-mNBA}S2Z$p~2lp2H<5*mWkO6x3>oTe6zP5lBz zlO~m}mG%MlX|}Jh53-A0^>Vw|<70>5q~>LDc{(&xW_%ubZoD`o>^^b z)t1Iu)dw|=^;72xj25%QHtH>HU2C$f=Ei2V`GkF|Jz>?IR%4@1N~X>d#J5dcWN!Ry3=e@5&a}fRM;|~ao_Cmlaq}UtlU-A zD6jAAaa-TxN35kiro#`PqEuo#^^He6+5%f#WZZ5$9o}IFh86JWLOt%<2AwNWJUaGT zqp5AI*F_!Yf;eNDHMKR3E_+qmX8V?L#9eQ&vrt}|$ttU}vSivF{?dD4(Lkmff$xZ) z^}eUR+~2x(Y)?h;Sc>D}`=;Hcctq>zp?E~8?6LuI9jB*L{N7Vd%doqF(H$Z<2~vsM zrN~1ZW8d(+zasEnC8uyopPc4 zx>3j}*RRWO0|9Qd{jmMVtEmKcocJ-d>5z__1dMdW4!!MI!@Ea(qpQ;Wqy3?nMF%=` z+@Uy+93jkdp;OsGZicWtI7PIdY7LZ;!tSXy)KUKTak@hjz||lga{>EFV9= zDu$akB@vr=%7|Cl#4+N;4+@DGSM9*E!pZ%NDXu>)iYz`idda{I_w16(J0(WlyR=LF zyCh1vjx{s@jg;!rSU?kJR5FZORMPbHUC00E!CTB`mFo4hxi{9;m*b7xn|vmxD2n{^ z3lSMHMTl<`iKvLAK}^I9dg}Hk>arg+dx^r1tEb(FgyGS{Sv5UIQ5c^SteAZ*vuFD0 z_fPT7le5olx=Hi<#m+4d#-8Sl!1lR&Zf0sRm&z+Dc;VjPaqKh3(o>LN z+8bqe4ybk)xeyVTL+8A(h#|)^?Y-3S70Ts_tn#&z)^tS^)R)ETDm{BlhR+|pD>KKBtrlK5P$##AOHafKmY;|fB*!>NC40OV-!qO z1p*L&00bZa0SG_<0uX=z1R#(ifam`d7$idg0uX=z1Rwwb2tWV=5P$###z-K1{+Elt zO7y}40SG_<0uX=z1Rwwb2tWV=5P-n{PheHfO50Z|$}RQQokjKDqFQ0<{T21z%AMOc zRZ6?_Rps`rd9|{ls(~7R-rv5EKe_maL@z85fB*y_009U<00Izz Y00bZa0SJ8R0-t9p{SW;={Y!v<0Mu{j^8f$< diff --git a/handlers/auth.go b/handlers/auth.go index 0f6afbc..6f8756e 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -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 { diff --git a/repos/main.go b/repos/main.go index 3d4efb3..30ba722 100644 --- a/repos/main.go +++ b/repos/main.go @@ -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