master
go 324 lines 7.23 KB
Raw
1 //go:build cgo
2
3 package db2
4
5 // SPDX-License-Identifier: GPL-3.0-or-later
6
7 import (
8 "context"
9 "database/sql"
10 "errors"
11 "strings"
12 "sync"
13 "time"
14
15 "github.com/netdata/netdata/go/plugins/pkg/matcher"
16 "github.com/netdata/netdata/go/plugins/plugin/ibm.d/framework"
17 db2proto "github.com/netdata/netdata/go/plugins/plugin/ibm.d/protocols/db2"
18 )
19
20 type serverInfo struct {
21 instanceName string
22 hostName string
23 version string
24 platform string
25 }
26
27 // Collector implements the DB2 module on top of the ibm.d framework.
28 type Collector struct {
29 framework.Collector
30
31 Config `yaml:",inline" json:",inline"`
32
33 client *db2proto.Client
34 db *sql.DB
35
36 mx *metricsData
37
38 // Metadata caches
39 databases map[string]*databaseMetrics
40 bufferpools map[string]*bufferpoolMetrics
41 tablespaces map[string]*tablespaceMetrics
42 connections map[string]*connectionMetrics
43 tables map[string]*tableMetrics
44 indexes map[string]*indexMetrics
45 memoryPools map[string]*memoryPoolMetrics
46 memorySets map[string]*memorySetInstanceMetrics
47 prefetchers map[string]*prefetcherInstanceMetrics
48
49 // Selectors
50 databaseSelector matcher.Matcher
51
52 connectionInclude matcher.Matcher
53 connectionExclude matcher.Matcher
54
55 bufferpoolInclude matcher.Matcher
56 bufferpoolExclude matcher.Matcher
57
58 tablespaceInclude matcher.Matcher
59 tablespaceExclude matcher.Matcher
60
61 tableInclude matcher.Matcher
62 tableExclude matcher.Matcher
63
64 indexInclude matcher.Matcher
65 indexExclude matcher.Matcher
66
67 // DB2 version info
68 version string
69 edition string
70 versionMajor int
71 versionMinor int
72 serverInfo serverInfo
73
74 // Filtering mode flags
75 databaseFilterMode bool
76
77 // Resilience tracking
78 disabledMetrics map[string]bool
79 disabledFeatures map[string]bool
80
81 // Edition flags
82 isDB2ForAS400 bool
83 isDB2ForZOS bool
84 isDB2Cloud bool
85
86 // Memory set iteration state
87 currentMemorySetHostName string
88 currentMemorySetDBName string
89 currentMemorySetType string
90 currentMemorySetMember int64
91
92 // Prefetcher iteration state
93 currentBufferPoolName string
94
95 once sync.Once
96 metaOnce sync.Once
97
98 warnMu sync.Mutex
99 warns map[string]time.Time
100 }
101
102 const warnThrottleInterval = 10 * time.Minute
103
104 func compileMatcher(patterns []string) (matcher.Matcher, error) {
105 if len(patterns) == 0 {
106 return nil, nil
107 }
108
109 expr := strings.TrimSpace(strings.Join(patterns, " "))
110 if expr == "" {
111 return nil, nil
112 }
113
114 return matcher.NewSimplePatternsMatcher(expr)
115 }
116
117 func (c *Collector) warnOnce(key string, format string, args ...any) {
118 c.warnMu.Lock()
119 defer c.warnMu.Unlock()
120
121 if c.warns == nil {
122 c.warns = make(map[string]time.Time)
123 }
124
125 now := time.Now()
126 if last, ok := c.warns[key]; ok && now.Sub(last) < warnThrottleInterval {
127 return
128 }
129
130 c.Warningf(format, args...)
131 c.warns[key] = now
132 }
133
134 func (c *Collector) clearWarnOnce(key string) {
135 c.warnMu.Lock()
136 defer c.warnMu.Unlock()
137
138 if c.warns == nil {
139 return
140 }
141 delete(c.warns, key)
142 }
143
144 func (c *Collector) initOnce() {
145 c.once.Do(func() {
146 c.disabledMetrics = make(map[string]bool)
147 c.disabledFeatures = make(map[string]bool)
148 c.resetCaches()
149 })
150 }
151
152 func (c *Collector) resetCaches() {
153 c.mx = &metricsData{
154 databases: make(map[string]databaseInstanceMetrics),
155 bufferpools: make(map[string]bufferpoolInstanceMetrics),
156 tablespaces: make(map[string]tablespaceInstanceMetrics),
157 connections: make(map[string]connectionInstanceMetrics),
158 tables: make(map[string]tableInstanceMetrics),
159 indexes: make(map[string]indexInstanceMetrics),
160 memoryPools: make(map[string]memoryPoolInstanceMetrics),
161 memorySets: make(map[string]memorySetInstanceMetrics),
162 tableIOs: make(map[string]tableIOInstanceMetrics),
163 prefetchers: make(map[string]prefetcherInstanceMetrics),
164 }
165
166 c.databases = make(map[string]*databaseMetrics)
167 c.bufferpools = make(map[string]*bufferpoolMetrics)
168 c.tablespaces = make(map[string]*tablespaceMetrics)
169 c.connections = make(map[string]*connectionMetrics)
170 c.tables = make(map[string]*tableMetrics)
171 c.indexes = make(map[string]*indexMetrics)
172 c.memoryPools = make(map[string]*memoryPoolMetrics)
173 c.memorySets = make(map[string]*memorySetInstanceMetrics)
174 c.prefetchers = make(map[string]*prefetcherInstanceMetrics)
175 }
176
177 func (c *Collector) matchIncludeExclude(include, exclude matcher.Matcher, values ...string) bool {
178 matched := false
179 matchedMap := make(map[string]bool)
180
181 if include == nil {
182 matched = true
183 } else {
184 for _, v := range values {
185 if v == "" {
186 continue
187 }
188 if include.MatchString(v) {
189 matched = true
190 matchedMap[v] = true
191 }
192 }
193 }
194
195 if !matched {
196 return false
197 }
198
199 if exclude != nil {
200 for _, v := range values {
201 if v == "" {
202 continue
203 }
204 if exclude.MatchString(v) {
205 if include != nil && matchedMap[v] {
206 continue
207 }
208 return false
209 }
210 }
211 }
212
213 return true
214 }
215
216 func (c *Collector) allowConnection(id string, meta *connectionMetrics) bool {
217 appName := ""
218 host := ""
219 ip := ""
220 if meta != nil {
221 appName = meta.applicationName
222 host = meta.clientHostname
223 ip = meta.clientIP
224 }
225 return c.matchIncludeExclude(c.connectionInclude, c.connectionExclude, id, appName, host, ip)
226 }
227
228 func (c *Collector) allowBufferpool(name string) bool {
229 return c.matchIncludeExclude(c.bufferpoolInclude, c.bufferpoolExclude, name)
230 }
231
232 func (c *Collector) allowTablespace(name, contentType, state string) bool {
233 return c.matchIncludeExclude(c.tablespaceInclude, c.tablespaceExclude, name, contentType, state)
234 }
235
236 func (c *Collector) allowTable(key string) bool {
237 return c.matchIncludeExclude(c.tableInclude, c.tableExclude, key)
238 }
239
240 func (c *Collector) allowIndex(key string) bool {
241 return c.matchIncludeExclude(c.indexInclude, c.indexExclude, key)
242 }
243
244 // CollectOnce implements framework.CollectorImpl.
245 func (c *Collector) CollectOnce() error {
246 c.initOnce()
247
248 ctx := context.Background()
249 if err := c.ensureConnected(ctx); err != nil {
250 return err
251 }
252
253 c.metaOnce.Do(func() {
254 if err := c.detectDB2Edition(ctx); err != nil {
255 c.Warningf("failed to detect DB2 edition: %v", err)
256 }
257 c.logVersionInformation()
258 c.setConfigurationDefaults()
259 c.detectColumnOrganizedSupport(ctx)
260 c.applyGlobalLabels()
261 })
262
263 c.resetCaches()
264
265 metricsMap, err := c.collect(ctx)
266 if err != nil {
267 return err
268 }
269
270 c.exportSystemMetrics()
271 c.exportDatabaseMetrics()
272 c.exportBufferpoolMetrics(*c.mx)
273 c.exportTablespaceMetrics()
274 c.exportConnectionMetrics()
275 c.exportTableMetrics()
276 c.exportIndexMetrics()
277 c.exportMemoryPoolMetrics()
278 c.exportTableIOMetrics()
279 c.exportMemorySetMetrics()
280 c.exportPrefetcherMetrics()
281 c.applyGlobalLabels()
282
283 _ = metricsMap
284
285 return nil
286 }
287
288 func (c *Collector) ensureConnected(ctx context.Context) error {
289 if c.client == nil {
290 return errors.New("db2 collector: client not initialised")
291 }
292
293 if err := c.client.Connect(ctx); err != nil {
294 return err
295 }
296 c.db = c.client.DB()
297
298 if err := c.client.Ping(ctx); err != nil {
299 _ = c.client.Close()
300 if err := c.client.Connect(ctx); err != nil {
301 return err
302 }
303 c.db = c.client.DB()
304 if err := c.client.Ping(ctx); err != nil {
305 return err
306 }
307 }
308
309 return nil
310 }
311
312 func (c *Collector) applyGlobalLabels() {
313 labels := map[string]string{
314 "db2_version": c.version,
315 "db2_edition": c.edition,
316 }
317 if c.serverInfo.instanceName != "" {
318 labels["db2_instance"] = c.serverInfo.instanceName
319 }
320 if c.serverInfo.hostName != "" {
321 labels["db2_host"] = c.serverInfo.hostName
322 }
323 c.SetGlobalLabels(labels)
324 }