fix(go.d/oracledb): correct tablespace usage calculation for all types (#20373)
Ilya Mashchenko committed
May 29, 2025 at 13:12 UTC
2706778fecb920e5767711f631b144296a12c4da
3 files changed
+146
-46
src/go/plugin/go.d/collector/oracledb/charts.go
+6
-5
@@ -319,21 +319,22 @@ var (
319
}
320
)
321
322
-func (c *Collector) addTablespaceCharts(tablespace string) {
322
+func (c *Collector) addTablespaceCharts(ts tablespaceInfo) {
323
charts := tablespaceChartsTmpl.Copy()
324
325
for _, chart := range *charts {
326
- chart.ID = cleanChartId(fmt.Sprintf(chart.ID, tablespace))
326
+ chart.ID = cleanChartId(fmt.Sprintf(chart.ID, ts.name))
327
chart.Labels = []module.Label{
328
- {Key: "tablespace", Value: tablespace},
328
+ {Key: "tablespace", Value: ts.name},
329
+ {Key: "autoextend_status", Value: ts.autoExtent},
330
}
331
for _, dim := range chart.Dims {
331
- dim.ID = fmt.Sprintf(dim.ID, tablespace)
332
+ dim.ID = fmt.Sprintf(dim.ID, ts.name)
333
}
334
}
335
336
if err := c.Charts().Add(*charts...); err != nil {
336
- c.Warningf("failed to add tablespace '%s' charts: %v", tablespace, err)
337
+ c.Warningf("failed to add tablespace '%s' charts: %v", ts.name, err)
338
}
339
}
340
src/go/plugin/go.d/collector/oracledb/collect_tablespace.go
+134
-41
@@ -5,45 +5,111 @@ package oracledb
5
import (
6
"fmt"
7
"strconv"
8
+ "strings"
9
)
10
11
const queryTablespace = `
11
-SELECT
12
- f.tablespace_name,
13
- f.autoextensible,
14
- SUM(f.bytes) AS allocated_bytes,
15
- SUM(f.maxbytes) AS max_bytes,
16
- (SUM(f.bytes) - COALESCE(SUM(fs.free_bytes), 0)) AS used_bytes
17
-FROM
18
- dba_data_files f
19
-LEFT JOIN
20
- (
21
- SELECT
12
+SELECT
13
+ ts.tablespace_name,
14
+ ts.contents AS tablespace_type,
15
+ CASE
16
+ WHEN df_all.files_with_autoextend = 0 THEN 'NO'
17
+ WHEN df_all.files_with_autoextend = df_all.total_files THEN 'YES'
18
+ ELSE 'MIXED (' || df_all.files_with_autoextend || '/' || df_all.total_files || ')'
19
+ END AS autoextend_status,
20
+ CASE
21
+ -- For PERMANENT tablespaces: allocated - free
22
+ WHEN ts.contents = 'PERMANENT' THEN
23
+ COALESCE(df_all.allocated_bytes, 0) - COALESCE(fs.free_bytes, 0)
24
+ -- For UNDO tablespaces: sum of UNDO segments
25
+ WHEN ts.contents = 'UNDO' THEN
26
+ COALESCE(us.used_bytes, 0)
27
+ -- For TEMPORARY tablespaces: use v$temp_space_header
28
+ WHEN ts.contents = 'TEMPORARY' THEN
29
+ COALESCE(tu.used_bytes, 0)
30
+ ELSE 0
31
+ END AS used_bytes,
32
+ COALESCE(df_all.allocated_bytes, 0) AS allocated_bytes,
33
+ -- For max_bytes, only count maxbytes from files with autoextend=YES
34
+ COALESCE(df_all.max_bytes, 0) AS max_bytes
35
+FROM
36
+ dba_tablespaces ts
37
+ -- Combined datafiles and tempfiles info with autoextend counts
38
+ LEFT JOIN (
39
+ SELECT
40
+ tablespace_name,
41
+ COUNT(*) AS total_files,
42
+ SUM(CASE WHEN autoextensible = 'YES' THEN 1 ELSE 0 END) AS files_with_autoextend,
43
+ SUM(bytes) AS allocated_bytes,
44
+ -- Only count maxbytes for files with autoextend enabled
45
+ SUM(CASE
46
+ WHEN autoextensible = 'YES' AND maxbytes > 0 THEN maxbytes
47
+ WHEN autoextensible = 'YES' AND maxbytes = 0 THEN bytes
48
+ ELSE bytes -- For non-autoextend files, current size is the max
49
+ END) AS max_bytes
50
+ FROM dba_data_files
51
+ GROUP BY tablespace_name
52
+ UNION ALL
53
+ SELECT
54
+ tablespace_name,
55
+ COUNT(*) AS total_files,
56
+ SUM(CASE WHEN autoextensible = 'YES' THEN 1 ELSE 0 END) AS files_with_autoextend,
57
+ SUM(bytes) AS allocated_bytes,
58
+ SUM(CASE
59
+ WHEN autoextensible = 'YES' AND maxbytes > 0 THEN maxbytes
60
+ WHEN autoextensible = 'YES' AND maxbytes = 0 THEN bytes
61
+ ELSE bytes
62
+ END) AS max_bytes
63
+ FROM dba_temp_files
64
+ GROUP BY tablespace_name
65
+ ) df_all ON ts.tablespace_name = df_all.tablespace_name
66
+ -- Free space for permanent tablespaces
67
+ LEFT JOIN (
68
+ SELECT
69
tablespace_name,
70
SUM(bytes) AS free_bytes
24
- FROM
25
- dba_free_space
26
- GROUP BY
27
- tablespace_name
28
- ) fs
29
- ON f.tablespace_name = fs.tablespace_name
30
-GROUP BY
31
- f.tablespace_name, f.autoextensible
71
+ FROM dba_free_space
72
+ GROUP BY tablespace_name
73
+ ) fs ON ts.tablespace_name = fs.tablespace_name
74
+ -- Used space for UNDO tablespaces
75
+ LEFT JOIN (
76
+ SELECT
77
+ tablespace_name,
78
+ SUM(bytes) AS used_bytes
79
+ FROM dba_segments
80
+ WHERE segment_type LIKE 'UNDO%'
81
+ GROUP BY tablespace_name
82
+ ) us ON ts.tablespace_name = us.tablespace_name
83
+ -- Used space for TEMPORARY tablespaces
84
+ LEFT JOIN (
85
+ SELECT
86
+ tablespace_name,
87
+ SUM(bytes_used) AS used_bytes
88
+ FROM v$temp_space_header
89
+ GROUP BY tablespace_name
90
+ ) tu ON ts.tablespace_name = tu.tablespace_name
91
+WHERE
92
+ df_all.tablespace_name IS NOT NULL -- Only show tablespaces with datafiles/tempfiles
93
+ORDER BY
94
+ ts.tablespace_name
95
`
96
97
+type tablespaceInfo struct {
98
+ name string
99
+ typ string
100
+ autoExtent string
101
+ allocBytes float64
102
+ maxBytes float64
103
+ usedBytes float64
104
+}
105
+
106
func (c *Collector) collectTablespace(mx map[string]int64) error {
107
q := queryTablespace
108
c.Debugf("executing query: %s", q)
109
38
- var ts struct {
39
- name string
40
- autoExtent bool
41
- allocBytes float64
42
- maxBytes float64
43
- usedBytes float64
44
- }
110
+ var ts tablespaceInfo
111
46
- seen := make(map[string]bool)
112
+ seen := make(map[string]tablespaceInfo)
113
114
err := c.doQuery(q, func(column, value string, lineEnd bool) error {
115
var err error
@@ -51,8 +117,18 @@ func (c *Collector) collectTablespace(mx map[string]int64) error {
117
switch column {
118
case "TABLESPACE_NAME":
119
ts.name = value
54
- case "AUTOEXTENSIBLE":
55
- ts.autoExtent = value == "YES"
120
+ case "TABLESPACE_TYPE":
121
+ ts.typ = value
122
+ case "AUTOEXTEND_STATUS":
123
+ switch {
124
+ case value == "YES":
125
+ value = "enabled"
126
+ case value == "NO":
127
+ value = "disabled"
128
+ case strings.HasPrefix(value, "MIXED"):
129
+ value = "mixed"
130
+ }
131
+ ts.autoExtent = value
132
case "ALLOCATED_BYTES":
133
ts.allocBytes, err = strconv.ParseFloat(value, 64)
134
case "MAX_BYTES":
@@ -65,21 +141,38 @@ func (c *Collector) collectTablespace(mx map[string]int64) error {
141
}
142
143
if lineEnd {
68
- seen[ts.name] = true
144
+ if ts.typ == "TEMPORARY" {
145
+ return nil
146
+ }
147
+ seen[ts.name] = ts
148
+
149
+ limit := ts.maxBytes
150
+ if ts.autoExtent == "disabled" {
151
+ limit = ts.allocBytes
152
+ }
153
70
- limit := ts.allocBytes
71
- if ts.autoExtent {
72
- limit = ts.maxBytes
154
+ used := ts.usedBytes
155
+ if used > limit {
156
+ used = limit
157
+ }
158
+
159
+ avail := limit - used
160
+
161
+ var util float64
162
+ if limit > 0 {
163
+ if util = used / limit * 100; util > 100 {
164
+ util = 100
165
+ }
166
}
167
168
px := fmt.Sprintf("tablespace_%s_", ts.name)
169
170
mx[px+"max_size_bytes"] = int64(limit)
78
- mx[px+"used_bytes"] = int64(ts.usedBytes)
79
- mx[px+"avail_bytes"] = int64(limit - ts.usedBytes)
171
+ mx[px+"used_bytes"] = int64(used)
172
+ mx[px+"avail_bytes"] = int64(avail)
173
mx[px+"utilization"] = 0
174
if limit > 0 {
82
- mx[px+"utilization"] = int64(ts.usedBytes / limit * 100 * precision)
175
+ mx[px+"utilization"] = int64(util * precision)
176
}
177
}
178
@@ -89,14 +182,14 @@ func (c *Collector) collectTablespace(mx map[string]int64) error {
182
return err
183
}
184
92
- for name := range seen {
93
- if !c.seenTablespaces[name] {
94
- c.seenTablespaces[name] = true
95
- c.addTablespaceCharts(name)
185
+ for _, ts := range seen {
186
+ if !c.seenTablespaces[ts.name] {
187
+ c.seenTablespaces[ts.name] = true
188
+ c.addTablespaceCharts(ts)
189
}
190
}
191
for name := range c.seenTablespaces {
99
- if !seen[name] {
192
+ if _, ok := seen[name]; !ok {
193
delete(c.seenTablespaces, name)
194
c.removeTablespaceChart(name)
195
}
src/go/plugin/go.d/collector/oracledb/metadata.yaml
+6
@@ -34,6 +34,10 @@ modules:
34
- `v$system_wait_class`
35
- `dba_data_files`
36
- `dba_free_space`
37
+ - `dba_segments`
38
+ - `dba_temp_files`
39
+ - `dba_tablespaces`
40
+ - `v$temp_space_header`
41
default_behavior:
42
auto_detection:
43
description: |
@@ -243,6 +247,8 @@ modules:
247
labels:
248
- name: tablespace
249
description: Tablespace name.
250
+ - name: autoextend_status
251
+ description: Autoextend status (enabled, disabled, mixed).
252
metrics:
253
- name: oracledb.tablespace_utilization
254
description: Tablespace Utilization