Feat: db reconnect
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder