Feat: db reconnect
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,3 +3,4 @@ golias | ||||
| gralias | ||||
| store.json | ||||
| 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) | ||||
| 		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,13 +128,15 @@ 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 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 { | ||||
| 	// 	log.Error("failed to execute base template", "error", err) | ||||
| 	// } | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import ( | ||||
| 	"context" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/jmoiron/sqlx" | ||||
| 	_ "github.com/mattn/go-sqlite3" | ||||
| @@ -18,6 +20,8 @@ type AllRepos interface { | ||||
|  | ||||
| type RepoProvider struct { | ||||
| 	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{ | ||||
| 	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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder