| 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 tableEntry struct { |
| 14 | name string |
| 15 | metrics tableInstanceMetrics |
| 16 | } |
| 17 | |
| 18 | type tableGroupAggregate struct { |
| 19 | DataSize int64 |
| 20 | IndexSize int64 |
| 21 | LongObjSize int64 |
| 22 | RowsRead int64 |
| 23 | RowsWritten int64 |
| 24 | Count int64 |
| 25 | } |
| 26 | |
| 27 | func tableGroupKey(name string) string { |
| 28 | if idx := strings.Index(name, "."); idx > 0 { |
| 29 | return name[:idx] |
| 30 | } |
| 31 | return name |
| 32 | } |
| 33 | |
| 34 | func (a *tableGroupAggregate) add(m tableInstanceMetrics) { |
| 35 | a.Count++ |
| 36 | a.DataSize += m.DataSize |
| 37 | a.IndexSize += m.IndexSize |
| 38 | a.LongObjSize += m.LongObjSize |
| 39 | a.RowsRead += m.RowsRead |
| 40 | a.RowsWritten += m.RowsWritten |
| 41 | } |
| 42 | |
| 43 | func (c *Collector) exportTableMetrics() { |
| 44 | entries := make([]tableEntry, 0, len(c.mx.tables)) |
| 45 | for name, metrics := range c.mx.tables { |
| 46 | entries = append(entries, tableEntry{name: name, metrics: metrics}) |
| 47 | } |
| 48 | |
| 49 | if len(entries) == 0 { |
| 50 | c.clearWarnOnce("db2_table_overflow") |
| 51 | return |
| 52 | } |
| 53 | |
| 54 | sort.Slice(entries, func(i, j int) bool { |
| 55 | return entries[i].name < entries[j].name |
| 56 | }) |
| 57 | |
| 58 | limit := c.MaxTables |
| 59 | if limit <= 0 || limit > len(entries) { |
| 60 | limit = len(entries) |
| 61 | } |
| 62 | |
| 63 | groupAgg := make(map[string]*tableGroupAggregate) |
| 64 | overflowAgg := &tableGroupAggregate{} |
| 65 | overflowCount := 0 |
| 66 | overflowGroups := make(map[string]int) |
| 67 | overflowExample := make(map[string]string) |
| 68 | |
| 69 | for idx, entry := range entries { |
| 70 | key := tableGroupKey(entry.name) |
| 71 | agg := groupAgg[key] |
| 72 | if agg == nil { |
| 73 | agg = &tableGroupAggregate{} |
| 74 | groupAgg[key] = agg |
| 75 | } |
| 76 | agg.add(entry.metrics) |
| 77 | |
| 78 | if idx < limit { |
| 79 | labels := contexts.TableLabels{Table: entry.name} |
| 80 | contexts.Table.Size.Set(c.State, labels, contexts.TableSizeValues{ |
| 81 | Data: entry.metrics.DataSize, |
| 82 | Index: entry.metrics.IndexSize, |
| 83 | Long_obj: entry.metrics.LongObjSize, |
| 84 | }) |
| 85 | contexts.Table.Activity.Set(c.State, labels, contexts.TableActivityValues{ |
| 86 | Read: entry.metrics.RowsRead, |
| 87 | Written: entry.metrics.RowsWritten, |
| 88 | }) |
| 89 | continue |
| 90 | } |
| 91 | |
| 92 | overflowAgg.add(entry.metrics) |
| 93 | overflowCount++ |
| 94 | overflowGroups[key]++ |
| 95 | if _, ok := overflowExample[key]; !ok { |
| 96 | overflowExample[key] = entry.name |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | c.emitTableGroupMetrics(groupAgg, overflowAgg, overflowCount) |
| 101 | |
| 102 | if overflowCount > 0 { |
| 103 | parts := make([]string, 0, len(overflowGroups)) |
| 104 | for group, count := range overflowGroups { |
| 105 | parts = append(parts, fmt.Sprintf("%s:%d (e.g. %s)", group, count, overflowExample[group])) |
| 106 | } |
| 107 | sort.Strings(parts) |
| 108 | c.warnOnce("db2_table_overflow", "too many tables for per-instance charts (MaxTables=%d). Aggregated %d additional tables: %s", c.MaxTables, overflowCount, strings.Join(parts, ", ")) |
| 109 | } else { |
| 110 | c.clearWarnOnce("db2_table_overflow") |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | func (c *Collector) emitTableGroupMetrics(groups map[string]*tableGroupAggregate, overflow *tableGroupAggregate, overflowCount int) { |
| 115 | keys := make([]string, 0, len(groups)) |
| 116 | for k := range groups { |
| 117 | keys = append(keys, k) |
| 118 | } |
| 119 | sort.Strings(keys) |
| 120 | |
| 121 | for _, key := range keys { |
| 122 | agg := groups[key] |
| 123 | labels := contexts.TableGroupLabels{Group: key} |
| 124 | |
| 125 | contexts.TableGroup.Size.Set(c.State, labels, contexts.TableGroupSizeValues{ |
| 126 | Data: agg.DataSize, |
| 127 | Index: agg.IndexSize, |
| 128 | Long_obj: agg.LongObjSize, |
| 129 | }) |
| 130 | |
| 131 | contexts.TableGroup.Activity.Set(c.State, labels, contexts.TableGroupActivityValues{ |
| 132 | Read: agg.RowsRead, |
| 133 | Written: agg.RowsWritten, |
| 134 | }) |
| 135 | } |
| 136 | |
| 137 | if overflowCount > 0 && overflow != nil { |
| 138 | labels := contexts.TableGroupLabels{Group: "__other__"} |
| 139 | contexts.TableGroup.Size.Set(c.State, labels, contexts.TableGroupSizeValues{ |
| 140 | Data: overflow.DataSize, |
| 141 | Index: overflow.IndexSize, |
| 142 | Long_obj: overflow.LongObjSize, |
| 143 | }) |
| 144 | contexts.TableGroup.Activity.Set(c.State, labels, contexts.TableGroupActivityValues{ |
| 145 | Read: overflow.RowsRead, |
| 146 | Written: overflow.RowsWritten, |
| 147 | }) |
| 148 | } |
| 149 | } |