| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package pgbouncer |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "strings" |
| 8 | |
| 9 | "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi" |
| 10 | ) |
| 11 | |
| 12 | const ( |
| 13 | prioClientConnectionsUtilization = collectorapi.Priority + iota |
| 14 | prioDBClientConnections |
| 15 | prioDBServerConnections |
| 16 | prioDBServerConnectionsUtilization |
| 17 | prioDBClientsWaitTime |
| 18 | prioDBClientsWaitMaxTime |
| 19 | prioDBTransactions |
| 20 | prioDBTransactionsTime |
| 21 | prioDBTransactionsAvgTime |
| 22 | prioDBQueries |
| 23 | prioDBQueriesTime |
| 24 | prioDBQueryAvgTime |
| 25 | prioDBNetworkIO |
| 26 | ) |
| 27 | |
| 28 | var ( |
| 29 | globalCharts = collectorapi.Charts{ |
| 30 | clientConnectionsUtilization.Copy(), |
| 31 | } |
| 32 | |
| 33 | clientConnectionsUtilization = collectorapi.Chart{ |
| 34 | ID: "client_connections_utilization", |
| 35 | Title: "Client connections utilization", |
| 36 | Units: "percentage", |
| 37 | Fam: "client connections", |
| 38 | Ctx: "pgbouncer.client_connections_utilization", |
| 39 | Priority: prioClientConnectionsUtilization, |
| 40 | Dims: collectorapi.Dims{ |
| 41 | {ID: "cl_conns_utilization", Name: "used"}, |
| 42 | }, |
| 43 | } |
| 44 | ) |
| 45 | |
| 46 | var ( |
| 47 | dbChartsTmpl = collectorapi.Charts{ |
| 48 | dbClientConnectionsTmpl.Copy(), |
| 49 | |
| 50 | dbServerConnectionsUtilizationTmpl.Copy(), |
| 51 | dbServerConnectionsTmpl.Copy(), |
| 52 | |
| 53 | dbClientsWaitTimeChartTmpl.Copy(), |
| 54 | dbClientMaxWaitTimeChartTmpl.Copy(), |
| 55 | |
| 56 | dbTransactionsChartTmpl.Copy(), |
| 57 | dbTransactionsTimeChartTmpl.Copy(), |
| 58 | dbTransactionAvgTimeChartTmpl.Copy(), |
| 59 | |
| 60 | dbQueriesChartTmpl.Copy(), |
| 61 | dbQueriesTimeChartTmpl.Copy(), |
| 62 | dbQueryAvgTimeChartTmpl.Copy(), |
| 63 | |
| 64 | dbNetworkIOChartTmpl.Copy(), |
| 65 | } |
| 66 | |
| 67 | dbClientConnectionsTmpl = collectorapi.Chart{ |
| 68 | ID: "db_%s_client_connections", |
| 69 | Title: "Database client connections", |
| 70 | Units: "connections", |
| 71 | Fam: "client connections", |
| 72 | Ctx: "pgbouncer.db_client_connections", |
| 73 | Priority: prioDBClientConnections, |
| 74 | Type: collectorapi.Stacked, |
| 75 | Dims: collectorapi.Dims{ |
| 76 | {ID: "db_%s_cl_active", Name: "active"}, |
| 77 | {ID: "db_%s_cl_waiting", Name: "waiting"}, |
| 78 | {ID: "db_%s_cl_cancel_req", Name: "cancel_req"}, |
| 79 | }, |
| 80 | } |
| 81 | |
| 82 | dbServerConnectionsTmpl = collectorapi.Chart{ |
| 83 | ID: "db_%s_server_connections", |
| 84 | Title: "Database server connections", |
| 85 | Units: "connections", |
| 86 | Fam: "server connections", |
| 87 | Ctx: "pgbouncer.db_server_connections", |
| 88 | Priority: prioDBServerConnections, |
| 89 | Type: collectorapi.Stacked, |
| 90 | Dims: collectorapi.Dims{ |
| 91 | {ID: "db_%s_sv_active", Name: "active"}, |
| 92 | {ID: "db_%s_sv_idle", Name: "idle"}, |
| 93 | {ID: "db_%s_sv_used", Name: "used"}, |
| 94 | {ID: "db_%s_sv_tested", Name: "tested"}, |
| 95 | {ID: "db_%s_sv_login", Name: "login"}, |
| 96 | }, |
| 97 | } |
| 98 | |
| 99 | dbServerConnectionsUtilizationTmpl = collectorapi.Chart{ |
| 100 | ID: "db_%s_server_connections_utilization", |
| 101 | Title: "Database server connections utilization", |
| 102 | Units: "percentage", |
| 103 | Fam: "server connections limit", |
| 104 | Ctx: "pgbouncer.db_server_connections_utilization", |
| 105 | Priority: prioDBServerConnectionsUtilization, |
| 106 | Dims: collectorapi.Dims{ |
| 107 | {ID: "db_%s_sv_conns_utilization", Name: "used"}, |
| 108 | }, |
| 109 | } |
| 110 | |
| 111 | dbClientsWaitTimeChartTmpl = collectorapi.Chart{ |
| 112 | ID: "db_%s_clients_wait_time", |
| 113 | Title: "Database clients wait time", |
| 114 | Units: "seconds", |
| 115 | Fam: "clients wait time", |
| 116 | Ctx: "pgbouncer.db_clients_wait_time", |
| 117 | Priority: prioDBClientsWaitTime, |
| 118 | Dims: collectorapi.Dims{ |
| 119 | {ID: "db_%s_total_wait_time", Name: "time", Algo: collectorapi.Incremental, Div: 1e6}, |
| 120 | }, |
| 121 | } |
| 122 | dbClientMaxWaitTimeChartTmpl = collectorapi.Chart{ |
| 123 | ID: "db_%s_client_max_wait_time", |
| 124 | Title: "Database client max wait time", |
| 125 | Units: "seconds", |
| 126 | Fam: "client max wait time", |
| 127 | Ctx: "pgbouncer.db_client_max_wait_time", |
| 128 | Priority: prioDBClientsWaitMaxTime, |
| 129 | Dims: collectorapi.Dims{ |
| 130 | {ID: "db_%s_maxwait", Name: "time", Div: 1e6}, |
| 131 | }, |
| 132 | } |
| 133 | |
| 134 | dbTransactionsChartTmpl = collectorapi.Chart{ |
| 135 | ID: "db_%s_transactions", |
| 136 | Title: "Database pooled SQL transactions", |
| 137 | Units: "transactions/s", |
| 138 | Fam: "transactions", |
| 139 | Ctx: "pgbouncer.db_transactions", |
| 140 | Priority: prioDBTransactions, |
| 141 | Dims: collectorapi.Dims{ |
| 142 | {ID: "db_%s_total_xact_count", Name: "transactions", Algo: collectorapi.Incremental}, |
| 143 | }, |
| 144 | } |
| 145 | dbTransactionsTimeChartTmpl = collectorapi.Chart{ |
| 146 | ID: "db_%s_transactions_time", |
| 147 | Title: "Database transactions time", |
| 148 | Units: "seconds", |
| 149 | Fam: "transactions time", |
| 150 | Ctx: "pgbouncer.db_transactions_time", |
| 151 | Priority: prioDBTransactionsTime, |
| 152 | Dims: collectorapi.Dims{ |
| 153 | {ID: "db_%s_total_xact_time", Name: "time", Algo: collectorapi.Incremental, Div: 1e6}, |
| 154 | }, |
| 155 | } |
| 156 | dbTransactionAvgTimeChartTmpl = collectorapi.Chart{ |
| 157 | ID: "db_%s_transactions_average_time", |
| 158 | Title: "Database transaction average time", |
| 159 | Units: "seconds", |
| 160 | Fam: "transaction avg time", |
| 161 | Ctx: "pgbouncer.db_transaction_avg_time", |
| 162 | Priority: prioDBTransactionsAvgTime, |
| 163 | Dims: collectorapi.Dims{ |
| 164 | {ID: "db_%s_avg_xact_time", Name: "time", Algo: collectorapi.Incremental, Div: 1e6}, |
| 165 | }, |
| 166 | } |
| 167 | |
| 168 | dbQueriesChartTmpl = collectorapi.Chart{ |
| 169 | ID: "db_%s_queries", |
| 170 | Title: "Database pooled SQL queries", |
| 171 | Units: "queries/s", |
| 172 | Fam: "queries", |
| 173 | Ctx: "pgbouncer.db_queries", |
| 174 | Priority: prioDBQueries, |
| 175 | Dims: collectorapi.Dims{ |
| 176 | {ID: "db_%s_total_query_count", Name: "queries", Algo: collectorapi.Incremental}, |
| 177 | }, |
| 178 | } |
| 179 | dbQueriesTimeChartTmpl = collectorapi.Chart{ |
| 180 | ID: "db_%s_queries_time", |
| 181 | Title: "Database queries time", |
| 182 | Units: "seconds", |
| 183 | Fam: "queries time", |
| 184 | Ctx: "pgbouncer.db_queries_time", |
| 185 | Priority: prioDBQueriesTime, |
| 186 | Dims: collectorapi.Dims{ |
| 187 | {ID: "db_%s_total_query_time", Name: "time", Algo: collectorapi.Incremental, Div: 1e6}, |
| 188 | }, |
| 189 | } |
| 190 | dbQueryAvgTimeChartTmpl = collectorapi.Chart{ |
| 191 | ID: "db_%s_query_average_time", |
| 192 | Title: "Database query average time", |
| 193 | Units: "seconds", |
| 194 | Fam: "query avg time", |
| 195 | Ctx: "pgbouncer.db_query_avg_time", |
| 196 | Priority: prioDBQueryAvgTime, |
| 197 | Dims: collectorapi.Dims{ |
| 198 | {ID: "db_%s_avg_query_time", Name: "time", Algo: collectorapi.Incremental, Div: 1e6}, |
| 199 | }, |
| 200 | } |
| 201 | |
| 202 | dbNetworkIOChartTmpl = collectorapi.Chart{ |
| 203 | ID: "db_%s_network_io", |
| 204 | Title: "Database traffic", |
| 205 | Units: "B/s", |
| 206 | Fam: "traffic", |
| 207 | Ctx: "pgbouncer.db_network_io", |
| 208 | Priority: prioDBNetworkIO, |
| 209 | Type: collectorapi.Area, |
| 210 | Dims: collectorapi.Dims{ |
| 211 | {ID: "db_%s_total_received", Name: "received", Algo: collectorapi.Incremental}, |
| 212 | {ID: "db_%s_total_sent", Name: "sent", Algo: collectorapi.Incremental, Mul: -1}, |
| 213 | }, |
| 214 | } |
| 215 | ) |
| 216 | |
| 217 | func newDatabaseCharts(dbname, pgDBName string) *collectorapi.Charts { |
| 218 | charts := dbChartsTmpl.Copy() |
| 219 | for _, c := range *charts { |
| 220 | c.ID = fmt.Sprintf(c.ID, dbname) |
| 221 | c.Labels = []collectorapi.Label{ |
| 222 | {Key: "database", Value: dbname}, |
| 223 | {Key: "postgres_database", Value: pgDBName}, |
| 224 | } |
| 225 | for _, d := range c.Dims { |
| 226 | d.ID = fmt.Sprintf(d.ID, dbname) |
| 227 | } |
| 228 | } |
| 229 | return charts |
| 230 | } |
| 231 | |
| 232 | func (c *Collector) addNewDatabaseCharts(dbname, pgDBName string) { |
| 233 | charts := newDatabaseCharts(dbname, pgDBName) |
| 234 | if err := c.Charts().Add(*charts...); err != nil { |
| 235 | c.Warning(err) |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | func (c *Collector) removeDatabaseCharts(dbname string) { |
| 240 | prefix := fmt.Sprintf("db_%s_", dbname) |
| 241 | for _, c := range *c.Charts() { |
| 242 | if strings.HasPrefix(c.ID, prefix) { |
| 243 | c.MarkRemove() |
| 244 | c.MarkNotCreated() |
| 245 | } |
| 246 | } |
| 247 | } |