Recover DB connection automatically after network returns
This commit is contained in:
@@ -32,14 +32,14 @@ type ConnectionStatus struct {
|
|||||||
|
|
||||||
// ConnectionManager manages database connections with thread-safety and connection pooling
|
// ConnectionManager manages database connections with thread-safety and connection pooling
|
||||||
type ConnectionManager struct {
|
type ConnectionManager struct {
|
||||||
localDB *localdb.LocalDB // for getting DSN from settings
|
localDB *localdb.LocalDB // for getting DSN from settings
|
||||||
mu sync.RWMutex // protects db and state
|
mu sync.RWMutex // protects db and state
|
||||||
db *gorm.DB // current connection (nil if not connected)
|
db *gorm.DB // current connection (nil if not connected)
|
||||||
lastError error // last connection error
|
lastError error // last connection error
|
||||||
lastCheck time.Time // time of last check/attempt
|
lastCheck time.Time // time of last check/attempt
|
||||||
connectTimeout time.Duration // timeout for connection (default: 5s)
|
connectTimeout time.Duration // timeout for connection (default: 5s)
|
||||||
pingInterval time.Duration // minimum interval between pings (default: 30s)
|
pingInterval time.Duration // minimum interval between pings (default: 30s)
|
||||||
reconnectCooldown time.Duration // pause after failed attempt (default: 10s)
|
reconnectCooldown time.Duration // pause after failed attempt (default: 10s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConnectionManager creates a new ConnectionManager instance
|
// NewConnectionManager creates a new ConnectionManager instance
|
||||||
@@ -94,6 +94,8 @@ func (cm *ConnectionManager) GetDB() (*gorm.DB, error) {
|
|||||||
// Attempt to connect
|
// Attempt to connect
|
||||||
err := cm.connect()
|
err := cm.connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Drop stale handle so callers don't treat it as an active connection.
|
||||||
|
cm.db = nil
|
||||||
cm.lastError = err
|
cm.lastError = err
|
||||||
cm.lastCheck = time.Now()
|
cm.lastCheck = time.Now()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -147,23 +149,27 @@ func (cm *ConnectionManager) connect() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOnline checks if the database is currently connected and responsive
|
// IsOnline checks if the database is currently connected and responsive.
|
||||||
// Does not attempt to reconnect, only checks current state with caching
|
// If disconnected, it tries to reconnect (respecting cooldowns in GetDB).
|
||||||
func (cm *ConnectionManager) IsOnline() bool {
|
func (cm *ConnectionManager) IsOnline() bool {
|
||||||
cm.mu.RLock()
|
cm.mu.RLock()
|
||||||
if cm.db == nil {
|
isDisconnected := cm.db == nil
|
||||||
cm.mu.RUnlock()
|
lastErr := cm.lastError
|
||||||
return false
|
checkedRecently := time.Since(cm.lastCheck) < cm.pingInterval
|
||||||
}
|
|
||||||
|
|
||||||
// If we've checked recently, return cached result
|
|
||||||
if time.Since(cm.lastCheck) < cm.pingInterval {
|
|
||||||
cm.mu.RUnlock()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
cm.mu.RUnlock()
|
cm.mu.RUnlock()
|
||||||
|
|
||||||
// Need to perform actual ping
|
// Try reconnect in disconnected state.
|
||||||
|
if isDisconnected {
|
||||||
|
_, err := cm.GetDB()
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've checked recently, return cached result.
|
||||||
|
if checkedRecently {
|
||||||
|
return lastErr == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to perform actual ping.
|
||||||
cm.mu.Lock()
|
cm.mu.Lock()
|
||||||
defer cm.mu.Unlock()
|
defer cm.mu.Unlock()
|
||||||
|
|
||||||
@@ -282,7 +288,7 @@ func extractHostFromDSN(dsn string) string {
|
|||||||
}
|
}
|
||||||
if parenEnd != -1 {
|
if parenEnd != -1 {
|
||||||
// Extract host:port part between tcp( and )
|
// Extract host:port part between tcp( and )
|
||||||
hostPort := dsn[tcpStart+1:parenEnd]
|
hostPort := dsn[tcpStart+1 : parenEnd]
|
||||||
return hostPort
|
return hostPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,7 +323,7 @@ func extractHostFromDSN(dsn string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if parenEnd != -1 {
|
if parenEnd != -1 {
|
||||||
hostPort := dsn[parenStart+1:parenEnd]
|
hostPort := dsn[parenStart+1 : parenEnd]
|
||||||
return hostPort
|
return hostPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,4 +331,4 @@ func extractHostFromDSN(dsn string) string {
|
|||||||
|
|
||||||
// If we can't parse it, return empty string
|
// If we can't parse it, return empty string
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user