83 lines
2.9 KiB
Go
83 lines
2.9 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
// PoolConfig contains only driver-independent settings. Connection credentials,
|
|
// TLS, logging and dial behavior belong to the application's Connector.
|
|
// Start from DefaultPoolConfig; zero PingTimeout is invalid.
|
|
type PoolConfig struct {
|
|
MaxOpenConns int `json:"maxOpenConns"`
|
|
MaxIdleConns int `json:"maxIdleConns"`
|
|
ConnMaxLifetime time.Duration `json:"connMaxLifetime"`
|
|
ConnMaxIdleTime time.Duration `json:"connMaxIdleTime"`
|
|
PingTimeout time.Duration `json:"pingTimeout"`
|
|
}
|
|
|
|
func DefaultPoolConfig() PoolConfig {
|
|
return PoolConfig{MaxOpenConns: 10, MaxIdleConns: 5, ConnMaxLifetime: 30 * time.Minute, ConnMaxIdleTime: 5 * time.Minute, PingTimeout: 5 * time.Second}
|
|
}
|
|
|
|
func (c PoolConfig) Validate() error {
|
|
if c.MaxOpenConns < 0 || c.MaxIdleConns < 0 {
|
|
return errors.New("database pool limits cannot be negative")
|
|
}
|
|
if c.MaxOpenConns > 0 && c.MaxIdleConns > c.MaxOpenConns {
|
|
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
|
|
}
|
|
|
|
func (c PoolConfig) config() Config {
|
|
return Config{MaxOpenConns: c.MaxOpenConns, MaxIdleConns: c.MaxIdleConns, ConnMaxLifetime: c.ConnMaxLifetime, ConnMaxIdleTime: c.ConnMaxIdleTime, PingTimeout: c.PingTimeout}
|
|
}
|
|
|
|
// OpenConnector opens an independent pool using the supplied standard Connector.
|
|
// No registered driver name or DSN is required. It applies cfg and completes a
|
|
// startup Ping bounded by ctx and PingTimeout before returning.
|
|
//
|
|
// Validation failures and an already-canceled ctx leave the Connector untouched.
|
|
// Once sql.OpenDB is called, the pool owns its lifecycle: a failed startup closes
|
|
// the pool; on success the caller must Close the returned DB. As in database/sql,
|
|
// this also closes a Connector that implements io.Closer. Use a separate Connector
|
|
// for each pool if it owns resources. Nil ctx means context.Background.
|
|
func OpenConnector(ctx context.Context, connector driver.Connector, cfg PoolConfig) (*DB, error) {
|
|
if nilConnector(connector) {
|
|
return nil, errors.New("database connector is required")
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return initializePool(ctx, sql.OpenDB(connector), cfg.config())
|
|
}
|
|
|
|
func nilConnector(connector driver.Connector) bool {
|
|
if connector == nil {
|
|
return true
|
|
}
|
|
v := reflect.ValueOf(connector)
|
|
switch v.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
|
return v.IsNil()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// PoolConfig returns a copy of the settings used to create this pool.
|
|
func (db *DB) PoolConfig() PoolConfig { return db.config.PoolConfig() }
|