fix(inputs.mysql): Use correct column-types for Percona 8 userstats (#15012)

This commit is contained in:
Sven Rebhan 2024-03-18 22:03:49 +01:00 committed by GitHub
parent b8936a83cb
commit 13c786bdfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 66 additions and 26 deletions

View File

@ -6,6 +6,7 @@ import (
_ "embed"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
@ -947,7 +948,7 @@ func (m *Mysql) gatherUserStatisticsStatuses(db *sql.DB, servtag string, acc tel
return err
}
read, err := getColSlice(len(cols))
read, err := getColSlice(rows)
if err != nil {
return err
}
@ -995,7 +996,13 @@ func columnsToLower(s []string, e error) ([]string, error) {
}
// getColSlice returns an in interface slice that can be used in the row.Scan().
func getColSlice(l int) ([]interface{}, error) {
func getColSlice(rows *sql.Rows) ([]interface{}, error) {
columnTypes, err := rows.ColumnTypes()
if err != nil {
return nil, err
}
l := len(columnTypes)
// list of all possible column names
var (
user string
@ -1111,30 +1118,26 @@ func getColSlice(l int) ([]interface{}, error) {
&emptyQueries,
}, nil
case 22: // percona
return []interface{}{
&user,
&totalConnections,
&concurrentConnections,
&connectedTime,
&busyTime,
&cpuTime,
&bytesReceived,
&bytesSent,
&binlogBytesWritten,
&rowsFetched,
&rowsUpdated,
&tableRowsRead,
&selectCommands,
&updateCommands,
&otherCommands,
&commitTransactions,
&rollbackTransactions,
&deniedConnections,
&lostConnections,
&accessDenied,
&emptyQueries,
&totalSslConnections,
}, nil
cols := make([]interface{}, 0, 22)
for i, ct := range columnTypes {
// The first column is the user and has to be a string
if i == 0 {
cols = append(cols, new(string))
continue
}
// Percona 8 has some special fields that are float instead of ints
// see: https://github.com/influxdata/telegraf/issues/7360
switch ct.ScanType().Kind() {
case reflect.Float32, reflect.Float64:
cols = append(cols, new(float64))
default:
// Keep old type for backward compatibility
cols = append(cols, new(int64))
}
}
return cols, nil
}
return nil, fmt.Errorf("not Supported - %d columns", l)

View File

@ -104,6 +104,43 @@ func TestMysqlMultipleInstancesIntegration(t *testing.T) {
require.False(t, acc2.HasMeasurement("mysql_variables"))
}
func TestPercona8Integration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
container := testutil.Container{
Image: "percona:8",
Env: map[string]string{
"MYSQL_ROOT_PASSWORD": "secret",
},
Cmd: []string{"--userstat=ON"},
ExposedPorts: []string{servicePort},
WaitingFor: wait.ForAll(
wait.ForLog("/usr/sbin/mysqld: ready for connections").WithOccurrence(2),
wait.ForListeningPort(nat.Port(servicePort)),
),
}
require.NoError(t, container.Start(), "failed to start container")
defer container.Terminate()
dsn := fmt.Sprintf("root:secret@tcp(%s:%s)/", container.Address, container.Ports[servicePort])
s := config.NewSecret([]byte(dsn))
plugin := &Mysql{
Servers: []*config.Secret{&s},
GatherUserStatistics: true,
}
require.NoError(t, plugin.Init())
var acc testutil.Accumulator
require.NoError(t, plugin.Gather(&acc))
require.Empty(t, acc.Errors)
require.True(t, acc.HasMeasurement("mysql_user_stats"))
require.True(t, acc.HasFloatField("mysql_user_stats", "connected_time"))
require.True(t, acc.HasFloatField("mysql_user_stats", "cpu_time"))
require.True(t, acc.HasFloatField("mysql_user_stats", "busy_time"))
}
func TestMysqlGetDSNTag(t *testing.T) {
tests := []struct {
input string