| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package cockroachdb |
| 4 | |
| 5 | import ( |
| 6 | "errors" |
| 7 | |
| 8 | "github.com/netdata/netdata/go/plugins/pkg/prometheus" |
| 9 | "github.com/netdata/netdata/go/plugins/pkg/stm" |
| 10 | ) |
| 11 | |
| 12 | func validCockroachDBMetrics(scraped prometheus.Series) bool { |
| 13 | return scraped.FindByName("sql_restart_savepoint_count_internal").Len() > 0 |
| 14 | } |
| 15 | |
| 16 | func (c *Collector) collect() (map[string]int64, error) { |
| 17 | scraped, err := c.prom.ScrapeSeries() |
| 18 | if err != nil { |
| 19 | return nil, err |
| 20 | } |
| 21 | |
| 22 | if !validCockroachDBMetrics(scraped) { |
| 23 | return nil, errors.New("returned metrics aren't CockroachDB metrics") |
| 24 | } |
| 25 | |
| 26 | mx := collectScraped(scraped, metrics) |
| 27 | calcUsableCapacity(mx) |
| 28 | calcUnusableCapacity(mx) |
| 29 | calcTotalCapacityUsedPercentage(mx) |
| 30 | calcUsableCapacityUsedPercentage(mx) |
| 31 | calcRocksDBCacheHitRate(mx) |
| 32 | calcActiveReplicas(mx) |
| 33 | calcCPUUsagePercent(mx) |
| 34 | |
| 35 | return stm.ToMap(mx), nil |
| 36 | } |
| 37 | |
| 38 | const precision = 1000 |
| 39 | |
| 40 | func collectScraped(scraped prometheus.Series, metricList []string) map[string]float64 { |
| 41 | mx := make(map[string]float64) |
| 42 | for _, name := range metricList { |
| 43 | for _, m := range scraped.FindByName(name) { |
| 44 | if isMetricFloat(name) { |
| 45 | mx[name] += m.Value * precision |
| 46 | } else { |
| 47 | mx[name] += m.Value |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | return mx |
| 52 | } |
| 53 | |
| 54 | func calcUsableCapacity(mx map[string]float64) { |
| 55 | if !hasAll(mx, metricCapacityAvailable, metricCapacityUsed) { |
| 56 | return |
| 57 | } |
| 58 | available := mx[metricCapacityAvailable] |
| 59 | used := mx[metricCapacityUsed] |
| 60 | |
| 61 | mx[metricCapacityUsable] = available + used |
| 62 | } |
| 63 | |
| 64 | func calcUnusableCapacity(mx map[string]float64) { |
| 65 | if !hasAll(mx, metricCapacity, metricCapacityAvailable, metricCapacityUsed) { |
| 66 | return |
| 67 | } |
| 68 | total := mx[metricCapacity] |
| 69 | available := mx[metricCapacityAvailable] |
| 70 | used := mx[metricCapacityUsed] |
| 71 | |
| 72 | mx[metricCapacityUnusable] = total - (available + used) |
| 73 | } |
| 74 | |
| 75 | func calcTotalCapacityUsedPercentage(mx map[string]float64) { |
| 76 | if !hasAll(mx, metricCapacity, metricCapacityUnusable, metricCapacityUsed) { |
| 77 | return |
| 78 | } |
| 79 | total := mx[metricCapacity] |
| 80 | unusable := mx[metricCapacityUnusable] |
| 81 | used := mx[metricCapacityUsed] |
| 82 | |
| 83 | if mx[metricCapacity] == 0 { |
| 84 | mx[metricCapacityUsedPercentage] = 0 |
| 85 | } else { |
| 86 | mx[metricCapacityUsedPercentage] = (unusable + used) / total * 100 * precision |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | func calcUsableCapacityUsedPercentage(mx map[string]float64) { |
| 91 | if !hasAll(mx, metricCapacityUsable, metricCapacityUsed) { |
| 92 | return |
| 93 | } |
| 94 | usable := mx[metricCapacityUsable] |
| 95 | used := mx[metricCapacityUsed] |
| 96 | |
| 97 | if usable == 0 { |
| 98 | mx[metricCapacityUsableUsedPercentage] = 0 |
| 99 | } else { |
| 100 | mx[metricCapacityUsableUsedPercentage] = used / usable * 100 * precision |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | func calcRocksDBCacheHitRate(mx map[string]float64) { |
| 105 | if !hasAll(mx, metricRocksDBBlockCacheHits, metricRocksDBBlockCacheMisses) { |
| 106 | return |
| 107 | } |
| 108 | hits := mx[metricRocksDBBlockCacheHits] |
| 109 | misses := mx[metricRocksDBBlockCacheMisses] |
| 110 | |
| 111 | if sum := hits + misses; sum == 0 { |
| 112 | mx[metricRocksDBBlockCacheHitRate] = 0 |
| 113 | } else { |
| 114 | mx[metricRocksDBBlockCacheHitRate] = hits / sum * 100 * precision |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | func calcActiveReplicas(mx map[string]float64) { |
| 119 | if !hasAll(mx, metricReplicasQuiescent) { |
| 120 | return |
| 121 | } |
| 122 | total := mx[metricReplicas] |
| 123 | quiescent := mx[metricReplicasQuiescent] |
| 124 | |
| 125 | mx[metricReplicasActive] = total - quiescent |
| 126 | } |
| 127 | |
| 128 | func calcCPUUsagePercent(mx map[string]float64) { |
| 129 | if hasAll(mx, metricSysCPUUserPercent) { |
| 130 | mx[metricSysCPUUserPercent] *= 100 |
| 131 | } |
| 132 | if hasAll(mx, metricSysCPUSysPercent) { |
| 133 | mx[metricSysCPUSysPercent] *= 100 |
| 134 | } |
| 135 | if hasAll(mx, metricSysCPUCombinedPercentNormalized) { |
| 136 | mx[metricSysCPUCombinedPercentNormalized] *= 100 |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | func isMetricFloat(name string) bool { |
| 141 | // only Float metrics (see NewGaugeFloat64 in the cockroach repo): |
| 142 | // - GcPausePercent, CPUUserPercent, CPUCombinedPercentNorm, AverageQueriesPerSecond, AverageWritesPerSecond |
| 143 | switch name { |
| 144 | case metricSysCPUUserPercent, |
| 145 | metricSysCPUSysPercent, |
| 146 | metricSysCPUCombinedPercentNormalized, |
| 147 | metricRebalancingQueriesPerSecond, |
| 148 | metricRebalancingWritesPerSecond: |
| 149 | return true |
| 150 | } |
| 151 | return false |
| 152 | } |
| 153 | |
| 154 | func hasAll(mx map[string]float64, key string, rest ...string) bool { |
| 155 | _, ok := mx[key] |
| 156 | if len(rest) == 0 { |
| 157 | return ok |
| 158 | } |
| 159 | return ok && hasAll(mx, rest[0], rest[1:]...) |
| 160 | } |