-
Notifications
You must be signed in to change notification settings - Fork 41
PMM-12392 Collect processlist from performance_schema on newer MySQL #359
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 9 commits
0124cb9
464f8eb
1655e5f
85eb22a
0bc9b36
3da171e
6d42a25
8d4a437
20e2d38
a502077
81d38ae
007f3fe
19caa77
b3137e1
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -22,18 +22,31 @@ import ( | |||||
| "strings" | ||||||
|
|
||||||
| "github.com/alecthomas/kingpin/v2" | ||||||
| "github.com/blang/semver/v4" | ||||||
| "github.com/prometheus/client_golang/prometheus" | ||||||
| ) | ||||||
|
|
||||||
| const ( | ||||||
| processlistInfoSchema = "information_schema" | ||||||
| processlistPerfSchema = "performance_schema" | ||||||
| ) | ||||||
|
|
||||||
| const pInfoSchemaProcesslistQuery = ` | ||||||
| SELECT COALESCE(command,''),COALESCE(state,''),count(*),sum(time) | ||||||
| FROM information_schema.processlist | ||||||
| FROM %s.processlist | ||||||
| WHERE ID != connection_id() | ||||||
| AND TIME >= %d | ||||||
| GROUP BY command,state | ||||||
| ORDER BY null | ||||||
| ` | ||||||
|
|
||||||
| // MySQL version boundaries for querying perf schema | ||||||
| var ( | ||||||
| v8_0_22 = semver.MustParse("8.0.22") | ||||||
| v5_7_39 = semver.MustParse("5.7.39") | ||||||
| v8_0_0 = semver.MustParse("8.0.0") | ||||||
| ) | ||||||
|
|
||||||
| // Tunable flags. | ||||||
| var ( | ||||||
| pProcesslistMinTime = kingpin.Flag( | ||||||
|
|
@@ -185,10 +198,19 @@ func (PScrapeProcesslist) Version() float64 { | |||||
|
|
||||||
| // Scrape collects data from database connection and sends it over channel as prometheus metric. | ||||||
| func (PScrapeProcesslist) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger *slog.Logger) error { | ||||||
| processQuery := fmt.Sprintf( | ||||||
| pInfoSchemaProcesslistQuery, | ||||||
| *pProcesslistMinTime, | ||||||
| ) | ||||||
| // Prefer querying performance_schema.processlist instead of information_schema.processlist to avoid negative perf consequences | ||||||
| // Supported by MySQL >=5.7.39 and >=8.0.22 | ||||||
|
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.
Suggested change
Author
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.
To me, this reads like MySQL is supported from 8.0.22, but not from 5.7.39. Since Percona server and MySQL are both aligned version-for-version in regards to P_S.processlist support, perhaps this reads better:
|
||||||
| usePerfSchema := instance.flavor == FlavorMySQL && | ||||||
| instance.isPerformanceSchemaEnabled && | ||||||
| (instance.version.GTE(v8_0_22) || | ||||||
| (instance.version.GTE(v5_7_39) && instance.version.LT(v8_0_0))) | ||||||
|
maxkondr marked this conversation as resolved.
|
||||||
|
|
||||||
| schema := processlistInfoSchema | ||||||
| if usePerfSchema { | ||||||
| schema = processlistPerfSchema | ||||||
| } | ||||||
|
|
||||||
| processQuery := fmt.Sprintf(pInfoSchemaProcesslistQuery, schema, *pProcesslistMinTime) | ||||||
| db := instance.getDB() | ||||||
| processlistRows, err := db.QueryContext(ctx, processQuery) | ||||||
| if err != nil { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // Copyright 2018 The Prometheus Authors, 2023 Percona LLC | ||
| // 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" | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| "github.com/DATA-DOG/go-sqlmock" | ||
| "github.com/alecthomas/kingpin/v2" | ||
| "github.com/blang/semver/v4" | ||
| "github.com/prometheus/client_golang/prometheus" | ||
| "github.com/prometheus/common/promslog" | ||
| ) | ||
|
|
||
| func TestPScrapeProcesslistQuerySelection(t *testing.T) { | ||
| if _, err := kingpin.CommandLine.Parse([]string{}); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| cases := []struct { | ||
| name string | ||
| flavor string | ||
| version semver.Version | ||
| perfSchemaEnabled bool | ||
| expectedSchema string | ||
| }{ | ||
| {"MySQL 8.0.22 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("8.0.22"), true, processlistPerfSchema}, | ||
| {"MySQL 8.0.30 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("8.0.30"), true, processlistPerfSchema}, | ||
| {"MySQL 5.7.39 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("5.7.39"), true, processlistPerfSchema}, | ||
| {"MySQL 8.0.22 + PS off -> info_schema", FlavorMySQL, semver.MustParse("8.0.22"), false, processlistInfoSchema}, | ||
| {"MySQL 5.7.39 + PS off -> info_schema", FlavorMySQL, semver.MustParse("5.7.39"), false, processlistInfoSchema}, | ||
| {"MySQL 8.0.21 -> info_schema", FlavorMySQL, semver.MustParse("8.0.21"), true, processlistInfoSchema}, | ||
| {"MySQL 5.7.38 -> info_schema", FlavorMySQL, semver.MustParse("5.7.38"), true, processlistInfoSchema}, | ||
| {"MySQL 8.0.0 -> info_schema", FlavorMySQL, semver.MustParse("8.0.0"), true, processlistInfoSchema}, | ||
| {"MySQL 5.6.50 -> info_schema", FlavorMySQL, semver.MustParse("5.6.50"), true, processlistInfoSchema}, | ||
| {"MariaDB 10.11.0 -> info_schema", FlavorMariaDB, semver.MustParse("10.11.0"), true, processlistInfoSchema}, | ||
| } | ||
|
|
||
| for _, tc := range cases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| db, mock, err := sqlmock.New() | ||
| if err != nil { | ||
| t.Fatalf("error opening a stub database connection: %s", err) | ||
| } | ||
| defer db.Close() | ||
|
|
||
| inst := &instance{ | ||
| db: db, | ||
| flavor: tc.flavor, | ||
| version: tc.version, | ||
| isPerformanceSchemaEnabled: tc.perfSchemaEnabled, | ||
| } | ||
|
|
||
| expectedSQL := fmt.Sprintf(pInfoSchemaProcesslistQuery, tc.expectedSchema, 0) | ||
| columns := []string{"command", "state", "count", "time"} | ||
| mock.ExpectQuery(sanitizeQuery(expectedSQL)). | ||
| WillReturnRows(sqlmock.NewRows(columns)) | ||
|
|
||
| ch := make(chan prometheus.Metric) | ||
| go func() { | ||
| if err := (PScrapeProcesslist{}).Scrape(context.Background(), inst, ch, promslog.NewNopLogger()); err != nil { | ||
|
4nte marked this conversation as resolved.
Outdated
|
||
| t.Errorf("error calling Scrape: %s", err) | ||
| } | ||
| close(ch) | ||
| }() | ||
| for range ch { | ||
| } | ||
|
|
||
| if err := mock.ExpectationsWereMet(); err != nil { | ||
| t.Errorf("unfulfilled expectations: %s", err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.