diff --git a/.chloggen/drosiek-mysql-select-metric.yaml b/.chloggen/drosiek-mysql-select-metric.yaml new file mode 100755 index 000000000000..e2aeb37384b6 --- /dev/null +++ b/.chloggen/drosiek-mysql-select-metric.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: mysqlreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add mysql.joins metric + +# One or more tracking issues related to the change +issues: [14138] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/receiver/mysqlreceiver/documentation.md b/receiver/mysqlreceiver/documentation.md index 9b35170ee455..a5f454197a6a 100644 --- a/receiver/mysqlreceiver/documentation.md +++ b/receiver/mysqlreceiver/documentation.md @@ -19,6 +19,7 @@ These are the metrics available for this scraper. | **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.joins** | The number of joins that perform table scans. | 1 | 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) | | @@ -58,6 +59,7 @@ metrics: | 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 | +| join_kind (kind) | The kind of join. | full, full_range, range, range_check, scan | | 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 | diff --git a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go index bd607f400d3c..d6bc3d44d3c9 100644 --- a/receiver/mysqlreceiver/internal/metadata/generated_metrics.go +++ b/receiver/mysqlreceiver/internal/metadata/generated_metrics.go @@ -30,6 +30,7 @@ type MetricsSettings struct { MysqlHandlers MetricSettings `mapstructure:"mysql.handlers"` MysqlIndexIoWaitCount MetricSettings `mapstructure:"mysql.index.io.wait.count"` MysqlIndexIoWaitTime MetricSettings `mapstructure:"mysql.index.io.wait.time"` + MysqlJoins MetricSettings `mapstructure:"mysql.joins"` MysqlLocks MetricSettings `mapstructure:"mysql.locks"` MysqlLogOperations MetricSettings `mapstructure:"mysql.log_operations"` MysqlOperations MetricSettings `mapstructure:"mysql.operations"` @@ -78,6 +79,9 @@ func DefaultMetricsSettings() MetricsSettings { MysqlIndexIoWaitTime: MetricSettings{ Enabled: true, }, + MysqlJoins: MetricSettings{ + Enabled: true, + }, MysqlLocks: MetricSettings{ Enabled: true, }, @@ -408,6 +412,44 @@ var MapAttributeIoWaitsOperations = map[string]AttributeIoWaitsOperations{ "update": AttributeIoWaitsOperationsUpdate, } +// AttributeJoinKind specifies the a value join_kind attribute. +type AttributeJoinKind int + +const ( + _ AttributeJoinKind = iota + AttributeJoinKindFull + AttributeJoinKindFullRange + AttributeJoinKindRange + AttributeJoinKindRangeCheck + AttributeJoinKindScan +) + +// String returns the string representation of the AttributeJoinKind. +func (av AttributeJoinKind) String() string { + switch av { + case AttributeJoinKindFull: + return "full" + case AttributeJoinKindFullRange: + return "full_range" + case AttributeJoinKindRange: + return "range" + case AttributeJoinKindRangeCheck: + return "range_check" + case AttributeJoinKindScan: + return "scan" + } + return "" +} + +// MapAttributeJoinKind is a helper map of string to AttributeJoinKind attribute value. +var MapAttributeJoinKind = map[string]AttributeJoinKind{ + "full": AttributeJoinKindFull, + "full_range": AttributeJoinKindFullRange, + "range": AttributeJoinKindRange, + "range_check": AttributeJoinKindRangeCheck, + "scan": AttributeJoinKindScan, +} + // AttributeLocks specifies the a value locks attribute. type AttributeLocks int @@ -1267,6 +1309,59 @@ func newMetricMysqlIndexIoWaitTime(settings MetricSettings) metricMysqlIndexIoWa return m } +type metricMysqlJoins 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.joins metric with initial data. +func (m *metricMysqlJoins) init() { + m.data.SetName("mysql.joins") + m.data.SetDescription("The number of joins that perform table scans.") + 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 *metricMysqlJoins) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, joinKindAttributeValue string) { + if !m.settings.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("kind", joinKindAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricMysqlJoins) 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 *metricMysqlJoins) emit(metrics pmetric.MetricSlice) { + if m.settings.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricMysqlJoins(settings MetricSettings) metricMysqlJoins { + m := metricMysqlJoins{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. @@ -1873,6 +1968,7 @@ type MetricsBuilder struct { metricMysqlHandlers metricMysqlHandlers metricMysqlIndexIoWaitCount metricMysqlIndexIoWaitCount metricMysqlIndexIoWaitTime metricMysqlIndexIoWaitTime + metricMysqlJoins metricMysqlJoins metricMysqlLocks metricMysqlLocks metricMysqlLogOperations metricMysqlLogOperations metricMysqlOperations metricMysqlOperations @@ -1912,6 +2008,7 @@ func NewMetricsBuilder(settings MetricsSettings, buildInfo component.BuildInfo, metricMysqlHandlers: newMetricMysqlHandlers(settings.MysqlHandlers), metricMysqlIndexIoWaitCount: newMetricMysqlIndexIoWaitCount(settings.MysqlIndexIoWaitCount), metricMysqlIndexIoWaitTime: newMetricMysqlIndexIoWaitTime(settings.MysqlIndexIoWaitTime), + metricMysqlJoins: newMetricMysqlJoins(settings.MysqlJoins), metricMysqlLocks: newMetricMysqlLocks(settings.MysqlLocks), metricMysqlLogOperations: newMetricMysqlLogOperations(settings.MysqlLogOperations), metricMysqlOperations: newMetricMysqlOperations(settings.MysqlOperations), @@ -1993,6 +2090,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricMysqlHandlers.emit(ils.Metrics()) mb.metricMysqlIndexIoWaitCount.emit(ils.Metrics()) mb.metricMysqlIndexIoWaitTime.emit(ils.Metrics()) + mb.metricMysqlJoins.emit(ils.Metrics()) mb.metricMysqlLocks.emit(ils.Metrics()) mb.metricMysqlLogOperations.emit(ils.Metrics()) mb.metricMysqlOperations.emit(ils.Metrics()) @@ -2113,6 +2211,16 @@ func (mb *MetricsBuilder) RecordMysqlIndexIoWaitTimeDataPoint(ts pcommon.Timesta mb.metricMysqlIndexIoWaitTime.recordDataPoint(mb.startTime, ts, val, ioWaitsOperationsAttributeValue.String(), tableNameAttributeValue, schemaAttributeValue, indexNameAttributeValue) } +// RecordMysqlJoinsDataPoint adds a data point to mysql.joins metric. +func (mb *MetricsBuilder) RecordMysqlJoinsDataPoint(ts pcommon.Timestamp, inputVal string, joinKindAttributeValue AttributeJoinKind) error { + val, err := strconv.ParseInt(inputVal, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse int64 for MysqlJoins, value was %s: %w", inputVal, err) + } + mb.metricMysqlJoins.recordDataPoint(mb.startTime, ts, val, joinKindAttributeValue.String()) + return nil +} + // 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) diff --git a/receiver/mysqlreceiver/metadata.yaml b/receiver/mysqlreceiver/metadata.yaml index 8d919bdafd28..7e83e9fa06bc 100644 --- a/receiver/mysqlreceiver/metadata.yaml +++ b/receiver/mysqlreceiver/metadata.yaml @@ -75,6 +75,10 @@ attributes: index_name: value: index description: The name of the index. + join_kind: + value: kind + description: The kind of join. + enum: [full, full_range, range, range_check, scan] tmp_resource: value: resource description: The kind of temporary resources. @@ -283,6 +287,16 @@ metrics: monotonic: false aggregation: cumulative attributes: [threads] + mysql.joins: + enabled: true + description: The number of joins that perform table scans. + unit: 1 + sum: + value_type: int + input_type: string + monotonic: true + aggregation: cumulative + attributes: [join_kind] mysql.tmp_resources: enabled: true description: The number of created temporary resources. diff --git a/receiver/mysqlreceiver/scraper.go b/receiver/mysqlreceiver/scraper.go index 9b7163505547..db94a7cc5b3b 100644 --- a/receiver/mysqlreceiver/scraper.go +++ b/receiver/mysqlreceiver/scraper.go @@ -274,6 +274,18 @@ func (m *mySQLScraper) scrapeGlobalStats(now pcommon.Timestamp, errs *scrapererr case "Table_locks_waited": addPartialIfError(errs, m.mb.RecordMysqlLocksDataPoint(now, v, metadata.AttributeLocksWaited)) + // joins + case "Select_full_join": + addPartialIfError(errs, m.mb.RecordMysqlJoinsDataPoint(now, v, metadata.AttributeJoinKindFull)) + case "Select_full_range_join": + addPartialIfError(errs, m.mb.RecordMysqlJoinsDataPoint(now, v, metadata.AttributeJoinKindFullRange)) + case "Select_range": + addPartialIfError(errs, m.mb.RecordMysqlJoinsDataPoint(now, v, metadata.AttributeJoinKindRange)) + case "Select_range_check": + addPartialIfError(errs, m.mb.RecordMysqlJoinsDataPoint(now, v, metadata.AttributeJoinKindRangeCheck)) + case "Select_scan": + addPartialIfError(errs, m.mb.RecordMysqlJoinsDataPoint(now, v, metadata.AttributeJoinKindScan)) + // sorts case "Sort_merge_passes": addPartialIfError(errs, m.mb.RecordMysqlSortsDataPoint(now, v, metadata.AttributeSortsMergePasses)) diff --git a/receiver/mysqlreceiver/testdata/scraper/expected.json b/receiver/mysqlreceiver/testdata/scraper/expected.json index bed533c47f10..4224583f5bd5 100644 --- a/receiver/mysqlreceiver/testdata/scraper/expected.json +++ b/receiver/mysqlreceiver/testdata/scraper/expected.json @@ -1537,6 +1537,77 @@ }, "unit": "ns" }, + { + "description": "The number of joins that perform table scans.", + "name": "mysql.joins", + "sum": { + "aggregationTemporality": "AGGREGATION_TEMPORALITY_CUMULATIVE", + "isMonotonic": true, + "dataPoints": [ + { + "asInt": "408", + "attributes": [ + { + "key": "kind", + "value": { + "stringValue": "full" + } + } ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "409", + "attributes": [ + { + "key": "kind", + "value": { + "stringValue": "full_range" + } + } ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "410", + "attributes": [ + { + "key": "kind", + "value": { + "stringValue": "range" + } + } ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "411", + "attributes": [ + { + "key": "kind", + "value": { + "stringValue": "range_check" + } + } ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + }, + { + "asInt": "412", + "attributes": [ + { + "key": "kind", + "value": { + "stringValue": "scan" + } + } ], + "startTimeUnixNano": "1644862687825728000", + "timeUnixNano": "1644862687825772000" + } + ] + }, + "unit": "1" + }, { "description": "The number of created temporary resources.", "name": "mysql.tmp_resources",