Add PostgreSQL support to database package alongside existing MySQL support. Both databases share common infrastructure (pool management, metrics, transactions) while using database-specific connectors. database/ changes: - Add PostgresConfig struct and PostgreSQL connector using pgx/stdlib - Change MySQL config from DBConfig to *MySQLConfig (pointer) - Add Config.Validate() to prevent multiple database configs - Add PostgreSQL connector with secure config building (no password in DSN) - Add field validation and secure defaults (SSLMode="prefer") - Support legacy flat PostgreSQL config format for backward compatibility - Add tests for PostgreSQL configs and validation New database/pgdb/ package: - Native pgx connection pool support (*pgxpool.Pool) - OpenPool() and OpenPoolWithConfigFile() APIs - CreatePoolConfig() for secure config conversion - PoolOptions for fine-grained pool control - Full test coverage and documentation Security: - Passwords never exposed in DSN strings - Set passwords separately in pgx config objects - Validate all configuration before connection Architecture: - Shared code in database/ for both MySQL and PostgreSQL (sql.DB) - database/pgdb/ for PostgreSQL-specific native pool support
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// Config represents the database configuration structure
|
|
type Config struct {
|
|
// MySQL configuration (use this OR Postgres, not both)
|
|
MySQL *MySQLConfig `yaml:"mysql,omitempty"`
|
|
// Postgres configuration (use this OR MySQL, not both)
|
|
Postgres *PostgresConfig `yaml:"postgres,omitempty"`
|
|
|
|
// Legacy flat PostgreSQL format (deprecated, for backward compatibility only)
|
|
// If neither MySQL nor Postgres is set, these fields will be used for PostgreSQL
|
|
User string `yaml:"user,omitempty"`
|
|
Pass string `yaml:"pass,omitempty"`
|
|
Host string `yaml:"host,omitempty"`
|
|
Port uint16 `yaml:"port,omitempty"`
|
|
Name string `yaml:"name,omitempty"`
|
|
SSLMode string `yaml:"sslmode,omitempty"`
|
|
}
|
|
|
|
// MySQLConfig represents the MySQL database configuration
|
|
type MySQLConfig struct {
|
|
DSN string `yaml:"dsn" default:"" flag:"dsn" usage:"Database DSN"`
|
|
User string `yaml:"user" default:"" flag:"user"`
|
|
Pass string `yaml:"pass" default:"" flag:"pass"`
|
|
DBName string `yaml:"name,omitempty"` // Optional database name override
|
|
}
|
|
|
|
// PostgresConfig represents the PostgreSQL database configuration
|
|
type PostgresConfig struct {
|
|
User string `yaml:"user"`
|
|
Pass string `yaml:"pass"`
|
|
Host string `yaml:"host"`
|
|
Port uint16 `yaml:"port"`
|
|
Name string `yaml:"name"`
|
|
SSLMode string `yaml:"sslmode"`
|
|
}
|
|
|
|
// DBConfig is a legacy alias for MySQLConfig
|
|
type DBConfig = MySQLConfig
|
|
|
|
// Validate ensures the configuration is valid and unambiguous
|
|
func (c *Config) Validate() error {
|
|
hasMySQL := c.MySQL != nil
|
|
hasPostgres := c.Postgres != nil
|
|
hasLegacy := c.User != "" || c.Host != "" || c.Port != 0 || c.Name != ""
|
|
|
|
count := 0
|
|
if hasMySQL {
|
|
count++
|
|
}
|
|
if hasPostgres {
|
|
count++
|
|
}
|
|
if hasLegacy {
|
|
count++
|
|
}
|
|
|
|
if count == 0 {
|
|
return fmt.Errorf("no database configuration provided")
|
|
}
|
|
if count > 1 {
|
|
return fmt.Errorf("multiple database configurations provided (only one allowed)")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ConfigOptions allows customization of database opening behavior
|
|
type ConfigOptions struct {
|
|
// ConfigFiles is a list of config file paths to search for database configuration
|
|
ConfigFiles []string
|
|
|
|
// EnablePoolMonitoring enables connection pool metrics collection
|
|
EnablePoolMonitoring bool
|
|
|
|
// PrometheusRegisterer for metrics collection. If nil, no metrics are collected.
|
|
PrometheusRegisterer prometheus.Registerer
|
|
|
|
// Connection pool settings
|
|
MaxOpenConns int
|
|
MaxIdleConns int
|
|
ConnMaxLifetime time.Duration
|
|
}
|
|
|
|
// getConfigFiles returns the list of config files to search for database configuration.
|
|
// If DATABASE_CONFIG_FILE environment variable is set, it returns that single file.
|
|
// Otherwise, it returns the default paths.
|
|
func getConfigFiles() []string {
|
|
if configFile := os.Getenv("DATABASE_CONFIG_FILE"); configFile != "" {
|
|
return []string{configFile}
|
|
}
|
|
return []string{"database.yaml", "/vault/secrets/database.yaml"}
|
|
}
|
|
|
|
// DefaultConfigOptions returns the standard configuration options used by API package
|
|
func DefaultConfigOptions() ConfigOptions {
|
|
return ConfigOptions{
|
|
ConfigFiles: getConfigFiles(),
|
|
EnablePoolMonitoring: true,
|
|
PrometheusRegisterer: prometheus.DefaultRegisterer,
|
|
MaxOpenConns: 25,
|
|
MaxIdleConns: 10,
|
|
ConnMaxLifetime: 3 * time.Minute,
|
|
}
|
|
}
|
|
|
|
// MonitorConfigOptions returns configuration options optimized for Monitor package
|
|
func MonitorConfigOptions() ConfigOptions {
|
|
return ConfigOptions{
|
|
ConfigFiles: getConfigFiles(),
|
|
EnablePoolMonitoring: false, // Monitor doesn't need metrics
|
|
PrometheusRegisterer: nil, // No Prometheus dependency
|
|
MaxOpenConns: 10,
|
|
MaxIdleConns: 5,
|
|
ConnMaxLifetime: 3 * time.Minute,
|
|
}
|
|
}
|