YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
902
void initSqlServerDefaultLabels(const char *metric_node_name) {
99
902
  prometheus_attr[EXPORTED_INSTANCE] = metric_node_name;
100
902
  prometheus_attr[METRIC_TYPE] = METRIC_TYPE_SERVER;
101
902
  prometheus_attr[METRIC_ID] = METRIC_ID_YB_YSQLSERVER;
102
902
}
103
104
}  // namespace
105
106
216
static void PgMetricsHandler(const Webserver::WebRequest &req, Webserver::WebResponse *resp) {
107
216
  std::stringstream *output = &resp->output;
108
216
  JsonWriter::Mode json_mode;
109
216
  string arg = FindWithDefault(req.parsed_args, "compact", "false");
110
216
  json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT : JsonWriter::PRETTY;
111
112
216
  JsonWriter writer(output, json_mode);
113
216
  writer.StartArray();
114
216
  writer.StartObject();
115
216
  writer.String("type");
116
216
  writer.String("server");
117
216
  writer.String("id");
118
216
  writer.String("yb.ysqlserver");
119
216
  writer.String("metrics");
120
216
  writer.StartArray();
121
122
3.02k
  for (const auto *entry = ybpgm_table, *end = entry + ybpgm_num_entries; entry != end; ++entry) {
123
2.80k
    writer.StartObject();
124
2.80k
    writer.String("name");
125
2.80k
    writer.String(entry->name);
126
2.80k
    writer.String("count");
127
2.80k
    writer.Int64(entry->calls);
128
2.80k
    writer.String("sum");
129
2.80k
    writer.Int64(entry->total_time);
130
2.80k
    writer.String("rows");
131
2.80k
    writer.Int64(entry->rows);
132
2.80k
    writer.EndObject();
133
2.80k
  }
134
135
216
  writer.EndArray();
136
216
  writer.EndObject();
137
216
  writer.EndArray();
138
216
}
139
140
0
static void DoWriteStatArrayElemToJson(JsonWriter *writer, YsqlStatementStat *stat) {
141
0
  writer->String("query");
142
0
  writer->String(stat->query);
143
144
0
  writer->String("calls");
145
0
  writer->Int64(stat->calls);
146
147
0
  writer->String("total_time");
148
0
  writer->Double(stat->total_time);
149
150
0
  writer->String("min_time");
151
0
  writer->Double(stat->min_time);
152
153
0
  writer->String("max_time");
154
0
  writer->Double(stat->max_time);
155
156
0
  writer->String("mean_time");
157
0
  writer->Double(stat->mean_time);
158
159
0
  writer->String("stddev_time");
160
  // Based on logic in pg_stat_monitor_internal().
161
0
  double stddev = (stat->calls > 1) ? (sqrt(stat->sum_var_time / stat->calls)) : 0.0;
162
0
  writer->Double(stddev);
163
164
0
  writer->String("rows");
165
0
  writer->Int64(stat->rows);
166
0
}
167
168
static void PgStatStatementsHandler(
169
0
    const Webserver::WebRequest &req, Webserver::WebResponse *resp) {
170
0
  std::stringstream *output = &resp->output;
171
0
  JsonWriter::Mode json_mode;
172
0
  string arg = FindWithDefault(req.parsed_args, "compact", "false");
173
0
  json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT : JsonWriter::PRETTY;
174
0
  JsonWriter writer(output, json_mode);
175
176
0
  writer.StartObject();
177
178
0
  writer.String("statements");
179
0
  if (pullYsqlStatementStats) {
180
0
    writer.StartArray();
181
0
    pullYsqlStatementStats(&writer);
182
0
    writer.EndArray();
183
0
  } else {
184
0
    writer.String("PG Stat Statements module is disabled.");
185
0
  }
186
187
0
  writer.EndObject();
188
0
}
189
190
static void PgStatStatementsResetHandler(
191
0
    const Webserver::WebRequest &req, Webserver::WebResponse *resp) {
192
0
  std::stringstream *output = &resp->output;
193
0
  JsonWriter::Mode json_mode;
194
0
  string arg = FindWithDefault(req.parsed_args, "compact", "false");
195
0
  json_mode = ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT : JsonWriter::PRETTY;
196
0
  JsonWriter writer(output, json_mode);
197
198
0
  writer.StartObject();
199
200
0
  writer.String("statements");
201
0
  if (resetYsqlStatementStats) {
202
0
    resetYsqlStatementStats();
203
0
    writer.String("PG Stat Statements reset.");
204
0
  } else {
205
0
    writer.String("PG Stat Statements module is disabled.");
206
0
  }
207
208
0
  writer.EndObject();
209
0
}
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
0
void WriteStatArrayElemToJson(void *p1, void *p2) {
321
0
  JsonWriter *writer = static_cast<JsonWriter *>(p1);
322
0
  YsqlStatementStat *stat = static_cast<YsqlStatementStat *>(p2);
323
324
0
  writer->StartObject();
325
0
  DoWriteStatArrayElemToJson(writer, stat);
326
0
  writer->EndObject();
327
0
}
328
329
902
WebserverWrapper *CreateWebserver(char *listen_addresses, int port) {
330
902
  WebserverOptions opts;
331
902
  opts.bind_interface = listen_addresses;
332
902
  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
902
  opts.num_worker_threads = 1;
336
902
  return reinterpret_cast<WebserverWrapper *>(new Webserver(opts, "Postgres webserver"));
337
902
}
338
339
902
void RegisterMetrics(ybpgmEntry *tab, int num_entries, char *metric_node_name) {
340
902
  ybpgm_table = tab;
341
902
  ybpgm_num_entries = num_entries;
342
902
  initSqlServerDefaultLabels(metric_node_name);
343
902
}
344
345
902
void RegisterGetYsqlStatStatements(void (*getYsqlStatementStats)(void *)) {
346
902
  pullYsqlStatementStats = getYsqlStatementStats;
347
902
}
348
349
902
void RegisterResetYsqlStatStatements(void (*fn)()) {
350
902
    resetYsqlStatementStats = fn;
351
902
}
352
353
void RegisterRpczEntries(
354
902
    postgresCallbacks *callbacks, int *num_backends_ptr, rpczEntry **rpczEntriesPointer) {
355
902
  pgCallbacks = *callbacks;
356
902
  num_backends = num_backends_ptr;
357
902
  rpczResultPointer = rpczEntriesPointer;
358
902
}
359
360
902
YBCStatus StartWebserver(WebserverWrapper *webserver_wrapper) {
361
902
  Webserver *webserver = reinterpret_cast<Webserver *>(webserver_wrapper);
362
902
  webserver->RegisterPathHandler("/metrics", "Metrics", PgMetricsHandler, false, false);
363
902
  webserver->RegisterPathHandler("/jsonmetricz", "Metrics", PgMetricsHandler, false, false);
364
902
  webserver->RegisterPathHandler(
365
902
      "/prometheus-metrics", "Metrics", PgPrometheusMetricsHandler, false, false);
366
902
  webserver->RegisterPathHandler("/rpcz", "RPCs in progress", PgRpczHandler, false, false);
367
902
  webserver->RegisterPathHandler(
368
902
      "/statements", "PG Stat Statements", PgStatStatementsHandler, false, false);
369
902
  webserver->RegisterPathHandler(
370
902
      "/statements-reset", "Reset PG Stat Statements", PgStatStatementsResetHandler, false, false);
371
902
  return ToYBCStatus(WithMaskedYsqlSignals([webserver]() { return webserver->Start(); }));
372
902
}
373
}  // extern "C"
374
375
}  // namespace yb