-
Notifications
You must be signed in to change notification settings - Fork 29
Enable beholder metrics for sqlutil #2353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package sqlutil | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync" | ||
|
|
||
| "go.opentelemetry.io/otel/metric" | ||
|
|
||
| "github.com/smartcontractkit/chainlink-common/pkg/beholder" | ||
| "github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
| ) | ||
|
|
||
| // sqlQueryTimeMetric records SQL query time as a percentage of timeout, duplicating [PromSQLQueryTime] | ||
| // as an OTel histogram via the Beholder meter. | ||
| type sqlQueryTimeMetric interface { | ||
| Record(ctx context.Context, pct float64) | ||
| } | ||
|
|
||
| type beholderSQLQueryTimeMetric struct { | ||
| histogram metric.Float64Histogram | ||
| } | ||
|
|
||
| func newSQLQueryTimeMetric(lggr logger.Logger) sqlQueryTimeMetric { | ||
| histogram, err := beholder.GetMeter().Float64Histogram( | ||
| "sql_query_timeout_percent", | ||
| metric.WithDescription("SQL query time as a percentage of timeout."), | ||
| metric.WithUnit("1"), | ||
| metric.WithExplicitBucketBoundaries(sqlQueryTimeBuckets...), | ||
| ) | ||
| if err != nil { | ||
| lggr.Errorw("Failed to create sql_query_timeout_percent beholder histogram; disabling beholder SQL query time metric", "err", err) | ||
| return noopSQLQueryTimeMetric{} | ||
| } | ||
| return &beholderSQLQueryTimeMetric{histogram: histogram} | ||
| } | ||
|
|
||
| func (m *beholderSQLQueryTimeMetric) Record(ctx context.Context, pct float64) { | ||
| m.histogram.Record(ctx, pct) | ||
| } | ||
|
|
||
| type noopSQLQueryTimeMetric struct{} | ||
|
|
||
| func (noopSQLQueryTimeMetric) Record(context.Context, float64) {} | ||
|
|
||
| var ( | ||
| sqlQueryTimeMetricOnce sync.Once | ||
| globalSQLQueryTimeMetric sqlQueryTimeMetric | ||
|
Comment on lines
+46
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we strictly need a global instance? or could we inject something to use or construct from instead? We don't typically need to suppress errors for metric creation. |
||
| ) | ||
|
|
||
| func getSQLQueryTimeMetric(lggr logger.Logger) sqlQueryTimeMetric { | ||
| sqlQueryTimeMetricOnce.Do(func() { | ||
| globalSQLQueryTimeMetric = newSQLQueryTimeMetric(lggr) | ||
| }) | ||
| return globalSQLQueryTimeMetric | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package pg | ||
|
|
||
| import ( | ||
| "context" | ||
| "database/sql" | ||
|
|
||
| "go.opentelemetry.io/otel/metric" | ||
| "go.opentelemetry.io/otel/metric/noop" | ||
|
|
||
| "github.com/smartcontractkit/chainlink-common/pkg/beholder" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this package should import beholder. We already provide a means of setting a custom hook via |
||
| "github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
| ) | ||
|
|
||
| type dbStatsBeholderMetrics struct { | ||
| connsMax metric.Int64Gauge | ||
| connsOpen metric.Int64Gauge | ||
| connsInUse metric.Int64Gauge | ||
| waitCount metric.Int64Gauge | ||
| waitDuration metric.Float64Gauge | ||
| } | ||
|
|
||
| func newDBStatsBeholderMetrics(lggr logger.Logger) *dbStatsBeholderMetrics { | ||
| meter := beholder.GetMeter() | ||
| m := &dbStatsBeholderMetrics{ | ||
| connsMax: noop.Int64Gauge{}, | ||
| connsOpen: noop.Int64Gauge{}, | ||
| connsInUse: noop.Int64Gauge{}, | ||
| waitCount: noop.Int64Gauge{}, | ||
| waitDuration: noop.Float64Gauge{}, | ||
| } | ||
|
|
||
| if g, err := meter.Int64Gauge("db_conns_max", | ||
| metric.WithDescription("Maximum number of open connections to the database.")); err != nil { | ||
| lggr.Errorw("Failed to create db_conns_max beholder gauge", "err", err) | ||
| } else { | ||
| m.connsMax = g | ||
| } | ||
|
|
||
| if g, err := meter.Int64Gauge("db_conns_open", | ||
| metric.WithDescription("The number of established connections both in use and idle.")); err != nil { | ||
| lggr.Errorw("Failed to create db_conns_open beholder gauge", "err", err) | ||
| } else { | ||
| m.connsOpen = g | ||
| } | ||
|
|
||
| if g, err := meter.Int64Gauge("db_conns_used", | ||
| metric.WithDescription("The number of connections currently in use.")); err != nil { | ||
| lggr.Errorw("Failed to create db_conns_used beholder gauge", "err", err) | ||
| } else { | ||
| m.connsInUse = g | ||
| } | ||
|
|
||
| if g, err := meter.Int64Gauge("db_wait_count", | ||
| metric.WithDescription("The total number of connections waited for.")); err != nil { | ||
| lggr.Errorw("Failed to create db_wait_count beholder gauge", "err", err) | ||
| } else { | ||
| m.waitCount = g | ||
| } | ||
|
|
||
| if g, err := meter.Float64Gauge("db_wait_time_seconds", | ||
| metric.WithDescription("The total time blocked waiting for a new connection."), | ||
| metric.WithUnit("s")); err != nil { | ||
| lggr.Errorw("Failed to create db_wait_time_seconds beholder gauge", "err", err) | ||
| } else { | ||
| m.waitDuration = g | ||
| } | ||
|
|
||
| return m | ||
| } | ||
|
|
||
| func (m *dbStatsBeholderMetrics) record(ctx context.Context, stats sql.DBStats) { | ||
| m.connsMax.Record(ctx, int64(stats.MaxOpenConnections)) | ||
| m.connsOpen.Record(ctx, int64(stats.OpenConnections)) | ||
| m.connsInUse.Record(ctx, int64(stats.InUse)) | ||
| m.waitCount.Record(ctx, stats.WaitCount) | ||
| m.waitDuration.Record(ctx, stats.WaitDuration.Seconds()) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.