feat: support database connectors and HTTP server configuration
This commit is contained in:
@@ -23,13 +23,14 @@ type Config struct {
|
||||
PingTimeout time.Duration `json:"pingTimeout"`
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
MaxOpenConns: 10,
|
||||
MaxIdleConns: 5,
|
||||
ConnMaxLifetime: 30 * time.Minute,
|
||||
ConnMaxIdleTime: 5 * time.Minute,
|
||||
PingTimeout: 5 * time.Second,
|
||||
func DefaultConfig() Config { return DefaultPoolConfig().config() }
|
||||
|
||||
// PoolConfig returns driver-independent connection pool settings.
|
||||
func (c Config) PoolConfig() PoolConfig {
|
||||
return PoolConfig{
|
||||
MaxOpenConns: c.MaxOpenConns, MaxIdleConns: c.MaxIdleConns,
|
||||
ConnMaxLifetime: c.ConnMaxLifetime, ConnMaxIdleTime: c.ConnMaxIdleTime,
|
||||
PingTimeout: c.PingTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,16 +84,7 @@ func (c Config) Validate() error {
|
||||
if strings.TrimSpace(c.DSN) == "" {
|
||||
return errors.New("database DSN is required")
|
||||
}
|
||||
if c.MaxOpenConns < 0 || c.MaxIdleConns < 0 {
|
||||
return errors.New("database pool limits cannot be negative")
|
||||
}
|
||||
if c.MaxIdleConns > c.MaxOpenConns && c.MaxOpenConns > 0 {
|
||||
return errors.New("max idle connections cannot exceed max open connections")
|
||||
}
|
||||
if c.ConnMaxLifetime < 0 || c.ConnMaxIdleTime < 0 || c.PingTimeout <= 0 {
|
||||
return errors.New("database timeouts are invalid")
|
||||
}
|
||||
return nil
|
||||
return c.PoolConfig().Validate()
|
||||
}
|
||||
|
||||
// DB owns a connection pool and exposes the embedded database/sql methods.
|
||||
@@ -112,23 +104,30 @@ func Open(ctx context.Context, cfg Config) (*DB, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open database: %w", err)
|
||||
}
|
||||
if cfg.MaxOpenConns > 0 {
|
||||
db.SetMaxOpenConns(cfg.MaxOpenConns)
|
||||
}
|
||||
// database/sql uses zero to disable idle connections; do not leave its
|
||||
// default pool size in place when the caller explicitly chooses zero.
|
||||
db.SetMaxIdleConns(cfg.MaxIdleConns)
|
||||
return initializePool(ctx, db, cfg)
|
||||
}
|
||||
|
||||
// initializePool is the shared ownership boundary for both creation paths.
|
||||
// All configuration is validated before creating the sql.DB.
|
||||
func initializePool(ctx context.Context, db *sql.DB, cfg Config) (*DB, error) {
|
||||
db.SetMaxOpenConns(cfg.MaxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.MaxIdleConns) // Zero explicitly disables idle connections.
|
||||
db.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
||||
db.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
|
||||
checkCtx, cancel := context.WithTimeout(ctx, cfg.PingTimeout)
|
||||
defer cancel()
|
||||
if err := db.PingContext(checkCtx); err != nil {
|
||||
_ = db.Close()
|
||||
return nil, fmt.Errorf("ping database: %w", err)
|
||||
pingErr := fmt.Errorf("ping database: %w", err)
|
||||
if closeErr := db.Close(); closeErr != nil {
|
||||
return nil, errors.Join(pingErr, fmt.Errorf("close database after failed ping: %w", closeErr))
|
||||
}
|
||||
return nil, pingErr
|
||||
}
|
||||
return &DB{DB: db, config: cfg}, nil
|
||||
}
|
||||
|
||||
// Config returns the original DSN configuration. Pools opened with a Connector
|
||||
// have empty Driver/DSN fields; their connector cannot be reconstructed from Config.
|
||||
func (db *DB) Config() Config { return db.config }
|
||||
|
||||
func (db *DB) Health(ctx context.Context) error {
|
||||
|
||||
Reference in New Issue
Block a user