| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package oracledb |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "strings" |
| 8 | |
| 9 | "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi" |
| 10 | ) |
| 11 | |
| 12 | const ( |
| 13 | prioSessionsCount = collectorapi.Priority + iota |
| 14 | prioActiveSessionsCount |
| 15 | prioSessionsUtilization |
| 16 | |
| 17 | prioCurrentLogons |
| 18 | prioLogonsRate |
| 19 | |
| 20 | prioTablespaceUtilization |
| 21 | prioTablespaceUsage |
| 22 | |
| 23 | prioDatabaseWaitTimeRatio |
| 24 | prioSqlServiceResponseTime |
| 25 | prioWaitClassWaitTime |
| 26 | prioEnqueueTimeouts |
| 27 | |
| 28 | prioActivities |
| 29 | |
| 30 | prioDiskIO |
| 31 | prioDiskIOPS |
| 32 | |
| 33 | prioDiskSorts |
| 34 | |
| 35 | prioTableScans |
| 36 | |
| 37 | prioCacheHitRatio |
| 38 | prioGlobalCacheBlocks |
| 39 | ) |
| 40 | |
| 41 | var globalCharts = collectorapi.Charts{ |
| 42 | sessionsCountChart.Copy(), |
| 43 | averageActiveSessionsCountChart.Copy(), |
| 44 | sessionsUtilizationChart.Copy(), |
| 45 | |
| 46 | currentLogonsChart.Copy(), |
| 47 | logonsRateChart.Copy(), |
| 48 | |
| 49 | databaseWaitTimeRatioChart.Copy(), |
| 50 | sqlServiceResponseTimeChart.Copy(), |
| 51 | enqueueTimeoutsChart.Copy(), |
| 52 | |
| 53 | activityChart.Copy(), |
| 54 | |
| 55 | diskIOChart.Copy(), |
| 56 | diskIOPSChart.Copy(), |
| 57 | |
| 58 | sortsChart.Copy(), |
| 59 | |
| 60 | tableScansChart.Copy(), |
| 61 | |
| 62 | cacheHitRatioChart.Copy(), |
| 63 | globalCacheBlocksChart.Copy(), |
| 64 | } |
| 65 | |
| 66 | var ( |
| 67 | sessionsCountChart = collectorapi.Chart{ |
| 68 | ID: "sessions", |
| 69 | Title: "Sessions", |
| 70 | Units: "sessions", |
| 71 | Fam: "sessions", |
| 72 | Ctx: "oracledb.sessions", |
| 73 | Type: collectorapi.Line, |
| 74 | Priority: prioSessionsCount, |
| 75 | Dims: collectorapi.Dims{ |
| 76 | {ID: "Session Count", Name: "sessions", Div: precision}, |
| 77 | }, |
| 78 | } |
| 79 | averageActiveSessionsCountChart = collectorapi.Chart{ |
| 80 | ID: "average_active_sessions", |
| 81 | Title: "Average Active Sessions", |
| 82 | Units: "sessions", |
| 83 | Fam: "sessions", |
| 84 | Ctx: "oracledb.average_active_sessions", |
| 85 | Type: collectorapi.Line, |
| 86 | Priority: prioActiveSessionsCount, |
| 87 | Dims: collectorapi.Dims{ |
| 88 | {ID: "Average Active Sessions", Name: "active", Div: precision}, |
| 89 | }, |
| 90 | } |
| 91 | sessionsUtilizationChart = collectorapi.Chart{ |
| 92 | ID: "sessions_utilization", |
| 93 | Title: "Sessions Limit %", |
| 94 | Units: "percent", |
| 95 | Fam: "sessions", |
| 96 | Ctx: "oracledb.sessions_utilization", |
| 97 | Type: collectorapi.Area, |
| 98 | Priority: prioSessionsUtilization, |
| 99 | Dims: collectorapi.Dims{ |
| 100 | {ID: "Session Limit %", Name: "session_limit", Div: precision}, |
| 101 | }, |
| 102 | } |
| 103 | |
| 104 | currentLogonsChart = collectorapi.Chart{ |
| 105 | ID: "current_logons", |
| 106 | Title: "Current Logons", |
| 107 | Units: "logons", |
| 108 | Fam: "logons", |
| 109 | Ctx: "oracledb.current_logons", |
| 110 | Type: collectorapi.Line, |
| 111 | Priority: prioCurrentLogons, |
| 112 | Dims: collectorapi.Dims{ |
| 113 | {ID: "logons current", Name: "logons"}, |
| 114 | }, |
| 115 | } |
| 116 | logonsRateChart = collectorapi.Chart{ |
| 117 | ID: "logons_rate", |
| 118 | Title: "Logons", |
| 119 | Units: "logons/s", |
| 120 | Fam: "logons", |
| 121 | Ctx: "oracledb.logons", |
| 122 | Type: collectorapi.Line, |
| 123 | Priority: prioLogonsRate, |
| 124 | Dims: collectorapi.Dims{ |
| 125 | {ID: "logons cumulative", Name: "logons", Algo: collectorapi.Incremental}, |
| 126 | }, |
| 127 | } |
| 128 | |
| 129 | databaseWaitTimeRatioChart = collectorapi.Chart{ |
| 130 | ID: "database_wait_time_ratio", |
| 131 | Title: "Database Wait Time Ratio", |
| 132 | Units: "percent", |
| 133 | Fam: "performance", |
| 134 | Ctx: "oracledb.database_wait_time_ratio", |
| 135 | Type: collectorapi.Area, |
| 136 | Priority: prioDatabaseWaitTimeRatio, |
| 137 | Dims: collectorapi.Dims{ |
| 138 | {ID: "Database Wait Time Ratio", Name: "db_wait_time", Div: precision}, |
| 139 | }, |
| 140 | } |
| 141 | sqlServiceResponseTimeChart = collectorapi.Chart{ |
| 142 | ID: "sql_service_response_time", |
| 143 | Title: "SQL Service Response Time", |
| 144 | Units: "seconds", |
| 145 | Fam: "performance", |
| 146 | Ctx: "oracledb.sql_service_response_time", |
| 147 | Type: collectorapi.Line, |
| 148 | Priority: prioSqlServiceResponseTime, |
| 149 | Dims: collectorapi.Dims{ |
| 150 | {ID: "SQL Service Response Time", Name: "sql_resp_time", Div: precision * 100}, |
| 151 | }, |
| 152 | } |
| 153 | enqueueTimeoutsChart = collectorapi.Chart{ |
| 154 | ID: "enqueue_timeouts", |
| 155 | Title: "Enqueue Timeouts", |
| 156 | Units: "timeouts/s", |
| 157 | Fam: "performance", |
| 158 | Ctx: "oracledb.enqueue_timeouts", |
| 159 | Type: collectorapi.Line, |
| 160 | Priority: prioEnqueueTimeouts, |
| 161 | Dims: collectorapi.Dims{ |
| 162 | {ID: "enqueue timeouts", Name: "enqueue", Algo: collectorapi.Incremental}, |
| 163 | }, |
| 164 | } |
| 165 | |
| 166 | diskIOChart = collectorapi.Chart{ |
| 167 | ID: "disk_io", |
| 168 | Title: "Disk IO", |
| 169 | Units: "bytes/s", |
| 170 | Fam: "disk", |
| 171 | Ctx: "oracledb.disk_io", |
| 172 | Type: collectorapi.Area, |
| 173 | Priority: prioDiskIO, |
| 174 | Dims: collectorapi.Dims{ |
| 175 | {ID: "physical read bytes", Name: "read", Algo: collectorapi.Incremental}, |
| 176 | {ID: "physical write bytes", Name: "written", Mul: -1, Algo: collectorapi.Incremental}, |
| 177 | }, |
| 178 | } |
| 179 | |
| 180 | diskIOPSChart = collectorapi.Chart{ |
| 181 | ID: "disk_physical_iops", |
| 182 | Title: "Disk IOPS", |
| 183 | Units: "operations/s", |
| 184 | Fam: "disk", |
| 185 | Ctx: "oracledb.disk_iops", |
| 186 | Type: collectorapi.Line, |
| 187 | Priority: prioDiskIOPS, |
| 188 | Dims: collectorapi.Dims{ |
| 189 | {ID: "physical reads", Name: "read", Algo: collectorapi.Incremental}, |
| 190 | {ID: "physical writes", Name: "write", Mul: -1, Algo: collectorapi.Incremental}, |
| 191 | }, |
| 192 | } |
| 193 | |
| 194 | sortsChart = collectorapi.Chart{ |
| 195 | ID: "sorts", |
| 196 | Title: "Sorts", |
| 197 | Units: "sorts/s", |
| 198 | Fam: "sorts", |
| 199 | Ctx: "oracledb.sorts", |
| 200 | Type: collectorapi.Line, |
| 201 | Priority: prioDiskSorts, |
| 202 | Dims: collectorapi.Dims{ |
| 203 | {ID: "sorts (memory)", Name: "memory", Algo: collectorapi.Incremental}, |
| 204 | {ID: "sorts (disk)", Name: "disk", Algo: collectorapi.Incremental}, |
| 205 | }, |
| 206 | } |
| 207 | |
| 208 | tableScansChart = collectorapi.Chart{ |
| 209 | ID: "table_scans", |
| 210 | Title: "Table Scans", |
| 211 | Units: "scans/s", |
| 212 | Fam: "table scans", |
| 213 | Ctx: "oracledb.table_scans", |
| 214 | Type: collectorapi.Line, |
| 215 | Priority: prioTableScans, |
| 216 | Dims: collectorapi.Dims{ |
| 217 | {ID: "table scans (short tables)", Name: "short_table", Algo: collectorapi.Incremental}, |
| 218 | {ID: "table scans (long tables)", Name: "long_table", Algo: collectorapi.Incremental}, |
| 219 | }, |
| 220 | } |
| 221 | |
| 222 | cacheHitRatioChart = collectorapi.Chart{ |
| 223 | ID: "cache_hit_ratio", |
| 224 | Title: "Cache Hit Ratio", |
| 225 | Units: "percent", |
| 226 | Fam: "cache", |
| 227 | Ctx: "oracledb.cache_hit_ratio", |
| 228 | Type: collectorapi.Line, |
| 229 | Priority: prioCacheHitRatio, |
| 230 | Dims: collectorapi.Dims{ |
| 231 | {ID: "Buffer Cache Hit Ratio", Name: "buffer", Div: precision}, |
| 232 | {ID: "Cursor Cache Hit Ratio", Name: "cursor", Div: precision}, |
| 233 | {ID: "Library Cache Hit Ratio", Name: "library", Div: precision}, |
| 234 | {ID: "Row Cache Hit Ratio", Name: "row", Div: precision}, |
| 235 | }, |
| 236 | } |
| 237 | globalCacheBlocksChart = collectorapi.Chart{ |
| 238 | ID: "global_cache_blocks", |
| 239 | Title: "Global Cache Blocks", |
| 240 | Units: "blocks/s", |
| 241 | Fam: "cache", |
| 242 | Ctx: "oracledb.global_cache_blocks", |
| 243 | Type: collectorapi.Line, |
| 244 | Priority: prioGlobalCacheBlocks, |
| 245 | Dims: collectorapi.Dims{ |
| 246 | {ID: "Global Cache Blocks Corrupted", Name: "corrupted", Algo: collectorapi.Incremental, Div: precision}, |
| 247 | {ID: "Global Cache Blocks Lost", Name: "lost", Algo: collectorapi.Incremental, Div: precision}, |
| 248 | }, |
| 249 | } |
| 250 | ) |
| 251 | |
| 252 | var ( |
| 253 | activityChart = collectorapi.Chart{ |
| 254 | ID: "activity", |
| 255 | Title: "Activities", |
| 256 | Units: "events/s", |
| 257 | Fam: "activity", |
| 258 | Ctx: "oracledb.activity", |
| 259 | Type: collectorapi.Line, |
| 260 | Priority: prioActivities, |
| 261 | Dims: collectorapi.Dims{ |
| 262 | {ID: "parse count (total)", Name: "parse", Algo: collectorapi.Incremental}, |
| 263 | {ID: "execute count", Name: "execute", Algo: collectorapi.Incremental}, |
| 264 | {ID: "user commits", Name: "user_commits", Algo: collectorapi.Incremental}, |
| 265 | {ID: "user rollbacks", Name: "user_rollbacks", Algo: collectorapi.Incremental}, |
| 266 | }, |
| 267 | } |
| 268 | ) |
| 269 | |
| 270 | var waitClassChartsTmpl = collectorapi.Charts{ |
| 271 | waitClassWaitTimeChartTmpl.Copy(), |
| 272 | } |
| 273 | |
| 274 | var ( |
| 275 | waitClassWaitTimeChartTmpl = collectorapi.Chart{ |
| 276 | ID: "wait_class_%s_wait_time", |
| 277 | Title: "Wait Class Wait Time", |
| 278 | Units: "milliseconds", |
| 279 | Fam: "performance", |
| 280 | Ctx: "oracledb.wait_class_wait_time", |
| 281 | Type: collectorapi.Line, |
| 282 | Priority: prioWaitClassWaitTime, |
| 283 | Dims: collectorapi.Dims{ |
| 284 | {ID: "wait_class_%s_wait_time", Name: "wait_time", Div: precision}, |
| 285 | }, |
| 286 | } |
| 287 | ) |
| 288 | |
| 289 | var tablespaceChartsTmpl = collectorapi.Charts{ |
| 290 | tablespaceUtilizationChartTmpl.Copy(), |
| 291 | tablespaceUsageChartTmpl.Copy(), |
| 292 | } |
| 293 | |
| 294 | var ( |
| 295 | tablespaceUtilizationChartTmpl = collectorapi.Chart{ |
| 296 | ID: "tablespace_%s_utilization", |
| 297 | Title: "Tablespace Utilization", |
| 298 | Units: "percent", |
| 299 | Fam: "tablespace", |
| 300 | Ctx: "oracledb.tablespace_utilization", |
| 301 | Type: collectorapi.Area, |
| 302 | Priority: prioTablespaceUtilization, |
| 303 | Dims: collectorapi.Dims{ |
| 304 | {ID: "tablespace_%s_utilization", Name: "utilization", Div: precision}, |
| 305 | }, |
| 306 | } |
| 307 | tablespaceUsageChartTmpl = collectorapi.Chart{ |
| 308 | ID: "tablespace_%s_usage", |
| 309 | Title: "Tablespace Usage", |
| 310 | Units: "bytes", |
| 311 | Fam: "tablespace", |
| 312 | Ctx: "oracledb.tablespace_usage", |
| 313 | Type: collectorapi.Stacked, |
| 314 | Priority: prioTablespaceUsage, |
| 315 | Dims: collectorapi.Dims{ |
| 316 | {ID: "tablespace_%s_avail_bytes", Name: "avail"}, |
| 317 | {ID: "tablespace_%s_used_bytes", Name: "used"}, |
| 318 | }, |
| 319 | } |
| 320 | ) |
| 321 | |
| 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, ts.name)) |
| 327 | chart.Labels = []collectorapi.Label{ |
| 328 | {Key: "tablespace", Value: ts.name}, |
| 329 | {Key: "autoextend_status", Value: ts.autoExtent}, |
| 330 | } |
| 331 | for _, dim := range chart.Dims { |
| 332 | dim.ID = fmt.Sprintf(dim.ID, ts.name) |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | if err := c.Charts().Add(*charts...); err != nil { |
| 337 | c.Warningf("failed to add tablespace '%s' charts: %v", ts.name, err) |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | func (c *Collector) removeTablespaceChart(tablespace string) {} |
| 342 | |
| 343 | func (c *Collector) addWaitClassCharts(waitClass string) { |
| 344 | charts := waitClassChartsTmpl.Copy() |
| 345 | |
| 346 | for _, chart := range *charts { |
| 347 | chart.ID = cleanChartId(fmt.Sprintf(chart.ID, waitClass)) |
| 348 | chart.Labels = []collectorapi.Label{ |
| 349 | {Key: "wait_class", Value: waitClass}, |
| 350 | } |
| 351 | for _, dim := range chart.Dims { |
| 352 | dim.ID = fmt.Sprintf(dim.ID, waitClass) |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | if err := c.Charts().Add(*charts...); err != nil { |
| 357 | c.Warningf("failed to add wait class '%s' charts: %v", waitClass, err) |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | func cleanChartId(id string) string { |
| 362 | r := strings.NewReplacer(" ", "_", ".", "_") |
| 363 | return r.Replace(id) |
| 364 | } |