YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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