badmfck-api-server 4.0.98 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -97,7 +97,7 @@ async function Initializer(services) {
97
97
  }
98
98
  exports.Initializer = Initializer;
99
99
  class APIService extends BaseService_1.BaseService {
100
- version = "4.0.98";
100
+ version = "4.1.01";
101
101
  options;
102
102
  monitor = null;
103
103
  started = new Date();
@@ -420,6 +420,10 @@ class APIService extends BaseService_1.BaseService {
420
420
  });
421
421
  }
422
422
  addNetlog(data, req, created, time) {
423
+ if (time === 0) {
424
+ time = +new Date() - created;
425
+ created = +new Date();
426
+ }
423
427
  const logItem = {
424
428
  id: nextLogID++,
425
429
  created: created,
@@ -447,7 +451,7 @@ class APIService extends BaseService_1.BaseService {
447
451
  this.addNetlog(data, req, requestTime, 0);
448
452
  return;
449
453
  }
450
- MonitorService_1.S_STAT_REGISTRATE_REQUEST.invoke(data);
454
+ MonitorService_1.S_STAT_REGISTRATE_REQUEST.invoke({ ...data, endpoint: data.endpoint ?? endpoint });
451
455
  if (this.options.postproducer) {
452
456
  try {
453
457
  data = await this.options.postproducer(req, res, data, requestTime, endpoint);
@@ -511,8 +515,6 @@ class APIService extends BaseService_1.BaseService {
511
515
  else {
512
516
  res.send(data);
513
517
  (0, LogService_1.logAPI)(this.checkDataLength(data));
514
- if (data.error)
515
- (0, LogService_1.logError)(data.error.message);
516
518
  }
517
519
  }
518
520
  }
@@ -1,4 +1,4 @@
1
- import Signal, { Req } from "badmfck-signal";
1
+ import { Req } from "badmfck-signal";
2
2
  import { BaseService } from "./BaseService";
3
3
  import { IDBAdapter } from "./db/IDBAdapter";
4
4
  export interface IDBQueryField {
@@ -76,22 +76,23 @@ export interface DBAdapterOptions {
76
76
  maxQueryTimeout?: number;
77
77
  debug?: boolean;
78
78
  }
79
- type ILongQuery = {
80
- query: string;
81
- started_at: number;
82
- finished_at: number;
83
- execution: number;
84
- };
85
- type IQueriesStat = {
86
- failed: number;
87
- success: number;
88
- failedQueries: string[];
89
- };
90
79
  export interface IPoolStatus {
91
80
  lastSuccessQueryTime: string;
92
81
  poolConnections: number;
93
82
  acquiredPoolConnections: number;
94
83
  }
84
+ export interface IDBStatFrame {
85
+ transactionsCount: number;
86
+ averageQueryTime: number;
87
+ totalQueries: number;
88
+ maxQueryTime: number;
89
+ minQueryTime: number;
90
+ failedQueries: number;
91
+ longestQueries: {
92
+ query: string;
93
+ execution: number;
94
+ }[];
95
+ }
95
96
  export declare const REQ_DB: Req<IDBQuery, IDBResult>;
96
97
  export declare const REQ_DBX: Req<IDBBulkQuery, IDBResult[]>;
97
98
  export declare const REQ_DB_TBEGIN: Req<{
@@ -105,32 +106,24 @@ export declare const REQ_DB_TROLLBACK: Req<{
105
106
  dbid: string;
106
107
  trxid: number;
107
108
  }, IDBResult>;
108
- export declare const S_DB_STAT: Signal<{
109
- dbid: string;
110
- stat: {
111
- queriesStat: {
112
- hour: string;
113
- stat: IQueriesStat;
114
- };
115
- poolStatus: IPoolStatus;
116
- longQueries: ILongQuery[];
117
- };
118
- }>;
119
109
  export declare class DBService extends BaseService {
120
110
  static allInstances: DBService[];
121
- longQueries: ILongQuery[];
122
- queriesStat: {
123
- hour: string;
124
- stat: IQueriesStat;
125
- };
126
111
  options: IDBSericeOptions;
127
112
  adapter: IDBAdapter | null;
128
113
  constructor(options: IDBSericeOptions);
129
114
  init(): Promise<void>;
130
115
  static escape(id: string, value: any): string;
131
116
  getAdapter(dbid?: string | null): IDBAdapter | IDBError;
132
- reportQuery(result: IDBResult, req: IDBQuery): Promise<void>;
117
+ static healthcheck(range?: "hour" | "day" | "week"): {
118
+ id: string;
119
+ host: string;
120
+ port: number;
121
+ poolStatus: IPoolStatus | null;
122
+ stat: (IDBStatFrame & {
123
+ slot: number;
124
+ })[] | null;
125
+ }[];
126
+ private static aggregateStat;
133
127
  finishService(): Promise<void>;
134
128
  createMysqlDatabase(): Promise<void>;
135
129
  }
136
- export {};
@@ -1,44 +1,17 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.DBService = exports.S_DB_STAT = exports.REQ_DB_TROLLBACK = exports.REQ_DB_TCOMMIT = exports.REQ_DB_TBEGIN = exports.REQ_DBX = exports.REQ_DB = void 0;
27
- const badmfck_signal_1 = __importStar(require("badmfck-signal"));
3
+ exports.DBService = exports.REQ_DB_TROLLBACK = exports.REQ_DB_TCOMMIT = exports.REQ_DB_TBEGIN = exports.REQ_DBX = exports.REQ_DB = void 0;
4
+ const badmfck_signal_1 = require("badmfck-signal");
28
5
  const BaseService_1 = require("./BaseService");
29
6
  const MysqlAdapter_1 = require("./db/MysqlAdapter");
30
- const LogService_1 = require("./LogService");
31
7
  const PostgresAdapter_1 = require("./db/PostgresAdapter");
32
8
  exports.REQ_DB = new badmfck_signal_1.Req(undefined, "REQ_DB");
33
9
  exports.REQ_DBX = new badmfck_signal_1.Req(undefined, "REQ_DBX");
34
10
  exports.REQ_DB_TBEGIN = new badmfck_signal_1.Req(undefined, "REQ_DB_TBEGIN");
35
11
  exports.REQ_DB_TCOMMIT = new badmfck_signal_1.Req(undefined, "REQ_DB_TCOMMIT");
36
12
  exports.REQ_DB_TROLLBACK = new badmfck_signal_1.Req(undefined, "REQ_DB_TROLLBACK");
37
- exports.S_DB_STAT = new badmfck_signal_1.default();
38
13
  class DBService extends BaseService_1.BaseService {
39
14
  static allInstances = [];
40
- longQueries = [];
41
- queriesStat = { hour: "00", stat: { failed: 0, success: 0, failedQueries: [] } };
42
15
  options;
43
16
  adapter = null;
44
17
  constructor(options) {
@@ -51,27 +24,6 @@ class DBService extends BaseService_1.BaseService {
51
24
  }
52
25
  }
53
26
  DBService.allInstances.push(this);
54
- setInterval(() => {
55
- (0, LogService_1.logDB)("Queries (" + options.id + ") at: " + new Date().toDateString() + " " + this.queriesStat.hour + ":--\n" + "success: " + this.queriesStat.stat.success + "\nfailed: " + this.queriesStat.stat.failed);
56
- let poolStatus = {
57
- lastSuccessQueryTime: "-1",
58
- poolConnections: -1,
59
- acquiredPoolConnections: -1
60
- };
61
- if (this.adapter) {
62
- poolStatus = this.adapter.poolStatus();
63
- (0, LogService_1.logDB)("Pool status (" + options.id + ") at: " + new Date().toDateString() + " " + new Date().toTimeString() + "\n LSQT: " + poolStatus.lastSuccessQueryTime + "\nCONN: " + poolStatus.poolConnections + "\nACONN: " + poolStatus.acquiredPoolConnections);
64
- }
65
- (0, LogService_1.logDB)(`Long queries ${options.id}\n${this.longQueries.map(val => `exec: ${val.execution} at: ${new Date(val.started_at).toISOString()}, ${val.query}`).join("\n")}`);
66
- exports.S_DB_STAT.invoke({
67
- dbid: options.id,
68
- stat: {
69
- queriesStat: this.queriesStat,
70
- poolStatus: poolStatus,
71
- longQueries: this.longQueries
72
- }
73
- });
74
- }, 1000 * 60 * 1);
75
27
  }
76
28
  async init() {
77
29
  super.init();
@@ -84,17 +36,11 @@ class DBService extends BaseService_1.BaseService {
84
36
  exports.REQ_DBX.listener = async (req) => {
85
37
  const executionStartTime = Date.now();
86
38
  if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter) {
87
- const result = await DBService.allInstances[0].adapter.bulk(req);
88
- for (let i = 0; i < result.length; i++)
89
- this.reportQuery(result[i], req.bulk[i]);
90
- return result;
39
+ return await DBService.allInstances[0].adapter.bulk(req);
91
40
  }
92
41
  for (let i of DBService.allInstances) {
93
42
  if (i.options.id === req.dbid && i.adapter) {
94
- const result = await i.adapter.bulk(req);
95
- for (let i = 0; i < result.length; i++)
96
- this.reportQuery(result[i], req.bulk[i]);
97
- return result;
43
+ return await i.adapter.bulk(req);
98
44
  }
99
45
  }
100
46
  const error = {
@@ -208,15 +154,11 @@ class DBService extends BaseService_1.BaseService {
208
154
  req.fields = JSON.parse(JSON.stringify(req.fields));
209
155
  const executionStartTime = Date.now();
210
156
  if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter) {
211
- const result = await DBService.allInstances[0].adapter.query(req);
212
- this.reportQuery(result, req);
213
- return result;
157
+ return await DBService.allInstances[0].adapter.query(req);
214
158
  }
215
159
  for (let i of DBService.allInstances) {
216
160
  if (i.options.id === req.dbid && i.adapter) {
217
- const result = await i.adapter.query(req);
218
- this.reportQuery(result, req);
219
- return result;
161
+ return await i.adapter.query(req);
220
162
  }
221
163
  }
222
164
  const error = {
@@ -234,7 +176,6 @@ class DBService extends BaseService_1.BaseService {
234
176
  finished_at: Date.now()
235
177
  }
236
178
  };
237
- this.reportQuery(errorResult, req);
238
179
  if (req.throwable)
239
180
  throw error;
240
181
  return errorResult;
@@ -283,33 +224,61 @@ class DBService extends BaseService_1.BaseService {
283
224
  };
284
225
  return error;
285
226
  }
286
- async reportQuery(result, req) {
287
- const hour = new Date().getHours().toString().padStart(2, "0");
288
- if (this.queriesStat.hour !== hour) {
289
- this.queriesStat.hour = hour;
290
- this.queriesStat.stat = { failed: 0, success: 0, failedQueries: [] };
291
- }
292
- if (result.error) {
293
- this.queriesStat.stat.failed++;
294
- this.queriesStat.stat.failedQueries.push(req.query);
295
- }
296
- else
297
- this.queriesStat.stat.success++;
298
- const execTime = result.execution.finished_at - result.execution.started_at;
299
- if (execTime && execTime > 0) {
300
- const minLong = this.longQueries.length < 20 || execTime > this.longQueries[0].execution;
301
- if (minLong) {
302
- this.longQueries.push({
303
- execution: execTime,
304
- query: req.query,
305
- started_at: result.execution.started_at,
306
- finished_at: result.execution.finished_at
307
- });
227
+ static healthcheck(range = "hour") {
228
+ return DBService.allInstances.map(i => {
229
+ const frames = i.adapter?.stat();
230
+ const stat = frames ? DBService.aggregateStat(frames, range) : null;
231
+ return {
232
+ id: i.options.id,
233
+ host: i.options.connection.host,
234
+ port: i.options.connection.port,
235
+ poolStatus: i.adapter?.poolStatus() ?? null,
236
+ stat
237
+ };
238
+ });
239
+ }
240
+ static aggregateStat(frames, range) {
241
+ const steps = { hour: 1, day: 10, week: 30 };
242
+ const limits = { hour: 60, day: 1440, week: 7200 };
243
+ const step = steps[range];
244
+ const limit = limits[range];
245
+ const sorted = Array.from(frames.entries()).sort(([a], [b]) => a - b);
246
+ const limited = sorted.slice(-limit);
247
+ const result = [];
248
+ for (let i = 0; i < limited.length; i += step) {
249
+ const chunk = limited.slice(i, i + step);
250
+ let totalQueries = 0;
251
+ let failedQueries = 0;
252
+ let transactionsCount = 0;
253
+ let weightedAvgSum = 0;
254
+ let maxQueryTime = 0;
255
+ let minQueryTime = Infinity;
256
+ const merged = [];
257
+ for (const [, frame] of chunk) {
258
+ totalQueries += frame.totalQueries;
259
+ failedQueries += frame.failedQueries;
260
+ transactionsCount += frame.transactionsCount;
261
+ weightedAvgSum += frame.averageQueryTime * frame.totalQueries;
262
+ if (frame.maxQueryTime > maxQueryTime)
263
+ maxQueryTime = frame.maxQueryTime;
264
+ if (frame.totalQueries > 0 && frame.minQueryTime < minQueryTime)
265
+ minQueryTime = frame.minQueryTime;
266
+ if (frame.longestQueries.length)
267
+ merged.push(...frame.longestQueries);
308
268
  }
309
- this.longQueries.sort((a, b) => a.execution - b.execution);
310
- if (this.longQueries.length > 20)
311
- this.longQueries = this.longQueries.slice(-20);
269
+ merged.sort((a, b) => b.execution - a.execution);
270
+ result.push({
271
+ slot: chunk[0][0],
272
+ transactionsCount,
273
+ totalQueries,
274
+ failedQueries,
275
+ averageQueryTime: totalQueries > 0 ? weightedAvgSum / totalQueries : 0,
276
+ maxQueryTime,
277
+ minQueryTime: minQueryTime === Infinity ? 0 : minQueryTime,
278
+ longestQueries: merged.slice(0, 5)
279
+ });
312
280
  }
281
+ return result;
313
282
  }
314
283
  async finishService() {
315
284
  await this.adapter?.finish();
@@ -16,6 +16,10 @@ export declare const S_STAT_REGISTRATE_SERVICE: Signal<string>;
16
16
  export declare const S_STAT_REGISTRATE_HTTP_REQUEST: Signal<{
17
17
  url: string;
18
18
  }>;
19
+ export declare const S_STAT_REGISTRATE_SQL_REQUEST: Signal<{
20
+ success: boolean;
21
+ query: string;
22
+ }>;
19
23
  export declare const REQ_STAT: Req<{
20
24
  range: "week" | "day" | "hour";
21
25
  }, IStat[]>;
@@ -32,6 +36,8 @@ type IMonitorSlot = {
32
36
  successEp: Record<string, number>;
33
37
  failEp: Record<string, number>;
34
38
  http: Record<string, number>;
39
+ success_sql: Record<string, number>;
40
+ fail_sql: Record<string, number>;
35
41
  };
36
42
  export declare class MonitorService extends BaseService {
37
43
  slots: Map<number, Map<number, IMonitorSlot>>;
@@ -47,7 +53,12 @@ export declare class MonitorService extends BaseService {
47
53
  private slotToTimestamp;
48
54
  onStatRegistrate(data: TransferPacketVO): void;
49
55
  collectUsage(): Promise<void>;
56
+ private static MAX_SLOT_KEYS;
57
+ static normalizeQuery(query: string): string;
58
+ static normalizeUrl(url: string): string;
59
+ private bumpCappedKey;
50
60
  private onHttpRequestRegistrate;
61
+ private onSQLRequestRegistrate;
51
62
  private getSlot;
52
63
  }
53
64
  export {};
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.MonitorService = exports.REQ_STAT = exports.S_STAT_REGISTRATE_HTTP_REQUEST = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
29
+ exports.MonitorService = exports.REQ_STAT = exports.S_STAT_REGISTRATE_SQL_REQUEST = exports.S_STAT_REGISTRATE_HTTP_REQUEST = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
30
30
  const badmfck_signal_1 = __importStar(require("badmfck-signal"));
31
31
  const BaseService_1 = require("./BaseService");
32
32
  const YYYYMMDDHH_1 = require("./helper/YYYYMMDDHH");
@@ -35,6 +35,7 @@ exports.S_STAT_REGISTRATE_REQUEST = new badmfck_signal_1.default();
35
35
  exports.S_STAT_REGISTRATE_CUSTOM_EVENT = new badmfck_signal_1.default();
36
36
  exports.S_STAT_REGISTRATE_SERVICE = new badmfck_signal_1.default();
37
37
  exports.S_STAT_REGISTRATE_HTTP_REQUEST = new badmfck_signal_1.default();
38
+ exports.S_STAT_REGISTRATE_SQL_REQUEST = new badmfck_signal_1.default();
38
39
  exports.REQ_STAT = new badmfck_signal_1.Req(undefined, "REQ_STAT");
39
40
  class MonitorService extends BaseService_1.BaseService {
40
41
  slots = new Map();
@@ -49,6 +50,7 @@ class MonitorService extends BaseService_1.BaseService {
49
50
  super.init();
50
51
  exports.REQ_STAT.listener = async (req) => this.getEPStat(req);
51
52
  exports.S_STAT_REGISTRATE_HTTP_REQUEST.subscribe((data) => this.onHttpRequestRegistrate(data));
53
+ exports.S_STAT_REGISTRATE_SQL_REQUEST.subscribe((data) => this.onSQLRequestRegistrate(data));
52
54
  exports.S_STAT_REGISTRATE_REQUEST.subscribe((data) => this.onStatRegistrate(data));
53
55
  }
54
56
  async getEPStat(req) {
@@ -78,7 +80,9 @@ class MonitorService extends BaseService_1.BaseService {
78
80
  memory: { rss: 0, heapUsed: 0, heapTotal: 0, external: 0 },
79
81
  successEp: {},
80
82
  failEp: {},
81
- http: {}
83
+ http: {},
84
+ success_sql: {},
85
+ fail_sql: {}
82
86
  };
83
87
  chunk.forEach(s => {
84
88
  agg.cpu += s.cpu;
@@ -93,6 +97,12 @@ class MonitorService extends BaseService_1.BaseService {
93
97
  for (const [url, count] of Object.entries(s.http)) {
94
98
  agg.http[url] = (agg.http[url] || 0) + count;
95
99
  }
100
+ for (const [query, count] of Object.entries(s.success_sql)) {
101
+ agg.success_sql[query] = (agg.success_sql[query] || 0) + count;
102
+ }
103
+ for (const [query, count] of Object.entries(s.fail_sql)) {
104
+ agg.fail_sql[query] = (agg.fail_sql[query] || 0) + count;
105
+ }
96
106
  });
97
107
  const len = chunk.length;
98
108
  agg.cpu = Number((agg.cpu / len).toFixed(2));
@@ -141,11 +151,51 @@ class MonitorService extends BaseService_1.BaseService {
141
151
  external: Math.round(mem.external / 1024 / 1024),
142
152
  };
143
153
  }
154
+ static MAX_SLOT_KEYS = 100;
155
+ static normalizeQuery(query) {
156
+ const normalized = query
157
+ .replace(/'(?:\\.|''|[^'])*'/g, "?")
158
+ .replace(/"(?:\\.|""|[^"])*"/g, "?")
159
+ .replace(/\b\d+(?:\.\d+)?\b/g, "?")
160
+ .replace(/\(\s*\?(?:\s*,\s*\?)+\s*\)/g, "(?)")
161
+ .replace(/\s+/g, " ")
162
+ .trim();
163
+ return normalized.length > 300 ? normalized.substring(0, 300) : normalized;
164
+ }
165
+ static normalizeUrl(url) {
166
+ const q = url.indexOf("?");
167
+ const h = url.indexOf("#");
168
+ let end = url.length;
169
+ if (q >= 0)
170
+ end = Math.min(end, q);
171
+ if (h >= 0)
172
+ end = Math.min(end, h);
173
+ const path = url.substring(0, end);
174
+ return path.length > 200 ? path.substring(0, 200) : path;
175
+ }
176
+ bumpCappedKey(record, key) {
177
+ if (key in record) {
178
+ record[key]++;
179
+ return;
180
+ }
181
+ if (Object.keys(record).length >= MonitorService.MAX_SLOT_KEYS) {
182
+ record["__other__"] = (record["__other__"] || 0) + 1;
183
+ return;
184
+ }
185
+ record[key] = 1;
186
+ }
144
187
  onHttpRequestRegistrate(data) {
145
188
  const date = new Date();
146
189
  const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
147
190
  const slot = this.getSlot(yyyymmddhhmm);
148
- slot.http[data.url] = (slot.http[data.url] || 0) + 1;
191
+ this.bumpCappedKey(slot.http, MonitorService.normalizeUrl(data.url));
192
+ }
193
+ onSQLRequestRegistrate(data) {
194
+ const date = new Date();
195
+ const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
196
+ const slot = this.getSlot(yyyymmddhhmm);
197
+ const key = MonitorService.normalizeQuery(data.query);
198
+ this.bumpCappedKey(data.success ? slot.success_sql : slot.fail_sql, key);
149
199
  }
150
200
  getSlot(yyyymmddhhii) {
151
201
  const yyymmdd = Math.floor(yyyymmddhhii / 100);
@@ -172,7 +222,9 @@ class MonitorService extends BaseService_1.BaseService {
172
222
  },
173
223
  successEp: {},
174
224
  failEp: {},
175
- http: {}
225
+ http: {},
226
+ success_sql: {},
227
+ fail_sql: {}
176
228
  };
177
229
  daySlots.set(yyyymmddhhii, slot);
178
230
  }
@@ -1,4 +1,4 @@
1
- import { IDBBulkQuery, IDBQuery, IDBResult, IPoolStatus } from "../DBService";
1
+ import { IDBBulkQuery, IDBQuery, IDBResult, IDBStatFrame, IPoolStatus } from "../DBService";
2
2
  export interface IDBAdapter {
3
3
  init(): Promise<void>;
4
4
  finish(): Promise<void>;
@@ -10,4 +10,5 @@ export interface IDBAdapter {
10
10
  bulk(request: IDBBulkQuery): Promise<IDBResult[]>;
11
11
  poolStatus(): IPoolStatus;
12
12
  escape(value: string): string;
13
+ stat(): Map<number, IDBStatFrame>;
13
14
  }
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { DBAdapterOptions, IDBBulkQuery, IDBError, IDBQuery, IDBQueryField, IDBResult } from "../DBService";
2
+ import { DBAdapterOptions, IDBBulkQuery, IDBError, IDBQuery, IDBQueryField, IDBResult, IDBStatFrame } from "../DBService";
3
3
  import { IDBAdapter } from "./IDBAdapter";
4
4
  import mysql from 'mysql2/promise';
5
5
  import fs from 'fs';
@@ -30,8 +30,14 @@ export declare class MysqlAdapter implements IDBAdapter {
30
30
  failReportLastAccessTime: number;
31
31
  poolConnections: number;
32
32
  acquiredPoolConnections: number;
33
+ statFrames: Map<number, IDBStatFrame>;
33
34
  constructor(options: DBAdapterOptions);
34
35
  escape(value: string): string;
36
+ stat(): Map<number, IDBStatFrame>;
37
+ private statRetentionMs;
38
+ private getOrCreateStatSlot;
39
+ collectStat(query: string, execTime: number, failed: boolean): void;
40
+ collectTransactionStat(): void;
35
41
  init(): Promise<void>;
36
42
  poolStatus(): {
37
43
  lastSuccessQueryTime: string;
@@ -10,6 +10,8 @@ const LogService_1 = require("../LogService");
10
10
  const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const crypto_1 = require("crypto");
13
+ const MonitorService_1 = require("../MonitorService");
14
+ const __1 = require("../..");
13
15
  class MysqlAdapter {
14
16
  options;
15
17
  serviceStarted = false;
@@ -28,6 +30,7 @@ class MysqlAdapter {
28
30
  failReportLastAccessTime = 0;
29
31
  poolConnections = 0;
30
32
  acquiredPoolConnections = 0;
33
+ statFrames = new Map();
31
34
  constructor(options) {
32
35
  this.options = options;
33
36
  if (this.options.host.startsWith("_"))
@@ -42,6 +45,59 @@ class MysqlAdapter {
42
45
  escape(value) {
43
46
  return promise_1.default.escape(value);
44
47
  }
48
+ stat() {
49
+ return this.statFrames;
50
+ }
51
+ statRetentionMs = 5 * 24 * 60 * 60 * 1000;
52
+ getOrCreateStatSlot(date = new Date()) {
53
+ const key = parseInt((0, __1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
54
+ let slot = this.statFrames.get(key);
55
+ if (slot)
56
+ return slot;
57
+ slot = {
58
+ transactionsCount: 0,
59
+ averageQueryTime: 0,
60
+ totalQueries: 0,
61
+ maxQueryTime: 0,
62
+ minQueryTime: 0,
63
+ failedQueries: 0,
64
+ longestQueries: []
65
+ };
66
+ this.statFrames.set(key, slot);
67
+ const cutoff = parseInt((0, __1.YYYYMMDDHH)(new Date(date.getTime() - this.statRetentionMs), "yyyymmddhhmm"));
68
+ for (const k of this.statFrames.keys()) {
69
+ if (k < cutoff)
70
+ this.statFrames.delete(k);
71
+ }
72
+ return slot;
73
+ }
74
+ collectStat(query, execTime, failed) {
75
+ const slot = this.getOrCreateStatSlot();
76
+ slot.totalQueries++;
77
+ if (failed)
78
+ slot.failedQueries++;
79
+ slot.averageQueryTime += (execTime - slot.averageQueryTime) / slot.totalQueries;
80
+ if (slot.totalQueries === 1) {
81
+ slot.maxQueryTime = execTime;
82
+ slot.minQueryTime = execTime;
83
+ }
84
+ else {
85
+ if (execTime > slot.maxQueryTime)
86
+ slot.maxQueryTime = execTime;
87
+ if (execTime < slot.minQueryTime)
88
+ slot.minQueryTime = execTime;
89
+ }
90
+ const truncated = query.length > 100 ? query.substring(0, 100) : query;
91
+ if (slot.longestQueries.length < 5 || execTime > slot.longestQueries[slot.longestQueries.length - 1].execution) {
92
+ slot.longestQueries.push({ query: truncated, execution: execTime });
93
+ slot.longestQueries.sort((a, b) => b.execution - a.execution);
94
+ if (slot.longestQueries.length > 5)
95
+ slot.longestQueries.length = 5;
96
+ }
97
+ }
98
+ collectTransactionStat() {
99
+ this.getOrCreateStatSlot().transactionsCount++;
100
+ }
45
101
  async init() {
46
102
  if (this.options.transactionFailReportDir) {
47
103
  try {
@@ -213,6 +269,7 @@ class MysqlAdapter {
213
269
  }
214
270
  trxRequest.queries.push({ sql: "START TRANSACTION", status: "ok" });
215
271
  this.transactions.push(trxRequest);
272
+ this.collectTransactionStat();
216
273
  if (this.options.debug)
217
274
  (0, LogService_1.logDB)("Transactions pool: ", this.transactions.map(t => "id:" + t.id + ", queries: " + t.queries.length));
218
275
  return {
@@ -410,8 +467,9 @@ class MysqlAdapter {
410
467
  async query(request, conn) {
411
468
  let executionStartTime = Date.now();
412
469
  const query = this.prepareQuery(request);
470
+ const isPing = query.trim().toLowerCase() === "select 1";
413
471
  if (this.options.debug || request.debug) {
414
- if (query.toLowerCase().trim().indexOf("select 1") === -1)
472
+ if (!isPing)
415
473
  (0, LogService_1.logDB)(query);
416
474
  }
417
475
  if (request.parseQueryOnly) {
@@ -468,17 +526,26 @@ class MysqlAdapter {
468
526
  this.finalizeConnection(conn);
469
527
  if (trx)
470
528
  trx.queries.push({ sql: query, status: "ok" });
529
+ const finishedAt = Date.now();
530
+ if (!isPing) {
531
+ MonitorService_1.S_STAT_REGISTRATE_SQL_REQUEST.invoke({ success: true, query });
532
+ this.collectStat(query, finishedAt - executionStartTime, false);
533
+ }
471
534
  return {
472
535
  data: result[0],
473
536
  error: null,
474
537
  total: total ? total : undefined,
475
538
  execution: {
476
539
  started_at: executionStartTime,
477
- finished_at: Date.now()
540
+ finished_at: finishedAt
478
541
  }
479
542
  };
480
543
  }
481
544
  catch (e) {
545
+ if (!isPing) {
546
+ MonitorService_1.S_STAT_REGISTRATE_SQL_REQUEST.invoke({ success: false, query });
547
+ this.collectStat(query, Date.now() - executionStartTime, true);
548
+ }
482
549
  if (!request.transactionID && doCloseConnection)
483
550
  this.finalizeConnection(conn);
484
551
  else if (request.transactionID && request.throwable) {
@@ -1,5 +1,5 @@
1
1
  import { Pool } from 'pg';
2
- import { DBAdapterOptions, IDBQuery, IDBResult, IDBBulkQuery, IPoolStatus } from '../DBService';
2
+ import { DBAdapterOptions, IDBQuery, IDBResult, IDBBulkQuery, IPoolStatus, IDBStatFrame } from '../DBService';
3
3
  import { ITransaction } from '../MysqlService';
4
4
  import { IDBAdapter } from './IDBAdapter';
5
5
  export declare class PostgresAdapter implements IDBAdapter {
@@ -17,7 +17,9 @@ export declare class PostgresAdapter implements IDBAdapter {
17
17
  pingInterval: number;
18
18
  poolConnections: number;
19
19
  acquiredPoolConnections: number;
20
+ statFrames: Map<number, IDBStatFrame>;
20
21
  constructor(options: DBAdapterOptions);
22
+ stat(): Map<number, IDBStatFrame>;
21
23
  escape(value: string): string;
22
24
  tBegin?(): Promise<IDBResult>;
23
25
  tCommit?(trxid: number): Promise<IDBResult>;
@@ -18,9 +18,13 @@ class PostgresAdapter {
18
18
  pingInterval = 1000 * 60 * 5;
19
19
  poolConnections = 0;
20
20
  acquiredPoolConnections = 0;
21
+ statFrames = new Map();
21
22
  constructor(options) {
22
23
  this.options = options;
23
24
  }
25
+ stat() {
26
+ return this.statFrames;
27
+ }
24
28
  escape(value) {
25
29
  return value;
26
30
  }