-
Notifications
You must be signed in to change notification settings - Fork 776
Gitlab Collector: Autovacuum collector and test #840
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
Changes from 1 commit
c988ac9
eb57d1d
10d5fcf
3aa5b16
655cc41
29f4f4a
581eb0e
50e235b
ae1c588
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,85 @@ | ||
// Copyright 2023 The Prometheus Authors | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package collector | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-kit/log" | ||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
const statActivityAutovacuumSubsystem = "stat_activity_autovacuum" | ||
|
||
func init() { | ||
registerCollector(statActivityAutovacuumSubsystem, defaultDisabled, NewPGStatActivityAutovacuumCollector) | ||
} | ||
|
||
type PGStatActivityAutovacuumCollector struct { | ||
log log.Logger | ||
} | ||
|
||
func NewPGStatActivityAutovacuumCollector(config collectorConfig) (Collector, error) { | ||
return &PGStatActivityAutovacuumCollector{log: config.logger}, nil | ||
} | ||
|
||
var ( | ||
statActivityAutovacuumAgeInSeconds = prometheus.NewDesc( | ||
prometheus.BuildFQName(namespace, statActivityAutovacuumSubsystem, "age_in_seconds"), | ||
"The age of the vacuum process in seconds", | ||
[]string{"relname"}, | ||
prometheus.Labels{}, | ||
) | ||
|
||
statActivityAutovacuumQuery = ` | ||
SELECT | ||
SPLIT_PART(query, '.', 2) AS relname, | ||
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. This seems like it could be fragile. How do we guarantee that this will be the relname? 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 guess because since the query is autogenerated it'll always look something like |
||
EXTRACT(EPOCH FROM (clock_timestamp() - xact_start)) AS age_in_seconds | ||
Sticksman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
FROM | ||
pg_catalog.pg_stat_activity | ||
WHERE | ||
query like 'autovacuum:%' AND | ||
Sticksman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
EXTRACT(EPOCH FROM (clock_timestamp() - xact_start)) > 300 | ||
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 wonder if we should drop this filter and always show active autovacuums. This way users can chose what to do with the age in PromQL. 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. Would work for me |
||
` | ||
) | ||
|
||
func (PGStatActivityAutovacuumCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error { | ||
db := instance.getDB() | ||
rows, err := db.QueryContext(ctx, | ||
statActivityAutovacuumQuery) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
defer rows.Close() | ||
|
||
for rows.Next() { | ||
var relname string | ||
var ageInSeconds float64 | ||
|
||
if err := rows.Scan(&relname, &ageInSeconds); err != nil { | ||
return err | ||
} | ||
|
||
ch <- prometheus.MustNewConstMetric( | ||
statActivityAutovacuumAgeInSeconds, | ||
prometheus.GaugeValue, | ||
ageInSeconds, relname, | ||
) | ||
} | ||
if err := rows.Err(); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2023 The Prometheus Authors | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package collector | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/DATA-DOG/go-sqlmock" | ||
"github.com/prometheus/client_golang/prometheus" | ||
dto "github.com/prometheus/client_model/go" | ||
"github.com/smartystreets/goconvey/convey" | ||
) | ||
|
||
func TestPGStatActivityAutovacuumCollector(t *testing.T) { | ||
db, mock, err := sqlmock.New() | ||
if err != nil { | ||
t.Fatalf("Error opening a stub db connection: %s", err) | ||
} | ||
defer db.Close() | ||
inst := &instance{db: db} | ||
columns := []string{ | ||
"relname", | ||
"timestamp_seconds", | ||
} | ||
rows := sqlmock.NewRows(columns). | ||
AddRow("test", 3600) | ||
|
||
mock.ExpectQuery(sanitizeQuery(statActivityAutovacuumQuery)).WillReturnRows(rows) | ||
|
||
ch := make(chan prometheus.Metric) | ||
go func() { | ||
defer close(ch) | ||
c := PGStatActivityAutovacuumCollector{} | ||
|
||
if err := c.Update(context.Background(), inst, ch); err != nil { | ||
t.Errorf("Error calling PGStatActivityAutovacuumCollector.Update: %s", err) | ||
} | ||
}() | ||
expected := []MetricResult{ | ||
{labels: labelMap{"relname": "test"}, value: 3600, metricType: dto.MetricType_GAUGE}, | ||
} | ||
convey.Convey("Metrics comparison", t, func() { | ||
for _, expect := range expected { | ||
m := readMetric(<-ch) | ||
convey.So(expect, convey.ShouldResemble, m) | ||
} | ||
}) | ||
if err := mock.ExpectationsWereMet(); err != nil { | ||
t.Errorf("there were unfulfilled exceptions: %s", err) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.