| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package riakkv |
| 4 | |
| 5 | import ( |
| 6 | "slices" |
| 7 | |
| 8 | "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi" |
| 9 | ) |
| 10 | |
| 11 | const ( |
| 12 | prioKvNodeOperations = collectorapi.Priority + iota |
| 13 | prioDtVnodeUpdates |
| 14 | prioSearchQueries |
| 15 | prioSearchDocuments |
| 16 | prioConsistentOperations |
| 17 | |
| 18 | prioKvLatencyGet |
| 19 | prioKvLatencyPut |
| 20 | prioDtLatencyCounter |
| 21 | prioDtLatencySet |
| 22 | prioDtLatencyMap |
| 23 | prioSearchLatencyQuery |
| 24 | prioSearchLatencyIndex |
| 25 | prioConsistentLatencyGet |
| 26 | prioConsistentLatencyPut |
| 27 | |
| 28 | prioVmProcessesCount |
| 29 | prioVmProcessesMemory |
| 30 | |
| 31 | prioKvSiblingsEncounteredGet |
| 32 | prioKvObjSizeGet |
| 33 | prioSearchVnodeqSize |
| 34 | prioSearchIndexErrors |
| 35 | prioCorePbc |
| 36 | prioCoreRepairs |
| 37 | prioCoreFsmActive |
| 38 | prioCoreFsmREjected |
| 39 | ) |
| 40 | |
| 41 | var charts = collectorapi.Charts{ |
| 42 | kvNodeOperationsChart.Copy(), |
| 43 | dtVnodeUpdatesChart.Copy(), |
| 44 | searchQueriesChart.Copy(), |
| 45 | searchDocumentsChart.Copy(), |
| 46 | consistentOperationsChart.Copy(), |
| 47 | |
| 48 | kvLatencyGetChart.Copy(), |
| 49 | kvLatencyPutChart.Copy(), |
| 50 | dtLatencyCounterChart.Copy(), |
| 51 | dtLatencySetChart.Copy(), |
| 52 | dtLatencyMapChart.Copy(), |
| 53 | searchLatencyQueryChart.Copy(), |
| 54 | searchLatencyIndexChart.Copy(), |
| 55 | consistentLatencyGetChart.Copy(), |
| 56 | consistentLatencyPutChart.Copy(), |
| 57 | |
| 58 | vmProcessesCountChart.Copy(), |
| 59 | vmProcessesMemoryChart.Copy(), |
| 60 | |
| 61 | kvSiblingsEncounteredGetChart.Copy(), |
| 62 | kvObjectSizeGetChart.Copy(), |
| 63 | searchVnodeqSizeChart.Copy(), |
| 64 | searchIndexErrorsChart.Copy(), |
| 65 | corePbsChart.Copy(), |
| 66 | coreRepairsChart.Copy(), |
| 67 | coreFsmActiveChart.Copy(), |
| 68 | coreFsmRejectedChart.Copy(), |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | Throughput metrics |
| 73 | https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#throughput-metrics |
| 74 | |
| 75 | Collected in totals |
| 76 | */ |
| 77 | var ( |
| 78 | kvNodeOperationsChart = collectorapi.Chart{ |
| 79 | ID: "kv_node_operations", |
| 80 | Title: "Reads & writes coordinated by this node", |
| 81 | Units: "operations/s", |
| 82 | Fam: "throughput", |
| 83 | Ctx: "riak.kv.throughput", |
| 84 | Priority: prioKvNodeOperations, |
| 85 | Dims: collectorapi.Dims{ |
| 86 | {ID: "node_gets_total", Name: "gets", Algo: collectorapi.Incremental}, |
| 87 | {ID: "node_puts_total", Name: "puts", Algo: collectorapi.Incremental}, |
| 88 | }, |
| 89 | } |
| 90 | dtVnodeUpdatesChart = collectorapi.Chart{ |
| 91 | ID: "dt_vnode_updates", |
| 92 | Title: "Update operations coordinated by local vnodes by data type", |
| 93 | Units: "operations/s", |
| 94 | Fam: "throughput", |
| 95 | Ctx: "riak.dt.vnode_updates", |
| 96 | Priority: prioDtVnodeUpdates, |
| 97 | Dims: collectorapi.Dims{ |
| 98 | {ID: "vnode_counter_update_total", Name: "counters", Algo: collectorapi.Incremental}, |
| 99 | {ID: "vnode_set_update_total", Name: "sets", Algo: collectorapi.Incremental}, |
| 100 | {ID: "vnode_map_update_total", Name: "maps", Algo: collectorapi.Incremental}, |
| 101 | }, |
| 102 | } |
| 103 | searchQueriesChart = collectorapi.Chart{ |
| 104 | ID: "dt_vnode_updates", |
| 105 | Title: "Search queries on the node", |
| 106 | Units: "queries/s", |
| 107 | Fam: "throughput", |
| 108 | Ctx: "riak.search", |
| 109 | Priority: prioSearchQueries, |
| 110 | Dims: collectorapi.Dims{ |
| 111 | {ID: "search_query_throughput_count", Name: "queries", Algo: collectorapi.Incremental}, |
| 112 | }, |
| 113 | } |
| 114 | searchDocumentsChart = collectorapi.Chart{ |
| 115 | ID: "search_documents", |
| 116 | Title: "Documents indexed by search", |
| 117 | Units: "documents/s", |
| 118 | Fam: "throughput", |
| 119 | Ctx: "riak.search.documents", |
| 120 | Priority: prioSearchDocuments, |
| 121 | Dims: collectorapi.Dims{ |
| 122 | {ID: "search_index_throughput_count", Name: "indexed", Algo: collectorapi.Incremental}, |
| 123 | }, |
| 124 | } |
| 125 | consistentOperationsChart = collectorapi.Chart{ |
| 126 | ID: "consistent_operations", |
| 127 | Title: "Consistent node operations", |
| 128 | Units: "operations/s", |
| 129 | Fam: "throughput", |
| 130 | Ctx: "riak.consistent.operations", |
| 131 | Priority: prioConsistentOperations, |
| 132 | Dims: collectorapi.Dims{ |
| 133 | {ID: "consistent_gets_total", Name: "gets", Algo: collectorapi.Incremental}, |
| 134 | {ID: "consistent_puts_total", Name: "puts", Algo: collectorapi.Incremental}, |
| 135 | }, |
| 136 | } |
| 137 | ) |
| 138 | |
| 139 | /* |
| 140 | Latency metrics |
| 141 | https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#throughput-metrics |
| 142 | |
| 143 | Collected for the past minute in milliseconds and |
| 144 | returned from Riak in microseconds. |
| 145 | */ |
| 146 | var ( |
| 147 | kvLatencyGetChart = collectorapi.Chart{ |
| 148 | ID: "kv_latency_get", |
| 149 | Title: "Time between reception of a client GET request and subsequent response to client", |
| 150 | Units: "ms", |
| 151 | Fam: "latency", |
| 152 | Ctx: "riak.kv.latency.get", |
| 153 | Priority: prioKvLatencyGet, |
| 154 | Dims: collectorapi.Dims{ |
| 155 | {ID: "node_get_fsm_time_mean", Name: "mean", Div: 1000}, |
| 156 | {ID: "node_get_fsm_time_median", Name: "median", Div: 1000}, |
| 157 | {ID: "node_get_fsm_time_95", Name: "95", Div: 1000}, |
| 158 | {ID: "node_get_fsm_time_99", Name: "99", Div: 1000}, |
| 159 | {ID: "node_get_fsm_time_100", Name: "100", Div: 1000}, |
| 160 | }, |
| 161 | } |
| 162 | kvLatencyPutChart = collectorapi.Chart{ |
| 163 | ID: "kv_latency_put", |
| 164 | Title: "Time between reception of a client PUT request and subsequent response to client", |
| 165 | Units: "ms", |
| 166 | Fam: "latency", |
| 167 | Ctx: "riak.kv.latency.put", |
| 168 | Priority: prioKvLatencyPut, |
| 169 | Dims: collectorapi.Dims{ |
| 170 | {ID: "node_put_fsm_time_mean", Name: "mean", Div: 1000}, |
| 171 | {ID: "node_put_fsm_time_median", Name: "median", Div: 1000}, |
| 172 | {ID: "node_put_fsm_time_95", Name: "95", Div: 1000}, |
| 173 | {ID: "node_put_fsm_time_99", Name: "99", Div: 1000}, |
| 174 | {ID: "node_put_fsm_time_100", Name: "100", Div: 1000}, |
| 175 | }, |
| 176 | } |
| 177 | dtLatencyCounterChart = collectorapi.Chart{ |
| 178 | ID: "dt_latency_counter", |
| 179 | Title: "Time it takes to perform an Update Counter operation", |
| 180 | Units: "ms", |
| 181 | Fam: "latency", |
| 182 | Ctx: "riak.dt.latency.counter_merge", |
| 183 | Priority: prioDtLatencyCounter, |
| 184 | Dims: collectorapi.Dims{ |
| 185 | {ID: "object_counter_merge_time_mean", Name: "mean", Div: 1000}, |
| 186 | {ID: "object_counter_merge_time_median", Name: "median", Div: 1000}, |
| 187 | {ID: "object_counter_merge_time_95", Name: "95", Div: 1000}, |
| 188 | {ID: "object_counter_merge_time_99", Name: "99", Div: 1000}, |
| 189 | {ID: "object_counter_merge_time_100", Name: "100", Div: 1000}, |
| 190 | }, |
| 191 | } |
| 192 | dtLatencySetChart = collectorapi.Chart{ |
| 193 | ID: "dt_latency_counter", |
| 194 | Title: "Time it takes to perform an Update Set operation", |
| 195 | Units: "ms", |
| 196 | Fam: "latency", |
| 197 | Ctx: "riak.dt.latency.set_merge", |
| 198 | Priority: prioDtLatencySet, |
| 199 | Dims: collectorapi.Dims{ |
| 200 | {ID: "object_set_merge_time_mean", Name: "mean", Div: 1000}, |
| 201 | {ID: "object_set_merge_time_median", Name: "median", Div: 1000}, |
| 202 | {ID: "object_set_merge_time_95", Name: "95", Div: 1000}, |
| 203 | {ID: "object_set_merge_time_99", Name: "99", Div: 1000}, |
| 204 | {ID: "object_set_merge_time_100", Name: "100", Div: 1000}, |
| 205 | }, |
| 206 | } |
| 207 | dtLatencyMapChart = collectorapi.Chart{ |
| 208 | ID: "dt_latency_map", |
| 209 | Title: "Time it takes to perform an Update Map operation", |
| 210 | Units: "ms", |
| 211 | Fam: "latency", |
| 212 | Ctx: "riak.dt.latency.map_merge", |
| 213 | Priority: prioDtLatencyMap, |
| 214 | Dims: collectorapi.Dims{ |
| 215 | {ID: "object_map_merge_time_mean", Name: "mean", Div: 1000}, |
| 216 | {ID: "object_map_merge_time_median", Name: "median", Div: 1000}, |
| 217 | {ID: "object_map_merge_time_95", Name: "95", Div: 1000}, |
| 218 | {ID: "object_map_merge_time_99", Name: "99", Div: 1000}, |
| 219 | {ID: "object_map_merge_time_100", Name: "100", Div: 1000}, |
| 220 | }, |
| 221 | } |
| 222 | searchLatencyQueryChart = collectorapi.Chart{ |
| 223 | ID: "search_latency_query", |
| 224 | Title: "Search query latency", |
| 225 | Units: "ms", |
| 226 | Fam: "latency", |
| 227 | Ctx: "riak.search.latency.query", |
| 228 | Priority: prioSearchLatencyQuery, |
| 229 | Dims: collectorapi.Dims{ |
| 230 | {ID: "search_query_latency_median", Name: "median", Div: 1000}, |
| 231 | {ID: "search_query_latency_min", Name: "min", Div: 1000}, |
| 232 | {ID: "search_query_latency_95", Name: "95", Div: 1000}, |
| 233 | {ID: "search_query_latency_99", Name: "99", Div: 1000}, |
| 234 | {ID: "search_query_latency_999", Name: "999", Div: 1000}, |
| 235 | {ID: "search_query_latency_max", Name: "max", Div: 1000}, |
| 236 | }, |
| 237 | } |
| 238 | searchLatencyIndexChart = collectorapi.Chart{ |
| 239 | ID: "search_latency_index", |
| 240 | Title: "Time it takes Search to index a new document", |
| 241 | Units: "ms", |
| 242 | Fam: "latency", |
| 243 | Ctx: "riak.search.latency.index", |
| 244 | Priority: prioSearchLatencyIndex, |
| 245 | Dims: collectorapi.Dims{ |
| 246 | {ID: "search_index_latency_median", Name: "median", Div: 1000}, |
| 247 | {ID: "search_index_latency_min", Name: "min", Div: 1000}, |
| 248 | {ID: "search_index_latency_95", Name: "95", Div: 1000}, |
| 249 | {ID: "search_index_latency_99", Name: "99", Div: 1000}, |
| 250 | {ID: "search_index_latency_999", Name: "999", Div: 1000}, |
| 251 | {ID: "search_index_latency_max", Name: "max", Div: 1000}, |
| 252 | }, |
| 253 | } |
| 254 | consistentLatencyGetChart = collectorapi.Chart{ |
| 255 | ID: "consistent_latency_get", |
| 256 | Title: "Strongly consistent read latency", |
| 257 | Units: "ms", |
| 258 | Fam: "latency", |
| 259 | Ctx: "riak.consistent.latency.get", |
| 260 | Priority: prioConsistentLatencyGet, |
| 261 | Dims: collectorapi.Dims{ |
| 262 | {ID: "consistent_get_time_mean", Name: "mean", Div: 1000}, |
| 263 | {ID: "consistent_get_time_median", Name: "median", Div: 1000}, |
| 264 | {ID: "consistent_get_time_95", Name: "95", Div: 1000}, |
| 265 | {ID: "consistent_get_time_99", Name: "99", Div: 1000}, |
| 266 | {ID: "consistent_get_time_100", Name: "100", Div: 1000}, |
| 267 | }, |
| 268 | } |
| 269 | consistentLatencyPutChart = collectorapi.Chart{ |
| 270 | ID: "consistent_latency_put", |
| 271 | Title: "Strongly consistent write latency", |
| 272 | Units: "ms", |
| 273 | Fam: "latency", |
| 274 | Ctx: "riak.consistent.latency.put", |
| 275 | Priority: prioConsistentLatencyPut, |
| 276 | Dims: collectorapi.Dims{ |
| 277 | {ID: "consistent_put_time_mean", Name: "mean", Div: 1000}, |
| 278 | {ID: "consistent_put_time_median", Name: "median", Div: 1000}, |
| 279 | {ID: "consistent_put_time_95", Name: "95", Div: 1000}, |
| 280 | {ID: "consistent_put_time_99", Name: "99", Div: 1000}, |
| 281 | {ID: "consistent_put_time_100", Name: "100", Div: 1000}, |
| 282 | }, |
| 283 | } |
| 284 | ) |
| 285 | |
| 286 | /* |
| 287 | Erlang's resource usage metrics |
| 288 | https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#erlang-resource-usage-metrics |
| 289 | |
| 290 | Processes collected as a gauge. |
| 291 | Memory collected as Megabytes, returned as bytes from Riak. |
| 292 | */ |
| 293 | var ( |
| 294 | vmProcessesCountChart = collectorapi.Chart{ |
| 295 | ID: "vm_processes", |
| 296 | Title: "Total processes running in the Erlang VM", |
| 297 | Units: "processes", |
| 298 | Fam: "vm", |
| 299 | Ctx: "riak.vm.processes.count", |
| 300 | Priority: prioVmProcessesCount, |
| 301 | Dims: collectorapi.Dims{ |
| 302 | {ID: "sys_processes", Name: "processes"}, |
| 303 | }, |
| 304 | } |
| 305 | vmProcessesMemoryChart = collectorapi.Chart{ |
| 306 | ID: "vm_processes", |
| 307 | Title: "Memory allocated & used by Erlang processes", |
| 308 | Units: "bytes", |
| 309 | Fam: "vm", |
| 310 | Ctx: "riak.vm.processes.memory", |
| 311 | Priority: prioVmProcessesMemory, |
| 312 | Dims: collectorapi.Dims{ |
| 313 | {ID: "memory_processes", Name: "allocated"}, |
| 314 | {ID: "memory_processes_used", Name: "used"}, |
| 315 | }, |
| 316 | } |
| 317 | ) |
| 318 | |
| 319 | /* |
| 320 | General Riak Load / Health metrics |
| 321 | https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#general-riak-load-health-metrics |
| 322 | */ |
| 323 | var ( |
| 324 | // General Riak Load / Health metrics |
| 325 | // https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#general-riak-load-health-metrics |
| 326 | // Collected by Riak over the past minute |
| 327 | |
| 328 | kvSiblingsEncounteredGetChart = collectorapi.Chart{ |
| 329 | ID: "kv_siblings_encountered_get", |
| 330 | Title: "Siblings encountered during GET operations by this node during the past minute", |
| 331 | Units: "siblings", |
| 332 | Fam: "load", |
| 333 | Ctx: "riak.kv.siblings_encountered.get", |
| 334 | Priority: prioKvSiblingsEncounteredGet, |
| 335 | Dims: collectorapi.Dims{ |
| 336 | {ID: "node_get_fsm_siblings_mean", Name: "mean"}, |
| 337 | {ID: "node_get_fsm_siblings_median", Name: "median"}, |
| 338 | {ID: "node_get_fsm_siblings_95", Name: "95"}, |
| 339 | {ID: "node_get_fsm_siblings_99", Name: "99"}, |
| 340 | {ID: "node_get_fsm_siblings_100", Name: "100"}, |
| 341 | }, |
| 342 | } |
| 343 | kvObjectSizeGetChart = collectorapi.Chart{ |
| 344 | ID: "kv_siblings_encountered_get", |
| 345 | Title: "Object size encountered by this node during the past minute", |
| 346 | Units: "bytes", |
| 347 | Fam: "load", |
| 348 | Ctx: "riak.kv.objsize.get", |
| 349 | Priority: prioKvObjSizeGet, |
| 350 | Dims: collectorapi.Dims{ |
| 351 | {ID: "node_get_fsm_objsize_mean", Name: "mean"}, |
| 352 | {ID: "node_get_fsm_objsize_median", Name: "median"}, |
| 353 | {ID: "node_get_fsm_objsize_95", Name: "95"}, |
| 354 | {ID: "node_get_fsm_objsize_99", Name: "99"}, |
| 355 | {ID: "node_get_fsm_objsize_100", Name: "100"}, |
| 356 | }, |
| 357 | } |
| 358 | searchVnodeqSizeChart = collectorapi.Chart{ |
| 359 | ID: "kv_siblings_encountered_get", |
| 360 | Title: "Unprocessed messages in the vnode message queues of Search in the past minute", |
| 361 | Units: "messages", |
| 362 | Fam: "load", |
| 363 | Ctx: "riak.search.vnodeq_size", |
| 364 | Priority: prioSearchVnodeqSize, |
| 365 | Dims: collectorapi.Dims{ |
| 366 | {ID: "riak_search_vnodeq_mean", Name: "mean"}, |
| 367 | {ID: "riak_search_vnodeq_median", Name: "median"}, |
| 368 | {ID: "riak_search_vnodeq_95", Name: "95"}, |
| 369 | {ID: "riak_search_vnodeq_99", Name: "99"}, |
| 370 | {ID: "riak_search_vnodeq_100", Name: "100"}, |
| 371 | }, |
| 372 | } |
| 373 | |
| 374 | // General Riak Search Load / Health metrics |
| 375 | // https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#general-riak-search-load-health-metrics |
| 376 | // https://docs.riak.com/riak/kv/latest/using/reference/statistics-monitoring/index.html#general-riak-search-load-health-metrics |
| 377 | // Reported as counters. |
| 378 | |
| 379 | searchIndexErrorsChart = collectorapi.Chart{ |
| 380 | ID: "search_index_errors", |
| 381 | Title: "Errors encountered by Search", |
| 382 | Units: "errors", |
| 383 | Fam: "load", |
| 384 | Ctx: "riak.search.index.errors", |
| 385 | Priority: prioSearchIndexErrors, |
| 386 | Dims: collectorapi.Dims{ |
| 387 | {ID: "search_index_fail_count", Name: "index_fail"}, |
| 388 | {ID: "search_index_bad_entry_count", Name: "bad_entry"}, |
| 389 | {ID: "search_index_extract_fail_count", Name: "extract_fail"}, |
| 390 | }, |
| 391 | } |
| 392 | corePbsChart = collectorapi.Chart{ |
| 393 | ID: "core_pbc", |
| 394 | Title: "Protocol buffer connections by status", |
| 395 | Units: "connections", |
| 396 | Fam: "load", |
| 397 | Ctx: "riak.core.protobuf_connections", |
| 398 | Priority: prioCorePbc, |
| 399 | Dims: collectorapi.Dims{ |
| 400 | {ID: "pbc_active", Name: "active"}, |
| 401 | }, |
| 402 | } |
| 403 | coreRepairsChart = collectorapi.Chart{ |
| 404 | ID: "core_repairs", |
| 405 | Title: "Number of repair operations this node has coordinated", |
| 406 | Units: "repairs", |
| 407 | Fam: "load", |
| 408 | Ctx: "riak.core.protobuf_connections", |
| 409 | Priority: prioCoreRepairs, |
| 410 | Dims: collectorapi.Dims{ |
| 411 | {ID: "read_repairs", Name: "read"}, |
| 412 | }, |
| 413 | } |
| 414 | coreFsmActiveChart = collectorapi.Chart{ |
| 415 | ID: "core_fsm_active", |
| 416 | Title: "Active finite state machines by kind", |
| 417 | Units: "fsms", |
| 418 | Fam: "load", |
| 419 | Ctx: "riak.core.fsm_active", |
| 420 | Priority: prioCoreFsmActive, |
| 421 | Dims: collectorapi.Dims{ |
| 422 | {ID: "node_get_fsm_active", Name: "get"}, |
| 423 | {ID: "node_put_fsm_active", Name: "put"}, |
| 424 | {ID: "index_fsm_active", Name: "secondary_index"}, |
| 425 | {ID: "list_fsm_active", Name: "list_keys"}, |
| 426 | }, |
| 427 | } |
| 428 | coreFsmRejectedChart = collectorapi.Chart{ |
| 429 | ID: "core_fsm_rejected", |
| 430 | Title: "Finite state machines being rejected by Sidejobs overload protection", |
| 431 | Units: "fsms", |
| 432 | Fam: "load", |
| 433 | Ctx: "riak.core.fsm_rejected", |
| 434 | Priority: prioCoreFsmREjected, |
| 435 | Dims: collectorapi.Dims{ |
| 436 | {ID: "node_get_fsm_rejected", Name: "get"}, |
| 437 | {ID: "node_put_fsm_rejected", Name: "put"}, |
| 438 | }, |
| 439 | } |
| 440 | ) |
| 441 | |
| 442 | func (c *Collector) adjustCharts(mx map[string]int64) { |
| 443 | var i int |
| 444 | for _, chart := range *c.Charts() { |
| 445 | chart.Dims = slices.DeleteFunc(chart.Dims, func(dim *collectorapi.Dim) bool { |
| 446 | _, ok := mx[dim.ID] |
| 447 | if !ok { |
| 448 | c.Debugf("removing dimension '%s' from chart '%s': metric not found", dim.ID, chart.ID) |
| 449 | } |
| 450 | return !ok |
| 451 | }) |
| 452 | |
| 453 | if len(chart.Dims) == 0 { |
| 454 | c.Debugf("removing chart '%s': no metrics found", chart.ID) |
| 455 | continue |
| 456 | } |
| 457 | |
| 458 | (*c.Charts())[i] = chart |
| 459 | i++ |
| 460 | } |
| 461 | } |