Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,18 @@ func maskDsn(dsn string) string {
// NewExporter creates a new Exporter instance
func NewExporter(logger *slog.Logger, m *MetricsConfiguration) *Exporter {
var databases []*Database
wg := &sync.WaitGroup{}
for dbname, dbconfig := range m.Databases {
databases = append(databases, NewDatabase(logger, dbname, dbconfig))
logger.Info("Initializing database", "database", dbname)
database := NewDatabase(logger, dbname, dbconfig)
databases = append(databases, database)
wg.Add(1)
go func() {
defer wg.Done()
database.WarmupConnectionPool(logger)
}()
}
wg.Wait()
e := &Exporter{
mu: &sync.Mutex{},
duration: prometheus.NewGauge(prometheus.GaugeOpts{
Expand Down
38 changes: 37 additions & 1 deletion collector/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package collector

import (
"context"
"database/sql"
"fmt"
"github.com/godror/godror"
Expand Down Expand Up @@ -64,7 +65,6 @@ func (d *Database) constLabels() map[string]string {

func NewDatabase(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *Database {
db, dbtype := connect(logger, dbname, dbconfig)

return &Database{
Name: dbname,
Up: 0,
Expand All @@ -74,6 +74,42 @@ func NewDatabase(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *D
}
}

// WarmupConnectionPool serially acquires connections to "warm up" the connection pool.
// This is a workaround for a perceived bug in ODPI_C where rapid acquisition of connections
// results in a SIGABRT.
func (d *Database) WarmupConnectionPool(logger *slog.Logger) {
var connections []*sql.Conn
poolSize := d.Config.GetMaxOpenConns()
if poolSize < 1 {
poolSize = d.Config.GetPoolMaxConnections()
}
if poolSize > 100 { // defensively cap poolsize
poolSize = 100
}
warmup := func(i int) {
time.Sleep(100 * time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

conn, err := d.Session.Conn(ctx)
if err != nil {
logger.Debug("Failed to open database connection on warmup", "conn", i, "error", err, "database", d.Name)
return
}
connections = append(connections, conn)
}
for i := 0; i < poolSize; i++ {
warmup(i + 1)
}

logger.Debug("Warmed connection pool", "total", len(connections), "database", d.Name)
for i, conn := range connections {
if err := conn.Close(); err != nil {
logger.Debug("Failed to return database connection to pool on warmup", "conn", i+1, "error", err, "database", d.Name)
}
}
}

func connect(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) (*sql.DB, float64) {
logger.Debug("Launching connection to "+maskDsn(dbconfig.URL), "database", dbname)

Expand Down
9 changes: 1 addition & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,7 @@ func main() {
logger.Error("unable to load metrics configuration", "error", err)
return
}

for dbname, db := range m.Databases {
if db.GetMaxOpenConns() > 0 {
logger.Info(dbname + " database max idle connections is greater than 0, so will use go-sql connection pool and pooling settings will be ignored")
} else {
logger.Info(dbname + " database max idle connections is 0, so will use Oracle connection pool. Tune with database pooling settings")
}
}

exporter := collector.NewExporter(logger, m)
if exporter.ScrapeInterval() != 0 {
ctx, cancel := context.WithCancel(context.Background())
Expand Down