feat(dbmanager): update health check interval and add tests

* Change default health check interval from 30s to 15s.
* Always start background health checks regardless of auto-reconnect setting.
* Add tests for health checker functionality and default configurations.
This commit is contained in:
Hein
2026-01-14 15:04:27 +02:00
parent 7879272dda
commit c75842ebb0
3 changed files with 235 additions and 3 deletions

View File

@@ -128,7 +128,7 @@ func DefaultManagerConfig() ManagerConfig {
RetryAttempts: 3,
RetryDelay: 1 * time.Second,
RetryMaxDelay: 10 * time.Second,
HealthCheckInterval: 30 * time.Second,
HealthCheckInterval: 15 * time.Second,
EnableAutoReconnect: true,
}
}
@@ -161,6 +161,11 @@ func (c *ManagerConfig) ApplyDefaults() {
if c.HealthCheckInterval == 0 {
c.HealthCheckInterval = defaults.HealthCheckInterval
}
// EnableAutoReconnect defaults to true - apply if not explicitly set
// Since this is a boolean, we apply the default unconditionally when it's false
if !c.EnableAutoReconnect {
c.EnableAutoReconnect = defaults.EnableAutoReconnect
}
}
// Validate validates the manager configuration

View File

@@ -219,9 +219,10 @@ func (m *connectionManager) Connect(ctx context.Context) error {
logger.Info("Database connection established: name=%s, type=%s", name, connCfg.Type)
}
// Start background health checks if enabled
if m.config.EnableAutoReconnect && m.config.HealthCheckInterval > 0 {
// Always start background health checks
if m.config.HealthCheckInterval > 0 {
m.startHealthChecker()
logger.Info("Background health checker started: interval=%v", m.config.HealthCheckInterval)
}
logger.Info("Database manager initialized: connections=%d", len(m.connections))

View File

@@ -0,0 +1,226 @@
package dbmanager
import (
"context"
"database/sql"
"testing"
"time"
_ "github.com/mattn/go-sqlite3"
)
func TestBackgroundHealthChecker(t *testing.T) {
// Create a SQLite in-memory database
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
defer db.Close()
// Create manager config with a short health check interval for testing
cfg := ManagerConfig{
DefaultConnection: "test",
Connections: map[string]ConnectionConfig{
"test": {
Name: "test",
Type: DatabaseTypeSQLite,
FilePath: ":memory:",
},
},
HealthCheckInterval: 1 * time.Second, // Short interval for testing
EnableAutoReconnect: true,
}
// Create manager
mgr, err := NewManager(cfg)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
// Connect - this should start the background health checker
ctx := context.Background()
err = mgr.Connect(ctx)
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer mgr.Close()
// Get the connection to verify it's healthy
conn, err := mgr.Get("test")
if err != nil {
t.Fatalf("Failed to get connection: %v", err)
}
// Verify initial health check
err = conn.HealthCheck(ctx)
if err != nil {
t.Errorf("Initial health check failed: %v", err)
}
// Wait for a few health check cycles
time.Sleep(3 * time.Second)
// Get stats to verify the connection is still healthy
stats := conn.Stats()
if stats == nil {
t.Fatal("Expected stats to be returned")
}
if !stats.Connected {
t.Error("Expected connection to still be connected")
}
if stats.HealthCheckStatus == "" {
t.Error("Expected health check status to be set")
}
// Verify the manager has started the health checker
if cm, ok := mgr.(*connectionManager); ok {
if cm.healthTicker == nil {
t.Error("Expected health ticker to be running")
}
}
}
func TestDefaultHealthCheckInterval(t *testing.T) {
// Verify the default health check interval is 15 seconds
defaults := DefaultManagerConfig()
expectedInterval := 15 * time.Second
if defaults.HealthCheckInterval != expectedInterval {
t.Errorf("Expected default health check interval to be %v, got %v",
expectedInterval, defaults.HealthCheckInterval)
}
if !defaults.EnableAutoReconnect {
t.Error("Expected EnableAutoReconnect to be true by default")
}
}
func TestApplyDefaultsEnablesAutoReconnect(t *testing.T) {
// Create a config without setting EnableAutoReconnect
cfg := ManagerConfig{
Connections: map[string]ConnectionConfig{
"test": {
Name: "test",
Type: DatabaseTypeSQLite,
FilePath: ":memory:",
},
},
}
// Verify it's false initially (Go's zero value for bool)
if cfg.EnableAutoReconnect {
t.Error("Expected EnableAutoReconnect to be false before ApplyDefaults")
}
// Apply defaults
cfg.ApplyDefaults()
// Verify it's now true
if !cfg.EnableAutoReconnect {
t.Error("Expected EnableAutoReconnect to be true after ApplyDefaults")
}
// Verify health check interval is also set
if cfg.HealthCheckInterval != 15*time.Second {
t.Errorf("Expected health check interval to be 15s, got %v", cfg.HealthCheckInterval)
}
}
func TestManagerHealthCheck(t *testing.T) {
// Create a SQLite in-memory database
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("Failed to open database: %v", err)
}
defer db.Close()
// Create manager config
cfg := ManagerConfig{
DefaultConnection: "test",
Connections: map[string]ConnectionConfig{
"test": {
Name: "test",
Type: DatabaseTypeSQLite,
FilePath: ":memory:",
},
},
HealthCheckInterval: 15 * time.Second,
EnableAutoReconnect: true,
}
// Create and connect manager
mgr, err := NewManager(cfg)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
ctx := context.Background()
err = mgr.Connect(ctx)
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer mgr.Close()
// Perform health check on all connections
err = mgr.HealthCheck(ctx)
if err != nil {
t.Errorf("Health check failed: %v", err)
}
// Get stats
stats := mgr.Stats()
if stats == nil {
t.Fatal("Expected stats to be returned")
}
if stats.TotalConnections != 1 {
t.Errorf("Expected 1 total connection, got %d", stats.TotalConnections)
}
if stats.HealthyCount != 1 {
t.Errorf("Expected 1 healthy connection, got %d", stats.HealthyCount)
}
if stats.UnhealthyCount != 0 {
t.Errorf("Expected 0 unhealthy connections, got %d", stats.UnhealthyCount)
}
}
func TestManagerStatsAfterClose(t *testing.T) {
cfg := ManagerConfig{
DefaultConnection: "test",
Connections: map[string]ConnectionConfig{
"test": {
Name: "test",
Type: DatabaseTypeSQLite,
FilePath: ":memory:",
},
},
HealthCheckInterval: 15 * time.Second,
}
mgr, err := NewManager(cfg)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
ctx := context.Background()
err = mgr.Connect(ctx)
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
// Close the manager
err = mgr.Close()
if err != nil {
t.Errorf("Failed to close manager: %v", err)
}
// Stats should show no connections
stats := mgr.Stats()
if stats.TotalConnections != 0 {
t.Errorf("Expected 0 total connections after close, got %d", stats.TotalConnections)
}
}