badmfck-api-server 4.0.97 → 4.0.99
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.
- package/dist/apiServer/APIService.d.ts +5 -2
- package/dist/apiServer/APIService.js +6 -4
- package/dist/apiServer/DBService.d.ts +23 -30
- package/dist/apiServer/DBService.js +59 -90
- package/dist/apiServer/MonitorService.d.ts +11 -0
- package/dist/apiServer/MonitorService.js +56 -4
- package/dist/apiServer/db/IDBAdapter.d.ts +2 -1
- package/dist/apiServer/db/MysqlAdapter.d.ts +7 -1
- package/dist/apiServer/db/MysqlAdapter.js +69 -2
- package/dist/apiServer/db/PostgresAdapter.d.ts +3 -1
- package/dist/apiServer/db/PostgresAdapter.js +4 -0
- package/dist/apiServer/documentation/Documentation.d.ts +1 -1
- package/dist/apiServer/documentation/Documentation.js +22 -3
- package/dist/apiServer/documentation/index.html +224 -161
- package/dist/apiServer/monitor/Monitor.d.ts +2 -0
- package/dist/apiServer/monitor/Monitor.js +14 -0
- package/package.json +2 -2
- package/untitled folder/assets/index-BQHWfl2U.js +0 -40
- package/untitled folder/index.html +0 -12
|
@@ -9,6 +9,9 @@ export interface IMonitorUser {
|
|
|
9
9
|
login: string;
|
|
10
10
|
password: string;
|
|
11
11
|
}
|
|
12
|
+
export interface IDocumentationUser extends IMonitorUser {
|
|
13
|
+
endpoints?: string[];
|
|
14
|
+
}
|
|
12
15
|
export interface APIServiceNetworkLogItem {
|
|
13
16
|
id: number;
|
|
14
17
|
created: number;
|
|
@@ -40,7 +43,7 @@ export interface APIServiceOptions<TInterceptor = unknown> {
|
|
|
40
43
|
preproducer?: (req: HTTPRequestVO | undefined | null) => any;
|
|
41
44
|
access: {
|
|
42
45
|
monitor?: IMonitorUser[];
|
|
43
|
-
documentation?:
|
|
46
|
+
documentation?: IDocumentationUser[];
|
|
44
47
|
};
|
|
45
48
|
appVersion?: string;
|
|
46
49
|
fileTempDir: string;
|
|
@@ -58,7 +61,7 @@ export declare const REQ_HTTP_SERVER: Req<void, {
|
|
|
58
61
|
}>;
|
|
59
62
|
export declare const REQ_INTERNAL_CALL: Req<HTTPRequestVO, IError | any>;
|
|
60
63
|
export declare const REQ_MONITOR_USERS: Req<void, IMonitorUser[]>;
|
|
61
|
-
export declare const REQ_DOC_USERS: Req<void,
|
|
64
|
+
export declare const REQ_DOC_USERS: Req<void, IDocumentationUser[]>;
|
|
62
65
|
export declare const S_APP_STARTED: Signal<void>;
|
|
63
66
|
export declare function Initializer(services: IBaseService[]): Promise<void>;
|
|
64
67
|
export declare class APIService<TInterceptorResult = any, TInternalCallParams = any> extends BaseService {
|
|
@@ -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.
|
|
100
|
+
version = "4.0.99";
|
|
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
|
|
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
|
-
|
|
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.
|
|
27
|
-
const badmfck_signal_1 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
|
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 (
|
|
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:
|
|
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>;
|