| 1 | //go:build cgo |
| 2 | |
| 3 | package db2 |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "sort" |
| 8 | "strings" |
| 9 | |
| 10 | "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/db2/contexts" |
| 11 | ) |
| 12 | |
| 13 | type bufferpoolEntry struct { |
| 14 | name string |
| 15 | meta *bufferpoolMetrics |
| 16 | metrics bufferpoolInstanceMetrics |
| 17 | } |
| 18 | |
| 19 | type bufferpoolGroupAggregate struct { |
| 20 | Hits, Misses int64 |
| 21 | DataHits, DataMisses int64 |
| 22 | IndexHits, IndexMisses int64 |
| 23 | XDAHits, XDAMisses int64 |
| 24 | ColumnHits, ColumnMisses int64 |
| 25 | |
| 26 | LogicalReads, PhysicalReads int64 |
| 27 | DataLogicalReads, DataPhysicalReads int64 |
| 28 | IndexLogicalReads, IndexPhysicalReads int64 |
| 29 | |
| 30 | UsedPages, TotalPages int64 |
| 31 | Writes int64 |
| 32 | } |
| 33 | |
| 34 | func bufferpoolGroupKey(name string) string { |
| 35 | if name == "" { |
| 36 | return "__unknown__" |
| 37 | } |
| 38 | up := strings.ToUpper(name) |
| 39 | if idx := strings.Index(up, "_"); idx > 0 { |
| 40 | return up[:idx] |
| 41 | } |
| 42 | return up |
| 43 | } |
| 44 | |
| 45 | func (a *bufferpoolGroupAggregate) add(m bufferpoolInstanceMetrics) { |
| 46 | a.Hits += m.Hits |
| 47 | a.Misses += m.Misses |
| 48 | a.DataHits += m.DataHits |
| 49 | a.DataMisses += m.DataMisses |
| 50 | a.IndexHits += m.IndexHits |
| 51 | a.IndexMisses += m.IndexMisses |
| 52 | a.XDAHits += m.XDAHits |
| 53 | a.XDAMisses += m.XDAMisses |
| 54 | a.ColumnHits += m.ColumnHits |
| 55 | a.ColumnMisses += m.ColumnMisses |
| 56 | |
| 57 | a.LogicalReads += m.LogicalReads |
| 58 | a.PhysicalReads += m.PhysicalReads |
| 59 | a.DataLogicalReads += m.DataLogicalReads |
| 60 | a.DataPhysicalReads += m.DataPhysicalReads |
| 61 | a.IndexLogicalReads += m.IndexLogicalReads |
| 62 | a.IndexPhysicalReads += m.IndexPhysicalReads |
| 63 | |
| 64 | a.UsedPages += m.UsedPages |
| 65 | a.TotalPages += m.TotalPages |
| 66 | a.Writes += m.Writes |
| 67 | } |
| 68 | |
| 69 | func (c *Collector) exportBufferpoolMetrics(mx metricsData) { |
| 70 | entries := make([]bufferpoolEntry, 0, len(mx.bufferpools)) |
| 71 | for name, metrics := range mx.bufferpools { |
| 72 | entries = append(entries, bufferpoolEntry{ |
| 73 | name: name, |
| 74 | meta: c.bufferpools[name], |
| 75 | metrics: metrics, |
| 76 | }) |
| 77 | } |
| 78 | |
| 79 | if len(entries) == 0 { |
| 80 | c.clearWarnOnce("db2_bufferpool_overflow") |
| 81 | return |
| 82 | } |
| 83 | |
| 84 | sort.Slice(entries, func(i, j int) bool { |
| 85 | return entries[i].name < entries[j].name |
| 86 | }) |
| 87 | |
| 88 | limit := c.MaxBufferpools |
| 89 | if limit <= 0 || limit > len(entries) { |
| 90 | limit = len(entries) |
| 91 | } |
| 92 | |
| 93 | groupAgg := make(map[string]*bufferpoolGroupAggregate) |
| 94 | overflowAgg := &bufferpoolGroupAggregate{} |
| 95 | overflowCount := 0 |
| 96 | overflowGroups := make(map[string]int) |
| 97 | overflowExample := make(map[string]string) |
| 98 | |
| 99 | for idx, entry := range entries { |
| 100 | groupKey := bufferpoolGroupKey(entry.name) |
| 101 | agg := groupAgg[groupKey] |
| 102 | if agg == nil { |
| 103 | agg = &bufferpoolGroupAggregate{} |
| 104 | groupAgg[groupKey] = agg |
| 105 | } |
| 106 | agg.add(entry.metrics) |
| 107 | |
| 108 | if idx < limit { |
| 109 | c.emitPerBufferpoolMetrics(entry) |
| 110 | continue |
| 111 | } |
| 112 | |
| 113 | overflowAgg.add(entry.metrics) |
| 114 | overflowCount++ |
| 115 | overflowGroups[groupKey]++ |
| 116 | if _, ok := overflowExample[groupKey]; !ok { |
| 117 | overflowExample[groupKey] = entry.name |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | c.emitBufferpoolGroupMetrics(groupAgg, overflowAgg, overflowCount) |
| 122 | |
| 123 | if overflowCount > 0 { |
| 124 | parts := make([]string, 0, len(overflowGroups)) |
| 125 | for group, count := range overflowGroups { |
| 126 | parts = append(parts, fmt.Sprintf("%s:%d (e.g. %s)", group, count, overflowExample[group])) |
| 127 | } |
| 128 | sort.Strings(parts) |
| 129 | c.warnOnce("db2_bufferpool_overflow", "too many buffer pools for per-instance charts (MaxBufferpools=%d). Aggregated %d additional pools: %s", c.MaxBufferpools, overflowCount, strings.Join(parts, ", ")) |
| 130 | } else { |
| 131 | c.clearWarnOnce("db2_bufferpool_overflow") |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | func (c *Collector) emitPerBufferpoolMetrics(entry bufferpoolEntry) { |
| 136 | pageSizeLabel := "unknown" |
| 137 | if entry.meta != nil && entry.meta.pageSize > 0 { |
| 138 | pageSizeLabel = fmt.Sprintf("%d", entry.meta.pageSize) |
| 139 | } |
| 140 | |
| 141 | labels := contexts.BufferpoolLabels{ |
| 142 | Bufferpool: entry.name, |
| 143 | Page_size: pageSizeLabel, |
| 144 | } |
| 145 | |
| 146 | totalReads := entry.metrics.Hits + entry.metrics.Misses |
| 147 | overallRatio := int64(0) |
| 148 | if totalReads > 0 { |
| 149 | overallRatio = entry.metrics.Hits * 100 * Precision / totalReads |
| 150 | } |
| 151 | |
| 152 | dataReads := entry.metrics.DataHits + entry.metrics.DataMisses |
| 153 | dataRatio := int64(0) |
| 154 | if dataReads > 0 { |
| 155 | dataRatio = entry.metrics.DataHits * 100 * Precision / dataReads |
| 156 | } |
| 157 | |
| 158 | indexReads := entry.metrics.IndexHits + entry.metrics.IndexMisses |
| 159 | indexRatio := int64(0) |
| 160 | if indexReads > 0 { |
| 161 | indexRatio = entry.metrics.IndexHits * 100 * Precision / indexReads |
| 162 | } |
| 163 | |
| 164 | xdaReads := entry.metrics.XDAHits + entry.metrics.XDAMisses |
| 165 | xdaRatio := int64(0) |
| 166 | if xdaReads > 0 { |
| 167 | xdaRatio = entry.metrics.XDAHits * 100 * Precision / xdaReads |
| 168 | } |
| 169 | |
| 170 | columnReads := entry.metrics.ColumnHits + entry.metrics.ColumnMisses |
| 171 | columnRatio := int64(0) |
| 172 | if columnReads > 0 { |
| 173 | columnRatio = entry.metrics.ColumnHits * 100 * Precision / columnReads |
| 174 | } |
| 175 | |
| 176 | contexts.Bufferpool.HitRatio.Set(c.State, labels, contexts.BufferpoolHitRatioValues{ |
| 177 | Overall: overallRatio, |
| 178 | }) |
| 179 | |
| 180 | contexts.Bufferpool.DetailedHitRatio.Set(c.State, labels, contexts.BufferpoolDetailedHitRatioValues{ |
| 181 | Data: dataRatio, |
| 182 | Index: indexRatio, |
| 183 | Xda: xdaRatio, |
| 184 | Column: columnRatio, |
| 185 | }) |
| 186 | |
| 187 | contexts.Bufferpool.Reads.Set(c.State, labels, contexts.BufferpoolReadsValues{ |
| 188 | Logical: entry.metrics.LogicalReads, |
| 189 | Physical: entry.metrics.PhysicalReads, |
| 190 | }) |
| 191 | |
| 192 | contexts.Bufferpool.DataReads.Set(c.State, labels, contexts.BufferpoolDataReadsValues{ |
| 193 | Logical: entry.metrics.DataLogicalReads, |
| 194 | Physical: entry.metrics.DataPhysicalReads, |
| 195 | }) |
| 196 | |
| 197 | contexts.Bufferpool.IndexReads.Set(c.State, labels, contexts.BufferpoolIndexReadsValues{ |
| 198 | Logical: entry.metrics.IndexLogicalReads, |
| 199 | Physical: entry.metrics.IndexPhysicalReads, |
| 200 | }) |
| 201 | |
| 202 | contexts.Bufferpool.Pages.Set(c.State, labels, contexts.BufferpoolPagesValues{ |
| 203 | Used: entry.metrics.UsedPages, |
| 204 | Total: entry.metrics.TotalPages, |
| 205 | }) |
| 206 | |
| 207 | contexts.Bufferpool.Writes.Set(c.State, labels, contexts.BufferpoolWritesValues{ |
| 208 | Writes: entry.metrics.Writes, |
| 209 | }) |
| 210 | |
| 211 | contexts.Bufferpool.DataReads.SetUpdateEvery(c.State, labels, c.Config.UpdateEvery) |
| 212 | contexts.Bufferpool.IndexReads.SetUpdateEvery(c.State, labels, c.Config.UpdateEvery) |
| 213 | } |
| 214 | |
| 215 | func (c *Collector) emitBufferpoolGroupMetrics(groups map[string]*bufferpoolGroupAggregate, overflow *bufferpoolGroupAggregate, overflowCount int) { |
| 216 | keys := make([]string, 0, len(groups)) |
| 217 | for k := range groups { |
| 218 | keys = append(keys, k) |
| 219 | } |
| 220 | sort.Strings(keys) |
| 221 | |
| 222 | for _, key := range keys { |
| 223 | agg := groups[key] |
| 224 | labels := contexts.BufferpoolGroupLabels{Group: key} |
| 225 | |
| 226 | totalReads := agg.Hits + agg.Misses |
| 227 | overallRatio := int64(0) |
| 228 | if totalReads > 0 { |
| 229 | overallRatio = agg.Hits * 100 * Precision / totalReads |
| 230 | } |
| 231 | |
| 232 | dataReads := agg.DataHits + agg.DataMisses |
| 233 | dataRatio := int64(0) |
| 234 | if dataReads > 0 { |
| 235 | dataRatio = agg.DataHits * 100 * Precision / dataReads |
| 236 | } |
| 237 | |
| 238 | indexReads := agg.IndexHits + agg.IndexMisses |
| 239 | indexRatio := int64(0) |
| 240 | if indexReads > 0 { |
| 241 | indexRatio = agg.IndexHits * 100 * Precision / indexReads |
| 242 | } |
| 243 | |
| 244 | xdaReads := agg.XDAHits + agg.XDAMisses |
| 245 | xdaRatio := int64(0) |
| 246 | if xdaReads > 0 { |
| 247 | xdaRatio = agg.XDAHits * 100 * Precision / xdaReads |
| 248 | } |
| 249 | |
| 250 | columnReads := agg.ColumnHits + agg.ColumnMisses |
| 251 | columnRatio := int64(0) |
| 252 | if columnReads > 0 { |
| 253 | columnRatio = agg.ColumnHits * 100 * Precision / columnReads |
| 254 | } |
| 255 | |
| 256 | contexts.BufferpoolGroup.HitRatio.Set(c.State, labels, contexts.BufferpoolGroupHitRatioValues{ |
| 257 | Overall: overallRatio, |
| 258 | }) |
| 259 | |
| 260 | contexts.BufferpoolGroup.DetailedHitRatio.Set(c.State, labels, contexts.BufferpoolGroupDetailedHitRatioValues{ |
| 261 | Data: dataRatio, |
| 262 | Index: indexRatio, |
| 263 | Xda: xdaRatio, |
| 264 | Column: columnRatio, |
| 265 | }) |
| 266 | |
| 267 | contexts.BufferpoolGroup.Reads.Set(c.State, labels, contexts.BufferpoolGroupReadsValues{ |
| 268 | Logical: agg.LogicalReads, |
| 269 | Physical: agg.PhysicalReads, |
| 270 | }) |
| 271 | |
| 272 | contexts.BufferpoolGroup.DataReads.Set(c.State, labels, contexts.BufferpoolGroupDataReadsValues{ |
| 273 | Logical: agg.DataLogicalReads, |
| 274 | Physical: agg.DataPhysicalReads, |
| 275 | }) |
| 276 | |
| 277 | contexts.BufferpoolGroup.IndexReads.Set(c.State, labels, contexts.BufferpoolGroupIndexReadsValues{ |
| 278 | Logical: agg.IndexLogicalReads, |
| 279 | Physical: agg.IndexPhysicalReads, |
| 280 | }) |
| 281 | |
| 282 | contexts.BufferpoolGroup.Pages.Set(c.State, labels, contexts.BufferpoolGroupPagesValues{ |
| 283 | Used: agg.UsedPages, |
| 284 | Total: agg.TotalPages, |
| 285 | }) |
| 286 | |
| 287 | contexts.BufferpoolGroup.Writes.Set(c.State, labels, contexts.BufferpoolGroupWritesValues{ |
| 288 | Writes: agg.Writes, |
| 289 | }) |
| 290 | } |
| 291 | |
| 292 | if overflowCount > 0 && overflow != nil { |
| 293 | labels := contexts.BufferpoolGroupLabels{Group: "__other__"} |
| 294 | |
| 295 | totalReads := overflow.Hits + overflow.Misses |
| 296 | overallRatio := int64(0) |
| 297 | if totalReads > 0 { |
| 298 | overallRatio = overflow.Hits * 100 * Precision / totalReads |
| 299 | } |
| 300 | |
| 301 | dataReads := overflow.DataHits + overflow.DataMisses |
| 302 | dataRatio := int64(0) |
| 303 | if dataReads > 0 { |
| 304 | dataRatio = overflow.DataHits * 100 * Precision / dataReads |
| 305 | } |
| 306 | |
| 307 | indexReads := overflow.IndexHits + overflow.IndexMisses |
| 308 | indexRatio := int64(0) |
| 309 | if indexReads > 0 { |
| 310 | indexRatio = overflow.IndexHits * 100 * Precision / indexReads |
| 311 | } |
| 312 | |
| 313 | xdaReads := overflow.XDAHits + overflow.XDAMisses |
| 314 | xdaRatio := int64(0) |
| 315 | if xdaReads > 0 { |
| 316 | xdaRatio = overflow.XDAHits * 100 * Precision / xdaReads |
| 317 | } |
| 318 | |
| 319 | columnReads := overflow.ColumnHits + overflow.ColumnMisses |
| 320 | columnRatio := int64(0) |
| 321 | if columnReads > 0 { |
| 322 | columnRatio = overflow.ColumnHits * 100 * Precision / columnReads |
| 323 | } |
| 324 | |
| 325 | contexts.BufferpoolGroup.HitRatio.Set(c.State, labels, contexts.BufferpoolGroupHitRatioValues{ |
| 326 | Overall: overallRatio, |
| 327 | }) |
| 328 | contexts.BufferpoolGroup.DetailedHitRatio.Set(c.State, labels, contexts.BufferpoolGroupDetailedHitRatioValues{ |
| 329 | Data: dataRatio, |
| 330 | Index: indexRatio, |
| 331 | Xda: xdaRatio, |
| 332 | Column: columnRatio, |
| 333 | }) |
| 334 | contexts.BufferpoolGroup.Reads.Set(c.State, labels, contexts.BufferpoolGroupReadsValues{ |
| 335 | Logical: overflow.LogicalReads, |
| 336 | Physical: overflow.PhysicalReads, |
| 337 | }) |
| 338 | contexts.BufferpoolGroup.DataReads.Set(c.State, labels, contexts.BufferpoolGroupDataReadsValues{ |
| 339 | Logical: overflow.DataLogicalReads, |
| 340 | Physical: overflow.DataPhysicalReads, |
| 341 | }) |
| 342 | contexts.BufferpoolGroup.IndexReads.Set(c.State, labels, contexts.BufferpoolGroupIndexReadsValues{ |
| 343 | Logical: overflow.IndexLogicalReads, |
| 344 | Physical: overflow.IndexPhysicalReads, |
| 345 | }) |
| 346 | contexts.BufferpoolGroup.Pages.Set(c.State, labels, contexts.BufferpoolGroupPagesValues{ |
| 347 | Used: overflow.UsedPages, |
| 348 | Total: overflow.TotalPages, |
| 349 | }) |
| 350 | contexts.BufferpoolGroup.Writes.Set(c.State, labels, contexts.BufferpoolGroupWritesValues{ |
| 351 | Writes: overflow.Writes, |
| 352 | }) |
| 353 | } |
| 354 | } |