/Users/deen/code/yugabyte-db/src/yb/server/pgsql_webserver_wrapper.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | |
13 | | #include "yb/server/pgsql_webserver_wrapper.h" |
14 | | |
15 | | #include <math.h> |
16 | | |
17 | | #include <map> |
18 | | |
19 | | #include "yb/common/ybc-internal.h" |
20 | | |
21 | | #include "yb/gutil/map-util.h" |
22 | | |
23 | | #include "yb/server/webserver.h" |
24 | | |
25 | | #include "yb/util/jsonwriter.h" |
26 | | #include "yb/util/metrics_writer.h" |
27 | | #include "yb/util/signal_util.h" |
28 | | #include "yb/util/status_log.h" |
29 | | |
30 | | namespace yb { |
31 | | DECLARE_string(metric_node_name); |
32 | | |
33 | | static ybpgmEntry *ybpgm_table; |
34 | | static int ybpgm_num_entries; |
35 | | static int *num_backends; |
36 | | MetricEntity::AttributeMap prometheus_attr; |
37 | | static void (*pullYsqlStatementStats)(void *); |
38 | | static void (*resetYsqlStatementStats)(); |
39 | | static rpczEntry **rpczResultPointer; |
40 | | |
41 | | static postgresCallbacks pgCallbacks; |
42 | | |
43 | | static const char *EXPORTED_INSTANCE = "exported_instance"; |
44 | | static const char *METRIC_TYPE = "metric_type"; |
45 | | static const char *METRIC_ID = "metric_id"; |
46 | | |
47 | | static const char *METRIC_TYPE_SERVER = "server"; |
48 | | static const char *METRIC_ID_YB_YSQLSERVER = "yb.ysqlserver"; |
49 | | |
50 | | static const char *PSQL_SERVER_CONNECTION_TOTAL = "yb_ysqlserver_connection_total"; |
51 | | static const char *PSQL_SERVER_CONNECTION = "yb_ysqlserver_connection_info"; |
52 | | |
53 | | static const char *CONN_BACKEND_TYPE = "backend_type"; |
54 | | static const char *CONN_BACKEND_STATUS = "backend_status"; |
55 | | static const char *CONN_APPLICATION_NAME = "application_name"; |
56 | | |
57 | | namespace { |
58 | | // A helper function to init an empty AttributeMap and fills |
59 | | // it with proper default lables. |
60 | 0 | MetricEntity::AttributeMap initEmptyAttributes() { |
61 | 0 | MetricEntity::AttributeMap connAttri; |
62 | 0 | connAttri[EXPORTED_INSTANCE] = prometheus_attr[EXPORTED_INSTANCE]; |
63 | 0 | connAttri[METRIC_TYPE] = prometheus_attr[METRIC_TYPE]; |
64 | 0 | connAttri[METRIC_ID] = prometheus_attr[METRIC_ID]; |
65 | 0 | return connAttri; |
66 | 0 | } |
67 | | |
68 | 0 | void emitConnectionMetrics(PrometheusWriter *pwriter) { |
69 | 0 | pgCallbacks.pullRpczEntries(); |
70 | 0 | rpczEntry *entry = *rpczResultPointer; |
71 | |
|
72 | 0 | uint64_t tot_connections = 0; |
73 | 0 | for (int i = 0; i < *num_backends; ++i, ++entry) { |
74 | 0 | if (entry->proc_id > 0) { |
75 | 0 | auto connAttri = initEmptyAttributes(); |
76 | |
|
77 | 0 | connAttri[CONN_BACKEND_TYPE] = entry->backend_type; |
78 | 0 | connAttri[CONN_BACKEND_STATUS] = entry->backend_status; |
79 | 0 | connAttri[CONN_APPLICATION_NAME] = entry->application_name; |
80 | |
|
81 | 0 | std::ostringstream errMsg; |
82 | 0 | errMsg << "Cannot publish connection metric to Promethesu-metrics endpoint for DB: " |
83 | 0 | << (entry->db_name ? entry->db_name : "Unknown DB"); |
84 | |
|
85 | 0 | WARN_NOT_OK( |
86 | 0 | pwriter->WriteSingleEntryNonTable(connAttri, PSQL_SERVER_CONNECTION, 1), errMsg.str()); |
87 | 0 | tot_connections++; |
88 | 0 | } |
89 | 0 | } |
90 | |
|
91 | 0 | WARN_NOT_OK( |
92 | 0 | pwriter->WriteSingleEntryNonTable( |
93 | 0 | prometheus_attr, PSQL_SERVER_CONNECTION_TOTAL, tot_connections), |
94 | 0 | "Cannot publish connection count metrics to Prometheus-metrics endpoint"); |
95 | 0 | pgCallbacks.freeRpczEntries(); |
96 | 0 | } |
97 | | |
98 | 1.99k | void initSqlServerDefaultLabels(const char *metric_node_name) { |
99 | 1.99k | prometheus_attr[EXPORTED_INSTANCE] = metric_node_name; |
100 | 1.99k | prometheus_attr[METRIC_TYPE] = METRIC_TYPE_SERVER; |
101 | 1.99k | prometheus_attr[METRIC_ID] = METRIC_ID_YB_YSQLSERVER; |
102 | 1.99k | } |
103 | | |
104 | | } // namespace |
105 | | |
106 | 2.12k | static void PgMetricsHandler(const Webserver::WebRequest &req, Webserver::WebResponse *resp) { |
107 | 2.12k | std::stringstream *output = &resp->output; |
108 | 2.12k | JsonWriter::Mode json_mode; |
109 | 2.12k | string arg = FindWithDefault(req.parsed_args, "compact", "false"); |
110 | 2.12k | json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT0 : JsonWriter::PRETTY; |
111 | | |
112 | 2.12k | JsonWriter writer(output, json_mode); |
113 | 2.12k | writer.StartArray(); |
114 | 2.12k | writer.StartObject(); |
115 | 2.12k | writer.String("type"); |
116 | 2.12k | writer.String("server"); |
117 | 2.12k | writer.String("id"); |
118 | 2.12k | writer.String("yb.ysqlserver"); |
119 | 2.12k | writer.String("metrics"); |
120 | 2.12k | writer.StartArray(); |
121 | | |
122 | 29.7k | for (const auto *entry = ybpgm_table, *end = entry + ybpgm_num_entries; entry != end; ++entry27.6k ) { |
123 | 27.6k | writer.StartObject(); |
124 | 27.6k | writer.String("name"); |
125 | 27.6k | writer.String(entry->name); |
126 | 27.6k | writer.String("count"); |
127 | 27.6k | writer.Int64(entry->calls); |
128 | 27.6k | writer.String("sum"); |
129 | 27.6k | writer.Int64(entry->total_time); |
130 | 27.6k | writer.String("rows"); |
131 | 27.6k | writer.Int64(entry->rows); |
132 | 27.6k | writer.EndObject(); |
133 | 27.6k | } |
134 | | |
135 | 2.12k | writer.EndArray(); |
136 | 2.12k | writer.EndObject(); |
137 | 2.12k | writer.EndArray(); |
138 | 2.12k | } |
139 | | |
140 | 283 | static void DoWriteStatArrayElemToJson(JsonWriter *writer, YsqlStatementStat *stat) { |
141 | 283 | writer->String("query"); |
142 | 283 | writer->String(stat->query); |
143 | | |
144 | 283 | writer->String("calls"); |
145 | 283 | writer->Int64(stat->calls); |
146 | | |
147 | 283 | writer->String("total_time"); |
148 | 283 | writer->Double(stat->total_time); |
149 | | |
150 | 283 | writer->String("min_time"); |
151 | 283 | writer->Double(stat->min_time); |
152 | | |
153 | 283 | writer->String("max_time"); |
154 | 283 | writer->Double(stat->max_time); |
155 | | |
156 | 283 | writer->String("mean_time"); |
157 | 283 | writer->Double(stat->mean_time); |
158 | | |
159 | 283 | writer->String("stddev_time"); |
160 | | // Based on logic in pg_stat_monitor_internal(). |
161 | 283 | double stddev = (stat->calls > 1) ? (sqrt(stat->sum_var_time / stat->calls))81 : 0.0202 ; |
162 | 283 | writer->Double(stddev); |
163 | | |
164 | 283 | writer->String("rows"); |
165 | 283 | writer->Int64(stat->rows); |
166 | 283 | } |
167 | | |
168 | | static void PgStatStatementsHandler( |
169 | 144 | const Webserver::WebRequest &req, Webserver::WebResponse *resp) { |
170 | 144 | std::stringstream *output = &resp->output; |
171 | 144 | JsonWriter::Mode json_mode; |
172 | 144 | string arg = FindWithDefault(req.parsed_args, "compact", "false"); |
173 | 144 | json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT0 : JsonWriter::PRETTY; |
174 | 144 | JsonWriter writer(output, json_mode); |
175 | | |
176 | 144 | writer.StartObject(); |
177 | | |
178 | 144 | writer.String("statements"); |
179 | 144 | if (pullYsqlStatementStats) { |
180 | 144 | writer.StartArray(); |
181 | 144 | pullYsqlStatementStats(&writer); |
182 | 144 | writer.EndArray(); |
183 | 144 | } else { |
184 | 0 | writer.String("PG Stat Statements module is disabled."); |
185 | 0 | } |
186 | | |
187 | 144 | writer.EndObject(); |
188 | 144 | } |
189 | | |
190 | | static void PgStatStatementsResetHandler( |
191 | 3 | const Webserver::WebRequest &req, Webserver::WebResponse *resp) { |
192 | 3 | std::stringstream *output = &resp->output; |
193 | 3 | JsonWriter::Mode json_mode; |
194 | 3 | string arg = FindWithDefault(req.parsed_args, "compact", "false"); |
195 | 3 | json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT0 : JsonWriter::PRETTY; |
196 | 3 | JsonWriter writer(output, json_mode); |
197 | | |
198 | 3 | writer.StartObject(); |
199 | | |
200 | 3 | writer.String("statements"); |
201 | 3 | if (resetYsqlStatementStats) { |
202 | 3 | resetYsqlStatementStats(); |
203 | 3 | writer.String("PG Stat Statements reset."); |
204 | 3 | } else { |
205 | 0 | writer.String("PG Stat Statements module is disabled."); |
206 | 0 | } |
207 | | |
208 | 3 | writer.EndObject(); |
209 | 3 | } |
210 | | |
211 | | static void WriteAsJsonTimestampAndRunningForMs( |
212 | | JsonWriter *writer, const std::string &prefix, int64 start_timestamp, int64 snapshot_timestamp, |
213 | 0 | bool active) { |
214 | 0 | writer->String(prefix + "_start_time"); |
215 | 0 | writer->String(pgCallbacks.getTimestampTzToStr(start_timestamp)); |
216 | |
|
217 | 0 | if (!active) { |
218 | 0 | return; |
219 | 0 | } |
220 | | |
221 | 0 | writer->String(prefix + "_running_for_ms"); |
222 | 0 | writer->Int64(pgCallbacks.getTimestampTzDiffMs(start_timestamp, snapshot_timestamp)); |
223 | 0 | } |
224 | | |
225 | 0 | static void PgRpczHandler(const Webserver::WebRequest &req, Webserver::WebResponse *resp) { |
226 | 0 | std::stringstream *output = &resp->output; |
227 | 0 | pgCallbacks.pullRpczEntries(); |
228 | 0 | int64 snapshot_timestamp = pgCallbacks.getTimestampTz(); |
229 | |
|
230 | 0 | JsonWriter::Mode json_mode; |
231 | 0 | string arg = FindWithDefault(req.parsed_args, "compact", "false"); |
232 | 0 | json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT : JsonWriter::PRETTY; |
233 | 0 | JsonWriter writer(output, json_mode); |
234 | 0 | rpczEntry *entry = *rpczResultPointer; |
235 | |
|
236 | 0 | writer.StartObject(); |
237 | 0 | writer.String("connections"); |
238 | 0 | writer.StartArray(); |
239 | 0 | for (int i = 0; i < *num_backends; ++i, ++entry) { |
240 | 0 | if (entry->proc_id > 0) { |
241 | 0 | writer.StartObject(); |
242 | 0 | if (entry->db_oid) { |
243 | 0 | writer.String("db_oid"); |
244 | 0 | writer.Int64(entry->db_oid); |
245 | 0 | writer.String("db_name"); |
246 | 0 | writer.String(entry->db_name); |
247 | 0 | } |
248 | |
|
249 | 0 | if (strlen(entry->query) > 0) { |
250 | 0 | writer.String("query"); |
251 | 0 | writer.String(entry->query); |
252 | 0 | } |
253 | |
|
254 | 0 | WriteAsJsonTimestampAndRunningForMs( |
255 | 0 | &writer, "process", entry->process_start_timestamp, snapshot_timestamp, |
256 | 0 | entry->backend_active); |
257 | |
|
258 | 0 | if (entry->transaction_start_timestamp > 0) { |
259 | 0 | WriteAsJsonTimestampAndRunningForMs( |
260 | 0 | &writer, "transaction", entry->transaction_start_timestamp, snapshot_timestamp, |
261 | 0 | entry->backend_active); |
262 | 0 | } |
263 | |
|
264 | 0 | if (entry->query_start_timestamp > 0) { |
265 | 0 | WriteAsJsonTimestampAndRunningForMs( |
266 | 0 | &writer, "query", entry->query_start_timestamp, snapshot_timestamp, |
267 | 0 | entry->backend_active); |
268 | 0 | } |
269 | |
|
270 | 0 | writer.String("application_name"); |
271 | 0 | writer.String(entry->application_name); |
272 | 0 | writer.String("backend_type"); |
273 | 0 | writer.String(entry->backend_type); |
274 | 0 | writer.String("backend_status"); |
275 | 0 | writer.String(entry->backend_status); |
276 | |
|
277 | 0 | if (entry->host) { |
278 | 0 | writer.String("host"); |
279 | 0 | writer.String(entry->host); |
280 | 0 | } |
281 | |
|
282 | 0 | if (entry->port) { |
283 | 0 | writer.String("port"); |
284 | 0 | writer.String(entry->port); |
285 | 0 | } |
286 | |
|
287 | 0 | writer.EndObject(); |
288 | 0 | } |
289 | 0 | } |
290 | 0 | writer.EndArray(); |
291 | 0 | writer.EndObject(); |
292 | 0 | pgCallbacks.freeRpczEntries(); |
293 | 0 | } |
294 | | |
295 | | static void PgPrometheusMetricsHandler( |
296 | 0 | const Webserver::WebRequest &req, Webserver::WebResponse *resp) { |
297 | 0 | std::stringstream *output = &resp->output; |
298 | 0 | PrometheusWriter writer(output); |
299 | | |
300 | | // Max size of ybpgm_table name (100 incl \0 char) + max size of "_count"/"_sum" (6 excl \0). |
301 | 0 | char copied_name[106]; |
302 | 0 | for (int i = 0; i < ybpgm_num_entries; ++i) { |
303 | 0 | snprintf(copied_name, sizeof(copied_name), "%s%s", ybpgm_table[i].name, "_count"); |
304 | 0 | WARN_NOT_OK( |
305 | 0 | writer.WriteSingleEntry( |
306 | 0 | prometheus_attr, copied_name, ybpgm_table[i].calls, AggregationFunction::kSum), |
307 | 0 | "Couldn't write text metrics for Prometheus"); |
308 | 0 | snprintf(copied_name, sizeof(copied_name), "%s%s", ybpgm_table[i].name, "_sum"); |
309 | 0 | WARN_NOT_OK( |
310 | 0 | writer.WriteSingleEntry( |
311 | 0 | prometheus_attr, copied_name, ybpgm_table[i].total_time, AggregationFunction::kSum), |
312 | 0 | "Couldn't write text metrics for Prometheus"); |
313 | 0 | } |
314 | | |
315 | | // Publish sql server connection related metrics |
316 | 0 | emitConnectionMetrics(&writer); |
317 | 0 | } |
318 | | |
319 | | extern "C" { |
320 | 283 | void WriteStatArrayElemToJson(void *p1, void *p2) { |
321 | 283 | JsonWriter *writer = static_cast<JsonWriter *>(p1); |
322 | 283 | YsqlStatementStat *stat = static_cast<YsqlStatementStat *>(p2); |
323 | | |
324 | 283 | writer->StartObject(); |
325 | 283 | DoWriteStatArrayElemToJson(writer, stat); |
326 | 283 | writer->EndObject(); |
327 | 283 | } |
328 | | |
329 | 1.99k | WebserverWrapper *CreateWebserver(char *listen_addresses, int port) { |
330 | 1.99k | WebserverOptions opts; |
331 | 1.99k | opts.bind_interface = listen_addresses; |
332 | 1.99k | opts.port = port; |
333 | | // Important! Since postgres functions aren't generally thread-safe, |
334 | | // we shouldn't allow more than one worker thread at a time. |
335 | 1.99k | opts.num_worker_threads = 1; |
336 | 1.99k | return reinterpret_cast<WebserverWrapper *>(new Webserver(opts, "Postgres webserver")); |
337 | 1.99k | } |
338 | | |
339 | 1.99k | void RegisterMetrics(ybpgmEntry *tab, int num_entries, char *metric_node_name) { |
340 | 1.99k | ybpgm_table = tab; |
341 | 1.99k | ybpgm_num_entries = num_entries; |
342 | 1.99k | initSqlServerDefaultLabels(metric_node_name); |
343 | 1.99k | } |
344 | | |
345 | 1.99k | void RegisterGetYsqlStatStatements(void (*getYsqlStatementStats)(void *)) { |
346 | 1.99k | pullYsqlStatementStats = getYsqlStatementStats; |
347 | 1.99k | } |
348 | | |
349 | 1.99k | void RegisterResetYsqlStatStatements(void (*fn)()) { |
350 | 1.99k | resetYsqlStatementStats = fn; |
351 | 1.99k | } |
352 | | |
353 | | void RegisterRpczEntries( |
354 | 1.99k | postgresCallbacks *callbacks, int *num_backends_ptr, rpczEntry **rpczEntriesPointer) { |
355 | 1.99k | pgCallbacks = *callbacks; |
356 | 1.99k | num_backends = num_backends_ptr; |
357 | 1.99k | rpczResultPointer = rpczEntriesPointer; |
358 | 1.99k | } |
359 | | |
360 | 1.99k | YBCStatus StartWebserver(WebserverWrapper *webserver_wrapper) { |
361 | 1.99k | Webserver *webserver = reinterpret_cast<Webserver *>(webserver_wrapper); |
362 | 1.99k | webserver->RegisterPathHandler("/metrics", "Metrics", PgMetricsHandler, false, false); |
363 | 1.99k | webserver->RegisterPathHandler("/jsonmetricz", "Metrics", PgMetricsHandler, false, false); |
364 | 1.99k | webserver->RegisterPathHandler( |
365 | 1.99k | "/prometheus-metrics", "Metrics", PgPrometheusMetricsHandler, false, false); |
366 | 1.99k | webserver->RegisterPathHandler("/rpcz", "RPCs in progress", PgRpczHandler, false, false); |
367 | 1.99k | webserver->RegisterPathHandler( |
368 | 1.99k | "/statements", "PG Stat Statements", PgStatStatementsHandler, false, false); |
369 | 1.99k | webserver->RegisterPathHandler( |
370 | 1.99k | "/statements-reset", "Reset PG Stat Statements", PgStatStatementsResetHandler, false, false); |
371 | 1.99k | return ToYBCStatus(WithMaskedYsqlSignals([webserver]() { return webserver->Start(); })); |
372 | 1.99k | } |
373 | | } // extern "C" |
374 | | |
375 | | } // namespace yb |