Skip to content

Commit 0f7b6ed

Browse files
committed
cmd,collector: reduce connections during /metrics scrapes
Signed-off-by: Max Englander <[email protected]>
1 parent 2367a8d commit 0f7b6ed

File tree

4 files changed

+72
-38
lines changed

4 files changed

+72
-38
lines changed

cmd/postgres_exporter/main.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,37 @@ func main() {
133133
dsn = dsns[0]
134134
}
135135

136-
pe, err := collector.NewPostgresCollector(
137-
logger,
138-
excludedDatabases,
139-
dsn,
140-
[]string{},
141-
collector.WithTimeout(*scrapeTimeout),
142-
)
143-
if err != nil {
144-
logger.Warn("Failed to create PostgresCollector", "err", err.Error())
145-
} else {
146-
prometheus.MustRegister(pe)
136+
if dsn != "" {
137+
// Get the server connection to share with collectors
138+
server, err := exporter.servers.GetServer(dsn)
139+
if err != nil {
140+
logger.Warn("Failed to get server for collectors", "err", err.Error())
141+
} else {
142+
// Create instance with shared connection from server
143+
instance, err := collector.NewInstance(dsn)
144+
if err != nil {
145+
logger.Warn("Failed to create instance", "err", err.Error())
146+
} else {
147+
err = instance.SetupWithConnection(server.db)
148+
if err != nil {
149+
logger.Warn("Failed to setup shared instance", "err", err.Error())
150+
} else {
151+
// Create collector with shared instance (instancePerCollect defaults to false)
152+
pe, err := collector.NewPostgresCollector(
153+
logger,
154+
excludedDatabases,
155+
instance,
156+
[]string{},
157+
collector.WithTimeout(*scrapeTimeout),
158+
)
159+
if err != nil {
160+
logger.Warn("Failed to create PostgresCollector", "err", err.Error())
161+
} else {
162+
prometheus.MustRegister(pe)
163+
}
164+
}
165+
}
166+
}
147167
}
148168

149169
http.Handle(*metricsPath, promhttp.Handler())

collector/collector.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ type PostgresCollector struct {
9393
logger *slog.Logger
9494
scrapeTimeout time.Duration
9595

96-
instance *instance
96+
instance *instance
97+
instancePerCollect bool
9798
}
9899

99100
type Option func(*PostgresCollector) error
@@ -106,10 +107,19 @@ func WithTimeout(timeout time.Duration) Option {
106107
}
107108
}
108109

110+
// WithInstancePerCollect configures whether to create a new instance per Collect call.
111+
func WithInstancePerCollect() Option {
112+
return func(p *PostgresCollector) error {
113+
p.instancePerCollect = true
114+
return nil
115+
}
116+
}
117+
109118
// NewPostgresCollector creates a new PostgresCollector.
110-
func NewPostgresCollector(logger *slog.Logger, excludeDatabases []string, dsn string, filters []string, options ...Option) (*PostgresCollector, error) {
119+
func NewPostgresCollector(logger *slog.Logger, excludeDatabases []string, instance *instance, filters []string, options ...Option) (*PostgresCollector, error) {
111120
p := &PostgresCollector{
112-
logger: logger,
121+
logger: logger,
122+
instance: instance,
113123
}
114124
// Apply options to customize the collector
115125
for _, o := range options {
@@ -154,16 +164,6 @@ func NewPostgresCollector(logger *slog.Logger, excludeDatabases []string, dsn st
154164

155165
p.Collectors = collectors
156166

157-
if dsn == "" {
158-
return nil, errors.New("empty dsn")
159-
}
160-
161-
instance, err := newInstance(dsn)
162-
if err != nil {
163-
return nil, err
164-
}
165-
p.instance = instance
166-
167167
return p, nil
168168
}
169169

@@ -184,15 +184,21 @@ func (p PostgresCollector) Collect(ch chan<- prometheus.Metric) {
184184
ctx = context.Background()
185185
}
186186

187-
// copy the instance so that concurrent scrapes have independent instances
188-
inst := p.instance.copy()
187+
var inst *instance
189188

190-
// Set up the database connection for the collector.
191-
err := inst.setup()
192-
defer inst.Close()
193-
if err != nil {
194-
p.logger.Error("Error opening connection to database", "err", err)
195-
return
189+
if p.instancePerCollect {
190+
// copy the instance so that concurrent scrapes have independent instances
191+
inst = p.instance.copy()
192+
// Set up the database connection for the collector.
193+
err := inst.setup()
194+
if err != nil {
195+
p.logger.Error("Error opening connection to database", "err", err)
196+
return
197+
}
198+
defer inst.Close()
199+
} else {
200+
// Use the shared instance directly
201+
inst = p.instance
196202
}
197203

198204
wg := sync.WaitGroup{}
@@ -206,10 +212,6 @@ func (p PostgresCollector) Collect(ch chan<- prometheus.Metric) {
206212
wg.Wait()
207213
}
208214

209-
func (p *PostgresCollector) Close() error {
210-
return p.instance.Close()
211-
}
212-
213215
func execute(ctx context.Context, name string, c Collector, instance *instance, ch chan<- prometheus.Metric, logger *slog.Logger) {
214216
begin := time.Now()
215217
err := c.Update(ctx, instance, ch)

collector/instance.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type instance struct {
2727
version semver.Version
2828
}
2929

30-
func newInstance(dsn string) (*instance, error) {
30+
func NewInstance(dsn string) (*instance, error) {
3131
i := &instance{
3232
dsn: dsn,
3333
}
@@ -68,6 +68,18 @@ func (i *instance) setup() error {
6868
return nil
6969
}
7070

71+
// SetupWithConnection sets up the instance with an existing database connection.
72+
func (i *instance) SetupWithConnection(db *sql.DB) error {
73+
i.db = db
74+
75+
version, err := queryVersion(i.db)
76+
if err != nil {
77+
return fmt.Errorf("error querying postgresql version: %w", err)
78+
}
79+
i.version = version
80+
return nil
81+
}
82+
7183
func (i *instance) getDB() *sql.DB {
7284
return i.db
7385
}

collector/probe.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func NewProbeCollector(logger *slog.Logger, excludeDatabases []string, registry
5757
}
5858
}
5959

60-
instance, err := newInstance(dsn.GetConnectionString())
60+
instance, err := NewInstance(dsn.GetConnectionString())
6161
if err != nil {
6262
return nil, err
6363
}

0 commit comments

Comments
 (0)