Skip to content

feat: add prometheus metrics to database.Store #7713

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

Merged
merged 11 commits into from
May 31, 2023
Merged

feat: add prometheus metrics to database.Store #7713

merged 11 commits into from
May 31, 2023

Conversation

johnstcn
Copy link
Member

@johnstcn johnstcn commented May 30, 2023

This PR adds a dbmetrics package and wraps database.Store with a Prometheus HistogramVec of timings.

The interface is manually written for now; the implementation is simple enough to not really require the overhead of generation.

Update: to fix the issue of potentially double-wrapping the database.Store, added a Wrappers() method to allow us to detect what wrappers are in use.

@johnstcn johnstcn self-assigned this May 30, 2023
@Emyrk
Copy link
Member

Emyrk commented May 30, 2023

I see value in this.

Only comment is to maybe wrap the db like we do with dbauthz:

coder/coderd/coderd.go

Lines 188 to 192 in 702c908

options.Database = dbauthz.New(
options.Database,
options.Authorizer,
options.Logger.Named("authz_querier"),
)

Make it so it's always wrapped.

@johnstcn johnstcn marked this pull request as ready for review May 30, 2023 13:28
@johnstcn johnstcn requested review from mafredri, Emyrk and mtojek May 30, 2023 13:28
@johnstcn
Copy link
Member Author

Make it so it's always wrapped.

In order to fix this properly, I ended up adding a Wrappers() []string method on database.Store so that we can avoid the case where

  • We wrap with A
  • We wrap with B
  • We wrap again with A

@johnstcn johnstcn requested a review from mafredri May 31, 2023 12:04
Comment on lines +180 to +184
// Safety check: if we're not running a unit test, we *must* have a Prometheus registry.
if options.PrometheusRegistry == nil && flag.Lookup("test.v") == nil {
panic("developer error: options.PrometheusRegistry is nil and not running a unit test")
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: I'm open to removing this, but I think it's a good check to have. We don't want to accidentally publish a release with no prometheus metrics.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always do this? Seems wasteful if --prometheus-enable=false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do. It is a little wasteful, but less error-prone than having to check if the prometheus registerer is nil everywhere.

@@ -52,7 +55,11 @@ type MethodTestSuite struct {
// SetupSuite sets up the suite by creating a map of all methods on querier
// and setting their count to 0.
func (s *MethodTestSuite) SetupSuite() {
az := dbauthz.New(nil, nil, slog.Make())
ctrl := gomock.NewController(s.T())
mockStore := dbmock.NewMockStore(ctrl)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Emyrk I think it makes sense to use the mock here instead of nil, as it will allow us to immediately see if we accidentally call methods of the underlying database.Store.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would just panic if the db is set to nil, which would tell us a call was made. Does the mock panic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see though, for the wrappers method.

@@ -158,7 +159,13 @@ func TestLicensesListFake(t *testing.T) {
assert.Equal(t, "claim1", licenses[0].Claims["h1"])
assert.Equal(t, int32(5), licenses[1].ID)
assert.Equal(t, "claim2", licenses[1].Claims["h2"])
assert.Equal(t, "2024-04-06T16:53:35Z", licenses[0].Claims["license_expires"])
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: this unit test breaks if your timezone is different 😛

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an underlying bug we should fix instead or working as expected?

Copy link
Member Author

@johnstcn johnstcn May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think showing the expiry in the user's local timezone is probably fine.
EDIT: we used to show the expiry as a UNIX timestamp; it was changed to show RFC3339 in #7687. I think showing with local timezone is in line with this change.

Comment on lines +180 to +184
// Safety check: if we're not running a unit test, we *must* have a Prometheus registry.
if options.PrometheusRegistry == nil && flag.Lookup("test.v") == nil {
panic("developer error: options.PrometheusRegistry is nil and not running a unit test")
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always do this? Seems wasteful if --prometheus-enable=false.

@@ -158,7 +159,13 @@ func TestLicensesListFake(t *testing.T) {
assert.Equal(t, "claim1", licenses[0].Claims["h1"])
assert.Equal(t, int32(5), licenses[1].ID)
assert.Equal(t, "claim2", licenses[1].Claims["h2"])
assert.Equal(t, "2024-04-06T16:53:35Z", licenses[0].Claims["license_expires"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an underlying bug we should fix instead or working as expected?

@Emyrk
Copy link
Member

Emyrk commented May 31, 2023

I was thinking about that double wrapping yesterday. Can we use the same style as errors and just implement an "unwrap" method? Then we can recursively unwrap to check it it's wrapped with something

@mafredri
Copy link
Member

I was thinking about that double wrapping yesterday. Can we use the same style as errors and just implement an "unwrap" method? Then we can recursively unwrap to check it it's wrapped with something

I thought about this too, and both approaches are solid IMO. It's perhaps a philosophical question, do we want to enable the ability to unwrap, e.g. escaping dbauthz?

@johnstcn
Copy link
Member Author

I was thinking about that double wrapping yesterday. Can we use the same style as errors and just implement an "unwrap" method? Then we can recursively unwrap to check it it's wrapped with something

I thought about this too, and both approaches are solid IMO. It's perhaps a philosophical question, do we want to enable the ability to unwrap, e.g. escaping dbauthz?

I'd be against it for exactly that reason! 🙃

@Emyrk
Copy link
Member

Emyrk commented May 31, 2023

Hmm good point. For unit testing unwrapping could be helpful. But I understand why you choose against it.

@johnstcn
Copy link
Member Author

Hmm good point. For unit testing unwrapping could be helpful. But I understand why you choose against it.

True, but in a unit test ideally you'd just pass in the database.Store you want with or without the wrappers you need.

@@ -52,7 +55,11 @@ type MethodTestSuite struct {
// SetupSuite sets up the suite by creating a map of all methods on querier
// and setting their count to 0.
func (s *MethodTestSuite) SetupSuite() {
az := dbauthz.New(nil, nil, slog.Make())
ctrl := gomock.NewController(s.T())
mockStore := dbmock.NewMockStore(ctrl)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see though, for the wrappers method.

@johnstcn johnstcn merged commit 784696d into main May 31, 2023
@johnstcn johnstcn deleted the cj/db-prom branch May 31, 2023 13:55
@github-actions github-actions bot locked and limited conversation to collaborators May 31, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants