mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-30 12:01:59 +00:00
Implements the fixes from audit/pkg/dbmanager.audit.md. - Stop closing the shared *sql.DB to recover from errors. Adapter factories and the health checker no longer call Reconnect; Reconnect is atomic and operator-only. - Postgres uses a custom driver.Connector: Reconnect retires pooled connections by generation without closing the pool, so held Bun/GORM handles keep working. Verified against a live server restart. - Add TCP keepalive, TCP_USER_TIMEOUT, a bounded reuse ping and statement_timeout as a runtime parameter; drop the 2 min timeout floor. - Health check pings without holding the connection lock. - Listener: single goroutine pair, bounded Close without UNLISTEN, and serialised use of the pgx connection (fixes conn busy and a close race). - Fix Connect/Close/Connect/Close panic, idempotent Connect, dial outside the manager lock, clean up on partial failure. - SQLite: pin :memory: to one connection, pragmas via DSN. - Escape credentials in Postgres/MSSQL/Mongo DSNs; sslmode defaults to prefer. Wire retry settings, publish metrics, fix logger calls. - NewConnectionFromDB: Close is a no-op with a warning (caller owns the pool); Reconnect only pings. - Document correct usage in the README; mark the audit with what was done. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
109 lines
2.9 KiB
Go
109 lines
2.9 KiB
Go
package providers
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
)
|
|
|
|
// Common errors
|
|
var (
|
|
// ErrNotSQLDatabase is returned when attempting SQL operations on a non-SQL database
|
|
ErrNotSQLDatabase = errors.New("not a SQL database")
|
|
|
|
// ErrNotMongoDB is returned when attempting MongoDB operations on a non-MongoDB connection
|
|
ErrNotMongoDB = errors.New("not a MongoDB connection")
|
|
)
|
|
|
|
// ConnectionStats contains statistics about a database connection
|
|
type ConnectionStats struct {
|
|
Name string
|
|
Type string // Database type as string to avoid circular dependency
|
|
Connected bool
|
|
LastHealthCheck time.Time
|
|
HealthCheckStatus string
|
|
|
|
// SQL connection pool stats
|
|
OpenConnections int
|
|
InUse int
|
|
Idle int
|
|
WaitCount int64
|
|
WaitDuration time.Duration
|
|
MaxIdleClosed int64
|
|
MaxLifetimeClosed int64
|
|
}
|
|
|
|
// ConnectionConfig is a minimal interface for configuration
|
|
// The actual implementation is in dbmanager package
|
|
type ConnectionConfig interface {
|
|
BuildDSN() (string, error)
|
|
GetName() string
|
|
GetType() string
|
|
GetHost() string
|
|
GetPort() int
|
|
GetUser() string
|
|
GetPassword() string
|
|
GetDatabase() string
|
|
GetFilePath() string
|
|
GetConnectTimeout() time.Duration
|
|
GetQueryTimeout() time.Duration
|
|
GetEnableLogging() bool
|
|
GetEnableMetrics() bool
|
|
GetMaxOpenConns() *int
|
|
GetMaxIdleConns() *int
|
|
GetConnMaxLifetime() *time.Duration
|
|
GetConnMaxIdleTime() *time.Duration
|
|
GetReadPreference() string
|
|
GetRetryAttempts() int
|
|
GetRetryDelay() time.Duration
|
|
GetRetryMaxDelay() time.Duration
|
|
}
|
|
|
|
// retryPolicy returns the configured retry settings, falling back to defaults.
|
|
func retryPolicy(cfg ConnectionConfig) (attempts int, delay, maxDelay time.Duration) {
|
|
attempts, delay, maxDelay = cfg.GetRetryAttempts(), cfg.GetRetryDelay(), cfg.GetRetryMaxDelay()
|
|
if attempts <= 0 {
|
|
attempts = 3
|
|
}
|
|
if delay <= 0 {
|
|
delay = time.Second
|
|
}
|
|
if maxDelay <= 0 {
|
|
maxDelay = 10 * time.Second
|
|
}
|
|
return
|
|
}
|
|
|
|
// Refresher is implemented by providers that can retire their pooled
|
|
// connections and dial fresh ones without closing the shared *sql.DB, so
|
|
// handles already handed out keep working.
|
|
type Refresher interface {
|
|
Refresh(ctx context.Context) error
|
|
}
|
|
|
|
// Provider creates and manages the underlying database connection
|
|
type Provider interface {
|
|
// Connect establishes the database connection
|
|
Connect(ctx context.Context, cfg ConnectionConfig) error
|
|
|
|
// Close closes the connection
|
|
Close() error
|
|
|
|
// HealthCheck verifies the connection is alive
|
|
HealthCheck(ctx context.Context) error
|
|
|
|
// GetNative returns the native *sql.DB (SQL databases only)
|
|
// Returns an error for non-SQL databases
|
|
GetNative() (*sql.DB, error)
|
|
|
|
// GetMongo returns the MongoDB client (MongoDB only)
|
|
// Returns an error for non-MongoDB databases
|
|
GetMongo() (*mongo.Client, error)
|
|
|
|
// Stats returns connection statistics
|
|
Stats() *ConnectionStats
|
|
}
|