From e6083b50b2e199d40eacfcb07db1ba85a83ac949 Mon Sep 17 00:00:00 2001 From: Adam Boguszewski <108867528+aboguszewski-sumo@users.noreply.github.com> Date: Fri, 23 Sep 2022 15:09:53 +0200 Subject: [PATCH] [receiver/mysql]: add scraping io_waits metrics (#14328) --- receiver/mysqlreceiver/client.go | 79 +++ receiver/mysqlreceiver/documentation.md | 8 + .../internal/metadata/generated_metrics.go | 304 +++++++++++ receiver/mysqlreceiver/metadata.yaml | 49 ++ receiver/mysqlreceiver/scraper.go | 72 +++ receiver/mysqlreceiver/scraper_test.go | 77 ++- .../testdata/integration/expected.5_7.json | 492 ++++++++++++++++++ .../testdata/integration/expected.8_0.json | 492 ++++++++++++++++++ .../testdata/integration/scripts/setup.sh | 16 +- .../testdata/scraper/expected.json | 492 ++++++++++++++++++ .../testdata/scraper/index_io_waits_stats.txt | 1 + .../scraper/index_io_waits_stats_empty.txt | 0 .../testdata/scraper/table_io_waits_stats.txt | 1 + .../scraper/table_io_waits_stats_empty.txt | 0 .../mysql-receiver-add-perf-io-waits.yaml | 5 + 15 files changed, 2081 insertions(+), 7 deletions(-) create mode 100644 receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats.txt create mode 100644 receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats_empty.txt create mode 100644 receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats.txt create mode 100644 receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats_empty.txt create mode 100644 unreleased/mysql-receiver-add-perf-io-waits.yaml diff --git a/receiver/mysqlreceiver/client.go b/receiver/mysqlreceiver/client.go index 72336777e6ad..e53eb2b22a5b 100644 --- a/receiver/mysqlreceiver/client.go +++ b/receiver/mysqlreceiver/client.go @@ -26,6 +26,8 @@ type client interface { Connect() error getGlobalStats() (map[string]string, error) getInnodbStats() (map[string]string, error) + getTableIoWaitsStats() ([]TableIoWaitsStats, error) + getIndexIoWaitsStats() ([]IndexIoWaitsStats, error) Close() error } @@ -34,6 +36,28 @@ type mySQLClient struct { client *sql.DB } +type IoWaitsStats struct { + schema string + name string + countDelete int64 + countFetch int64 + countInsert int64 + countUpdate int64 + timeDelete int64 + timeFetch int64 + timeInsert int64 + timeUpdate int64 +} + +type TableIoWaitsStats struct { + IoWaitsStats +} + +type IndexIoWaitsStats struct { + IoWaitsStats + index string +} + var _ client = (*mySQLClient)(nil) func newMySQLClient(conf *Config) client { @@ -73,6 +97,61 @@ func (c *mySQLClient) getInnodbStats() (map[string]string, error) { return Query(*c, query) } +// getTableIoWaitsStats queries the db for table_io_waits metrics. +func (c *mySQLClient) getTableIoWaitsStats() ([]TableIoWaitsStats, error) { + query := "SELECT OBJECT_SCHEMA, OBJECT_NAME, " + + "COUNT_DELETE, COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE," + + "SUM_TIMER_DELETE, SUM_TIMER_FETCH, SUM_TIMER_INSERT, SUM_TIMER_UPDATE" + + "FROM performance_schema.table_io_waits_summary_by_table" + + "WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema');" + rows, err := c.client.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + var stats []TableIoWaitsStats + for rows.Next() { + var s TableIoWaitsStats + err := rows.Scan(&s.schema, &s.name, + &s.countDelete, &s.countFetch, &s.countInsert, &s.countUpdate, + &s.timeDelete, &s.timeFetch, &s.timeInsert, &s.timeUpdate) + if err != nil { + return nil, err + } + stats = append(stats, s) + } + + return stats, nil +} + +// getIndexIoWaitsStats queries the db for index_io_waits metrics. +func (c *mySQLClient) getIndexIoWaitsStats() ([]IndexIoWaitsStats, error) { + query := "SELECT OBJECT_SCHEMA, OBJECT_NAME, ifnull(INDEX_NAME, 'NONE') as INDEX_NAME," + + "COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE, COUNT_DELETE," + + "SUM_TIMER_FETCH, SUM_TIMER_INSERT, SUM_TIMER_UPDATE, SUM_TIMER_DELETE" + + "FROM performance_schema.table_io_waits_summary_by_index_usage" + + "WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema');" + + rows, err := c.client.Query(query) + if err != nil { + return nil, err + } + defer rows.Close() + var stats []IndexIoWaitsStats + for rows.Next() { + var s IndexIoWaitsStats + err := rows.Scan(&s.schema, &s.name, &s.index, + &s.countDelete, &s.countFetch, &s.countInsert, &s.countUpdate, + &s.timeDelete, &s.timeFetch, &s.timeInsert, &s.timeUpdate) + if err != nil { + return nil, err + } + stats = append(stats, s) + } + + return stats, nil +} + func Query(c mySQLClient, query string) (map[string]string, error) { rows, err := c.client.Query(query) if err != nil { diff --git a/receiver/mysqlreceiver/documentation.md b/receiver/mysqlreceiver/documentation.md index 6b59418bf883..d5d9216cf649 100644 --- a/receiver/mysqlreceiver/documentation.md +++ b/receiver/mysqlreceiver/documentation.md @@ -17,6 +17,8 @@ These are the metrics available for this scraper. | **mysql.commands** | The number of times each type of command has been executed. | 1 | Sum(Int) | | | **mysql.double_writes** | The number of writes to the InnoDB doublewrite buffer. | 1 | Sum(Int) | | | **mysql.handlers** | The number of requests to various MySQL handlers. | 1 | Sum(Int) | | +| **mysql.index.io.wait.count** | The total count of I/O wait events for an index. | 1 | Sum(Int) | | +| **mysql.index.io.wait.time** | The total time of I/O wait events for an index. | ns | Sum(Int) | | | **mysql.locks** | The number of MySQL locks. | 1 | Sum(Int) | | | **mysql.log_operations** | The number of InnoDB log operations. | 1 | Sum(Int) | | | **mysql.operations** | The number of InnoDB operations. | 1 | Sum(Int) | | @@ -24,6 +26,8 @@ These are the metrics available for this scraper. | **mysql.row_locks** | The number of InnoDB row locks. | 1 | Sum(Int) | | | **mysql.row_operations** | The number of InnoDB row operations. | 1 | Sum(Int) | | | **mysql.sorts** | The number of MySQL sorts. | 1 | Sum(Int) | | +| **mysql.table.io.wait.count** | The total count of I/O wait events for a table. | 1 | Sum(Int) | | +| **mysql.table.io.wait.time** | The total time of I/O wait events for a table. | ns | Sum(Int) | | | **mysql.threads** | The state of MySQL threads. | 1 | Sum(Int) | | **Highlighted metrics** are emitted by default. Other metrics are optional and not emitted by default. @@ -51,11 +55,15 @@ metrics: | command (command) | The command types. | execute, close, fetch, prepare, reset, send_long_data | | double_writes (kind) | The doublewrite types. | pages_written, writes | | handler (kind) | The handler types. | commit, delete, discover, external_lock, mrr_init, prepare, read_first, read_key, read_last, read_next, read_prev, read_rnd, read_rnd_next, rollback, savepoint, savepoint_rollback, update, write | +| index_name (index) | The name of the index. | | +| io_waits_operations (operation) | The io_waits operation type. | delete, fetch, insert, update | | locks (kind) | The table locks type. | immediate, waited | | log_operations (operation) | The log operation types. | waits, write_requests, writes | | operations (operation) | The operation types. | fsyncs, reads, writes | | page_operations (operation) | The page operation types. | created, read, written | | row_locks (kind) | The row lock type. | waits, time | | row_operations (operation) | The row operation type. | deleted, inserted, read, updated | +| schema (schema) | The schema of the object. | | | sorts (kind) | The sort count type. | merge_passes, range, rows, scan | +| table_name (table) | Table name for event or process. | | | threads (kind) | The thread count type. | cached, connected, created, running | diff --git a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go index cd7d1b13e4ab..f6e456542cbd 100644 --- a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go +++ b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go @@ -28,6 +28,8 @@ type MetricsSettings struct { MysqlCommands MetricSettings `mapstructure:"mysql.commands"` MysqlDoubleWrites MetricSettings `mapstructure:"mysql.double_writes"` MysqlHandlers MetricSettings `mapstructure:"mysql.handlers"` + MysqlIndexIoWaitCount MetricSettings `mapstructure:"mysql.index.io.wait.count"` + MysqlIndexIoWaitTime MetricSettings `mapstructure:"mysql.index.io.wait.time"` MysqlLocks MetricSettings `mapstructure:"mysql.locks"` MysqlLogOperations MetricSettings `mapstructure:"mysql.log_operations"` MysqlOperations MetricSettings `mapstructure:"mysql.operations"` @@ -35,6 +37,8 @@ type MetricsSettings struct { MysqlRowLocks MetricSettings `mapstructure:"mysql.row_locks"` MysqlRowOperations MetricSettings `mapstructure:"mysql.row_operations"` MysqlSorts MetricSettings `mapstructure:"mysql.sorts"` + MysqlTableIoWaitCount MetricSettings `mapstructure:"mysql.table.io.wait.count"` + MysqlTableIoWaitTime MetricSettings `mapstructure:"mysql.table.io.wait.time"` MysqlThreads MetricSettings `mapstructure:"mysql.threads"` } @@ -67,6 +71,12 @@ func DefaultMetricsSettings() MetricsSettings { MysqlHandlers: MetricSettings{ Enabled: true, }, + MysqlIndexIoWaitCount: MetricSettings{ + Enabled: true, + }, + MysqlIndexIoWaitTime: MetricSettings{ + Enabled: true, + }, MysqlLocks: MetricSettings{ Enabled: true, }, @@ -88,6 +98,12 @@ func DefaultMetricsSettings() MetricsSettings { MysqlSorts: MetricSettings{ Enabled: true, }, + MysqlTableIoWaitCount: MetricSettings{ + Enabled: true, + }, + MysqlTableIoWaitTime: MetricSettings{ + Enabled: true, + }, MysqlThreads: MetricSettings{ Enabled: true, }, @@ -354,6 +370,40 @@ var MapAttributeHandler = map[string]AttributeHandler{ "write": AttributeHandlerWrite, } +// AttributeIoWaitsOperations specifies the a value io_waits_operations attribute. +type AttributeIoWaitsOperations int + +const ( + _ AttributeIoWaitsOperations = iota + AttributeIoWaitsOperationsDelete + AttributeIoWaitsOperationsFetch + AttributeIoWaitsOperationsInsert + AttributeIoWaitsOperationsUpdate +) + +// String returns the string representation of the AttributeIoWaitsOperations. +func (av AttributeIoWaitsOperations) String() string { + switch av { + case AttributeIoWaitsOperationsDelete: + return "delete" + case AttributeIoWaitsOperationsFetch: + return "fetch" + case AttributeIoWaitsOperationsInsert: + return "insert" + case AttributeIoWaitsOperationsUpdate: + return "update" + } + return "" +} + +// MapAttributeIoWaitsOperations is a helper map of string to AttributeIoWaitsOperations attribute value. +var MapAttributeIoWaitsOperations = map[string]AttributeIoWaitsOperations{ + "delete": AttributeIoWaitsOperationsDelete, + "fetch": AttributeIoWaitsOperationsFetch, + "insert": AttributeIoWaitsOperationsInsert, + "update": AttributeIoWaitsOperationsUpdate, +} + // AttributeLocks specifies the a value locks attribute. type AttributeLocks int @@ -1071,6 +1121,118 @@ func newMetricMysqlHandlers(settings MetricSettings) metricMysqlHandlers { return m } +type metricMysqlIndexIoWaitCount struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mysql.index.io.wait.count metric with initial data. +func (m *metricMysqlIndexIoWaitCount) init() { + m.data.SetName("mysql.index.io.wait.count") + m.data.SetDescription("The total count of I/O wait events for an index.") + m.data.SetUnit("1") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlIndexIoWaitCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue string, tableNameAttributeValue string, schemaAttributeValue string, indexNameAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) + dp.Attributes().PutString("operation", ioWaitsOperationsAttributeValue) + dp.Attributes().PutString("table", tableNameAttributeValue) + dp.Attributes().PutString("schema", schemaAttributeValue) + dp.Attributes().PutString("index", indexNameAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlIndexIoWaitCount) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMysqlIndexIoWaitCount) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlIndexIoWaitCount(settings MetricSettings) metricMysqlIndexIoWaitCount { + m := metricMysqlIndexIoWaitCount{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMysqlIndexIoWaitTime struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mysql.index.io.wait.time metric with initial data. +func (m *metricMysqlIndexIoWaitTime) init() { + m.data.SetName("mysql.index.io.wait.time") + m.data.SetDescription("The total time of I/O wait events for an index.") + m.data.SetUnit("ns") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlIndexIoWaitTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue string, tableNameAttributeValue string, schemaAttributeValue string, indexNameAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) + dp.Attributes().PutString("operation", ioWaitsOperationsAttributeValue) + dp.Attributes().PutString("table", tableNameAttributeValue) + dp.Attributes().PutString("schema", schemaAttributeValue) + dp.Attributes().PutString("index", indexNameAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlIndexIoWaitTime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMysqlIndexIoWaitTime) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlIndexIoWaitTime(settings MetricSettings) metricMysqlIndexIoWaitTime { + m := metricMysqlIndexIoWaitTime{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricMysqlLocks struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -1442,6 +1604,116 @@ func newMetricMysqlSorts(settings MetricSettings) metricMysqlSorts { return m } +type metricMysqlTableIoWaitCount struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mysql.table.io.wait.count metric with initial data. +func (m *metricMysqlTableIoWaitCount) init() { + m.data.SetName("mysql.table.io.wait.count") + m.data.SetDescription("The total count of I/O wait events for a table.") + m.data.SetUnit("1") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlTableIoWaitCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue string, tableNameAttributeValue string, schemaAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) + dp.Attributes().PutString("operation", ioWaitsOperationsAttributeValue) + dp.Attributes().PutString("table", tableNameAttributeValue) + dp.Attributes().PutString("schema", schemaAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlTableIoWaitCount) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMysqlTableIoWaitCount) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlTableIoWaitCount(settings MetricSettings) metricMysqlTableIoWaitCount { + m := metricMysqlTableIoWaitCount{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricMysqlTableIoWaitTime struct { + data pmetric.Metric // data buffer for generated metric. + settings MetricSettings // metric settings provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills mysql.table.io.wait.time metric with initial data. +func (m *metricMysqlTableIoWaitTime) init() { + m.data.SetName("mysql.table.io.wait.time") + m.data.SetDescription("The total time of I/O wait events for a table.") + m.data.SetUnit("ns") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.MetricAggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricMysqlTableIoWaitTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue string, tableNameAttributeValue string, schemaAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntVal(val) + dp.Attributes().PutString("operation", ioWaitsOperationsAttributeValue) + dp.Attributes().PutString("table", tableNameAttributeValue) + dp.Attributes().PutString("schema", schemaAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlTableIoWaitTime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricMysqlTableIoWaitTime) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlTableIoWaitTime(settings MetricSettings) metricMysqlTableIoWaitTime { + m := metricMysqlTableIoWaitTime{settings: settings} + if settings.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricMysqlThreads struct { data pmetric.Metric // data buffer for generated metric. settings MetricSettings // metric settings provided by user. @@ -1512,6 +1784,8 @@ type MetricsBuilder struct { metricMysqlCommands metricMysqlCommands metricMysqlDoubleWrites metricMysqlDoubleWrites metricMysqlHandlers metricMysqlHandlers + metricMysqlIndexIoWaitCount metricMysqlIndexIoWaitCount + metricMysqlIndexIoWaitTime metricMysqlIndexIoWaitTime metricMysqlLocks metricMysqlLocks metricMysqlLogOperations metricMysqlLogOperations metricMysqlOperations metricMysqlOperations @@ -1519,6 +1793,8 @@ type MetricsBuilder struct { metricMysqlRowLocks metricMysqlRowLocks metricMysqlRowOperations metricMysqlRowOperations metricMysqlSorts metricMysqlSorts + metricMysqlTableIoWaitCount metricMysqlTableIoWaitCount + metricMysqlTableIoWaitTime metricMysqlTableIoWaitTime metricMysqlThreads metricMysqlThreads } @@ -1546,6 +1822,8 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricMysqlCommands: newMetricMysqlCommands(settings.MysqlCommands), metricMysqlDoubleWrites: newMetricMysqlDoubleWrites(settings.MysqlDoubleWrites), metricMysqlHandlers: newMetricMysqlHandlers(settings.MysqlHandlers), + metricMysqlIndexIoWaitCount: newMetricMysqlIndexIoWaitCount(settings.MysqlIndexIoWaitCount), + metricMysqlIndexIoWaitTime: newMetricMysqlIndexIoWaitTime(settings.MysqlIndexIoWaitTime), metricMysqlLocks: newMetricMysqlLocks(settings.MysqlLocks), metricMysqlLogOperations: newMetricMysqlLogOperations(settings.MysqlLogOperations), metricMysqlOperations: newMetricMysqlOperations(settings.MysqlOperations), @@ -1553,6 +1831,8 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricMysqlRowLocks: newMetricMysqlRowLocks(settings.MysqlRowLocks), metricMysqlRowOperations: newMetricMysqlRowOperations(settings.MysqlRowOperations), metricMysqlSorts: newMetricMysqlSorts(settings.MysqlSorts), + metricMysqlTableIoWaitCount: newMetricMysqlTableIoWaitCount(settings.MysqlTableIoWaitCount), + metricMysqlTableIoWaitTime: newMetricMysqlTableIoWaitTime(settings.MysqlTableIoWaitTime), metricMysqlThreads: newMetricMysqlThreads(settings.MysqlThreads), } for _, op := range options { @@ -1622,6 +1902,8 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricMysqlCommands.emit(ils.Metrics()) mb.metricMysqlDoubleWrites.emit(ils.Metrics()) mb.metricMysqlHandlers.emit(ils.Metrics()) + mb.metricMysqlIndexIoWaitCount.emit(ils.Metrics()) + mb.metricMysqlIndexIoWaitTime.emit(ils.Metrics()) mb.metricMysqlLocks.emit(ils.Metrics()) mb.metricMysqlLogOperations.emit(ils.Metrics()) mb.metricMysqlOperations.emit(ils.Metrics()) @@ -1629,6 +1911,8 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricMysqlRowLocks.emit(ils.Metrics()) mb.metricMysqlRowOperations.emit(ils.Metrics()) mb.metricMysqlSorts.emit(ils.Metrics()) + mb.metricMysqlTableIoWaitCount.emit(ils.Metrics()) + mb.metricMysqlTableIoWaitTime.emit(ils.Metrics()) mb.metricMysqlThreads.emit(ils.Metrics()) for _, op := range rmo { op(rm) @@ -1729,6 +2013,16 @@ func (mb *MetricsBuilder) RecordMysqlHandlersDataPoint(ts pcommon.Timestamp, inp return nil } +// RecordMysqlIndexIoWaitCountDataPoint adds a data point to mysql.index.io.wait.count metric. +func (mb *MetricsBuilder) RecordMysqlIndexIoWaitCountDataPoint(ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue AttributeIoWaitsOperations, tableNameAttributeValue string, schemaAttributeValue string, indexNameAttributeValue string) { + mb.metricMysqlIndexIoWaitCount.recordDataPoint(mb.startTime, ts, val, ioWaitsOperationsAttributeValue.String(), tableNameAttributeValue, schemaAttributeValue, indexNameAttributeValue) +} + +// RecordMysqlIndexIoWaitTimeDataPoint adds a data point to mysql.index.io.wait.time metric. +func (mb *MetricsBuilder) RecordMysqlIndexIoWaitTimeDataPoint(ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue AttributeIoWaitsOperations, tableNameAttributeValue string, schemaAttributeValue string, indexNameAttributeValue string) { + mb.metricMysqlIndexIoWaitTime.recordDataPoint(mb.startTime, ts, val, ioWaitsOperationsAttributeValue.String(), tableNameAttributeValue, schemaAttributeValue, indexNameAttributeValue) +} + // RecordMysqlLocksDataPoint adds a data point to mysql.locks metric. func (mb *MetricsBuilder) RecordMysqlLocksDataPoint(ts pcommon.Timestamp, inputVal string, locksAttributeValue AttributeLocks) error { val, err := strconv.ParseInt(inputVal, 10, 64) @@ -1799,6 +2093,16 @@ func (mb *MetricsBuilder) RecordMysqlSortsDataPoint(ts pcommon.Timestamp, inputV return nil } +// RecordMysqlTableIoWaitCountDataPoint adds a data point to mysql.table.io.wait.count metric. +func (mb *MetricsBuilder) RecordMysqlTableIoWaitCountDataPoint(ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue AttributeIoWaitsOperations, tableNameAttributeValue string, schemaAttributeValue string) { + mb.metricMysqlTableIoWaitCount.recordDataPoint(mb.startTime, ts, val, ioWaitsOperationsAttributeValue.String(), tableNameAttributeValue, schemaAttributeValue) +} + +// RecordMysqlTableIoWaitTimeDataPoint adds a data point to mysql.table.io.wait.time metric. +func (mb *MetricsBuilder) RecordMysqlTableIoWaitTimeDataPoint(ts pcommon.Timestamp, val int64, ioWaitsOperationsAttributeValue AttributeIoWaitsOperations, tableNameAttributeValue string, schemaAttributeValue string) { + mb.metricMysqlTableIoWaitTime.recordDataPoint(mb.startTime, ts, val, ioWaitsOperationsAttributeValue.String(), tableNameAttributeValue, schemaAttributeValue) +} + // RecordMysqlThreadsDataPoint adds a data point to mysql.threads metric. func (mb *MetricsBuilder) RecordMysqlThreadsDataPoint(ts pcommon.Timestamp, inputVal string, threadsAttributeValue AttributeThreads) error { val, err := strconv.ParseInt(inputVal, 10, 64) diff --git a/receiver/mysqlreceiver/metadata.yaml b/receiver/mysqlreceiver/metadata.yaml index 5d8a9f24c67e..65e5fa79780e 100644 --- a/receiver/mysqlreceiver/metadata.yaml +++ b/receiver/mysqlreceiver/metadata.yaml @@ -62,6 +62,19 @@ attributes: value: kind description: The thread count type. enum: [cached, connected, created, running] + schema: + value: schema + description: The schema of the object. + io_waits_operations: + value: operation + description: The io_waits operation type. + enum: [delete, fetch, insert, update] + table_name: + value: table + description: Table name for event or process. + index_name: + value: index + description: The name of the index. metrics: mysql.buffer_pool.pages: @@ -180,6 +193,42 @@ metrics: monotonic: true aggregation: cumulative attributes: [page_operations] + mysql.table.io.wait.count: + enabled: true + description: The total count of I/O wait events for a table. + unit: 1 + sum: + value_type: int + monotonic: true + aggregation: cumulative + attributes: [io_waits_operations, table_name, schema] + mysql.table.io.wait.time: + enabled: true + description: The total time of I/O wait events for a table. + unit: ns + sum: + value_type: int + monotonic: true + aggregation: cumulative + attributes: [io_waits_operations, table_name, schema] + mysql.index.io.wait.count: + enabled: true + description: The total count of I/O wait events for an index. + unit: 1 + sum: + value_type: int + monotonic: true + aggregation: cumulative + attributes: [io_waits_operations, table_name, schema, index_name] + mysql.index.io.wait.time: + enabled: true + description: The total time of I/O wait events for an index. + unit: ns + sum: + value_type: int + monotonic: true + aggregation: cumulative + attributes: [io_waits_operations, table_name, schema, index_name] mysql.row_locks: enabled: true description: The number of InnoDB row locks. diff --git a/receiver/mysqlreceiver/scraper.go b/receiver/mysqlreceiver/scraper.go index cb133fa28fb1..b2c744acf845 100644 --- a/receiver/mysqlreceiver/scraper.go +++ b/receiver/mysqlreceiver/scraper.go @@ -29,6 +29,10 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/mysqlreceiver/internal/metadata" ) +const ( + picosecondsInNanoseconds int64 = 1000 +) + type mySQLScraper struct { sqlclient client logger *zap.Logger @@ -90,6 +94,10 @@ func (m *mySQLScraper) scrape(context.Context) (pmetric.Metrics, error) { addPartialIfError(errs, m.mb.RecordMysqlBufferPoolLimitDataPoint(now, v)) } + // collect io_waits metrics. + m.scrapeTableIoWaitsStats(now, errs) + m.scrapeIndexIoWaitsStats(now, errs) + // collect global status metrics. globalStats, err := m.sqlclient.getGlobalStats() if err != nil { @@ -276,6 +284,70 @@ func (m *mySQLScraper) scrape(context.Context) (pmetric.Metrics, error) { return m.mb.Emit(), errs.Combine() } +func (m *mySQLScraper) scrapeTableIoWaitsStats(now pcommon.Timestamp, errs *scrapererror.ScrapeErrors) { + tableIoWaitsStats, err := m.sqlclient.getTableIoWaitsStats() + if err != nil { + m.logger.Error("Failed to fetch table io_waits stats", zap.Error(err)) + errs.AddPartial(8, err) + return + } + + for i := 0; i < len(tableIoWaitsStats); i++ { + s := tableIoWaitsStats[i] + // counts + m.mb.RecordMysqlTableIoWaitCountDataPoint(now, s.countDelete, metadata.AttributeIoWaitsOperationsDelete, s.name, s.schema) + m.mb.RecordMysqlTableIoWaitCountDataPoint(now, s.countFetch, metadata.AttributeIoWaitsOperationsFetch, s.name, s.schema) + m.mb.RecordMysqlTableIoWaitCountDataPoint(now, s.countInsert, metadata.AttributeIoWaitsOperationsInsert, s.name, s.schema) + m.mb.RecordMysqlTableIoWaitCountDataPoint(now, s.countUpdate, metadata.AttributeIoWaitsOperationsUpdate, s.name, s.schema) + + // times + m.mb.RecordMysqlTableIoWaitTimeDataPoint( + now, s.timeDelete/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsDelete, s.name, s.schema, + ) + m.mb.RecordMysqlTableIoWaitTimeDataPoint( + now, s.timeFetch/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsFetch, s.name, s.schema, + ) + m.mb.RecordMysqlTableIoWaitTimeDataPoint( + now, s.timeInsert/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsInsert, s.name, s.schema, + ) + m.mb.RecordMysqlTableIoWaitTimeDataPoint( + now, s.timeUpdate/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsUpdate, s.name, s.schema, + ) + } +} + +func (m *mySQLScraper) scrapeIndexIoWaitsStats(now pcommon.Timestamp, errs *scrapererror.ScrapeErrors) { + indexIoWaitsStats, err := m.sqlclient.getIndexIoWaitsStats() + if err != nil { + m.logger.Error("Failed to fetch index io_waits stats", zap.Error(err)) + errs.AddPartial(8, err) + return + } + + for i := 0; i < len(indexIoWaitsStats); i++ { + s := indexIoWaitsStats[i] + // counts + m.mb.RecordMysqlIndexIoWaitCountDataPoint(now, s.countDelete, metadata.AttributeIoWaitsOperationsDelete, s.name, s.schema, s.index) + m.mb.RecordMysqlIndexIoWaitCountDataPoint(now, s.countFetch, metadata.AttributeIoWaitsOperationsFetch, s.name, s.schema, s.index) + m.mb.RecordMysqlIndexIoWaitCountDataPoint(now, s.countInsert, metadata.AttributeIoWaitsOperationsInsert, s.name, s.schema, s.index) + m.mb.RecordMysqlIndexIoWaitCountDataPoint(now, s.countUpdate, metadata.AttributeIoWaitsOperationsUpdate, s.name, s.schema, s.index) + + // times + m.mb.RecordMysqlIndexIoWaitTimeDataPoint( + now, s.timeDelete/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsDelete, s.name, s.schema, s.index, + ) + m.mb.RecordMysqlIndexIoWaitTimeDataPoint( + now, s.timeFetch/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsFetch, s.name, s.schema, s.index, + ) + m.mb.RecordMysqlIndexIoWaitTimeDataPoint( + now, s.timeInsert/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsInsert, s.name, s.schema, s.index, + ) + m.mb.RecordMysqlIndexIoWaitTimeDataPoint( + now, s.timeUpdate/picosecondsInNanoseconds, metadata.AttributeIoWaitsOperationsUpdate, s.name, s.schema, s.index, + ) + } +} + func addPartialIfError(errors *scrapererror.ScrapeErrors, err error) { if err != nil { errors.AddPartial(1, err) diff --git a/receiver/mysqlreceiver/scraper_test.go b/receiver/mysqlreceiver/scraper_test.go index d881fdfaf64d..b6a940d7436b 100644 --- a/receiver/mysqlreceiver/scraper_test.go +++ b/receiver/mysqlreceiver/scraper_test.go @@ -42,8 +42,10 @@ func TestScrape(t *testing.T) { scraper := newMySQLScraper(componenttest.NewNopReceiverCreateSettings(), cfg) scraper.sqlclient = &mockClient{ - globalStatsFile: "global_stats", - innodbStatsFile: "innodb_stats", + globalStatsFile: "global_stats", + innodbStatsFile: "innodb_stats", + tableIoWaitsFile: "table_io_waits_stats", + indexIoWaitsFile: "index_io_waits_stats", } actualMetrics, err := scraper.scrape(context.Background()) @@ -64,8 +66,10 @@ func TestScrape(t *testing.T) { scraper := newMySQLScraper(componenttest.NewNopReceiverCreateSettings(), cfg) scraper.sqlclient = &mockClient{ - globalStatsFile: "global_stats_partial", - innodbStatsFile: "innodb_stats_empty", + globalStatsFile: "global_stats_partial", + innodbStatsFile: "innodb_stats_empty", + tableIoWaitsFile: "table_io_waits_stats_empty", + indexIoWaitsFile: "index_io_waits_stats_empty", } actualMetrics, scrapeErr := scraper.scrape(context.Background()) @@ -88,8 +92,10 @@ func TestScrape(t *testing.T) { var _ client = (*mockClient)(nil) type mockClient struct { - globalStatsFile string - innodbStatsFile string + globalStatsFile string + innodbStatsFile string + tableIoWaitsFile string + indexIoWaitsFile string } func readFile(fname string) (map[string]string, error) { @@ -120,6 +126,65 @@ func (c *mockClient) getInnodbStats() (map[string]string, error) { return readFile(c.innodbStatsFile) } +func (c *mockClient) getTableIoWaitsStats() ([]TableIoWaitsStats, error) { + var stats []TableIoWaitsStats + file, err := os.Open(filepath.Join("testdata", "scraper", c.tableIoWaitsFile+".txt")) + if err != nil { + return nil, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + var s TableIoWaitsStats + text := strings.Split(scanner.Text(), "\t") + + s.schema = text[0] + s.name = text[1] + s.countDelete, _ = parseInt(text[2]) + s.countFetch, _ = parseInt(text[3]) + s.countInsert, _ = parseInt(text[4]) + s.countUpdate, _ = parseInt(text[5]) + s.timeDelete, _ = parseInt(text[6]) + s.timeFetch, _ = parseInt(text[7]) + s.timeInsert, _ = parseInt(text[8]) + s.timeUpdate, _ = parseInt(text[9]) + + stats = append(stats, s) + } + return stats, nil +} + +func (c *mockClient) getIndexIoWaitsStats() ([]IndexIoWaitsStats, error) { + var stats []IndexIoWaitsStats + file, err := os.Open(filepath.Join("testdata", "scraper", c.indexIoWaitsFile+".txt")) + if err != nil { + return nil, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + var s IndexIoWaitsStats + text := strings.Split(scanner.Text(), "\t") + + s.schema = text[0] + s.name = text[1] + s.index = text[2] + s.countDelete, _ = parseInt(text[3]) + s.countFetch, _ = parseInt(text[4]) + s.countInsert, _ = parseInt(text[5]) + s.countUpdate, _ = parseInt(text[6]) + s.timeDelete, _ = parseInt(text[7]) + s.timeFetch, _ = parseInt(text[8]) + s.timeInsert, _ = parseInt(text[9]) + s.timeUpdate, _ = parseInt(text[10]) + + stats = append(stats, s) + } + return stats, nil +} + func (c *mockClient) Close() error { return nil } diff --git a/receiver/mysqlreceiver/testdata/integration/expected.5_7.json b/receiver/mysqlreceiver/testdata/integration/expected.5_7.json index d3bfeb2a9674..2a54ddb586df 100644 --- a/receiver/mysqlreceiver/testdata/integration/expected.5_7.json +++ b/receiver/mysqlreceiver/testdata/integration/expected.5_7.json @@ -1044,6 +1044,498 @@ ] }, "unit": "1" + }, + { + "description": "The total count of I/O wait events for a table.", + "name": "mysql.table.io.waits.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "1", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "2", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "3", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "4", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for a table.", + "name": "mysql.table.io.waits.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "5", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "6", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "7", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "8", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" + }, + { + "description": "The total count of I/O wait events for an index.", + "name": "mysql.index.io.waits.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "9", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "10", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "11", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "12", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for an index.", + "name": "mysql.index.io.waits.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "13", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "14", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "15", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "16", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" } ] } diff --git a/receiver/mysqlreceiver/testdata/integration/expected.8_0.json b/receiver/mysqlreceiver/testdata/integration/expected.8_0.json index d3bfeb2a9674..2a54ddb586df 100644 --- a/receiver/mysqlreceiver/testdata/integration/expected.8_0.json +++ b/receiver/mysqlreceiver/testdata/integration/expected.8_0.json @@ -1044,6 +1044,498 @@ ] }, "unit": "1" + }, + { + "description": "The total count of I/O wait events for a table.", + "name": "mysql.table.io.waits.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "1", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "2", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "3", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "4", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for a table.", + "name": "mysql.table.io.waits.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "5", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "6", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "7", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "8", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" + }, + { + "description": "The total count of I/O wait events for an index.", + "name": "mysql.index.io.waits.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "9", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "10", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "11", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "12", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for an index.", + "name": "mysql.index.io.waits.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "13", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "14", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "15", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "16", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" } ] } diff --git a/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh b/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh index 0afe41c6d3d5..3632beeecd95 100644 --- a/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh +++ b/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh @@ -14,13 +14,27 @@ setup_permissions() { mysql -u root -p"${ROOT_PASS}" -e "FLUSH PRIVILEGES" > /dev/null } +setup_data() { + mysql -u root -p"${ROOT_PASS}" -e "CREATE DATABASE a_schema" > /dev/null + mysql -u root -p"${ROOT_PASS}" -e "CREATE TABLE a_schema.a_table (k int, v int)" > /dev/null + mysql -u root -p"${ROOT_PASS}" -e "CREATE INDEX an_index ON a_schema.a_table ((k + v))" > /dev/null +} + echo "Configuring ${USER} permissions. . ." end=$((SECONDS+60)) while [ $SECONDS -lt $end ]; do result="$?" if setup_permissions; then echo "Permissions configured!" - exit 0 + while [ $SECONDS -lt $end ]; do + result="$?" + if setup_data; then + echo "Data created!" + exit 0 + fi + echo "Trying again in 5 seconds. . ." + sleep 5 + done fi echo "Trying again in 5 seconds. . ." sleep 5 diff --git a/receiver/mysqlreceiver/testdata/scraper/expected.json b/receiver/mysqlreceiver/testdata/scraper/expected.json index a77203ea7f6b..74d262e04c72 100644 --- a/receiver/mysqlreceiver/testdata/scraper/expected.json +++ b/receiver/mysqlreceiver/testdata/scraper/expected.json @@ -1044,6 +1044,498 @@ ] }, "unit": "1" + }, + { + "description": "The total count of I/O wait events for a table.", + "name": "mysql.table.io.wait.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "1", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "2", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "3", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "4", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for a table.", + "name": "mysql.table.io.wait.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "5", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "6", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "7", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "8", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" + }, + { + "description": "The total count of I/O wait events for an index.", + "name": "mysql.index.io.wait.count", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "9", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "10", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "11", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "12", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, + { + "description": "The total time of I/O wait events for an index.", + "name": "mysql.index.io.wait.time", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "13", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "delete" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "14", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "fetch" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "15", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "insert" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "16", + "attributes": [ + { + "key": "operation", + "value": { + "stringValue": "update" + } + }, + { + "key": "table", + "value": { + "stringValue": "a_table" + } + }, + { + "key": "schema", + "value": { + "stringValue": "a_schema" + } + }, + { + "key": "index", + "value": { + "stringValue": "an_index" + } + } + ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "ns" } ] } diff --git a/receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats.txt b/receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats.txt new file mode 100644 index 000000000000..a9a654474d05 --- /dev/null +++ b/receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats.txt @@ -0,0 +1 @@ +a_schema a_table an_index 9 10 11 12 13000 14000 15000 16000 diff --git a/receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats_empty.txt b/receiver/mysqlreceiver/testdata/scraper/index_io_waits_stats_empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats.txt b/receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats.txt new file mode 100644 index 000000000000..4cd424c899e1 --- /dev/null +++ b/receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats.txt @@ -0,0 +1 @@ +a_schema a_table 1 2 3 4 5000 6000 7000 8000 diff --git a/receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats_empty.txt b/receiver/mysqlreceiver/testdata/scraper/table_io_waits_stats_empty.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/unreleased/mysql-receiver-add-perf-io-waits.yaml b/unreleased/mysql-receiver-add-perf-io-waits.yaml new file mode 100644 index 000000000000..c383db7df73e --- /dev/null +++ b/unreleased/mysql-receiver-add-perf-io-waits.yaml @@ -0,0 +1,5 @@ +change_type: enhancement +component: mysqlreceiver +note: The receiver now scraper 16 new metrics related to io_waits. +issues: [14138] +subtext: