badmfck-api-server 3.6.6 → 3.6.8
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.js +1 -1
- package/dist/apiServer/DBService.d.ts +40 -2
- package/dist/apiServer/DBService.js +139 -15
- package/dist/apiServer/db/IDBAdapter.d.ts +2 -1
- package/dist/apiServer/db/MysqlAdapter.d.ts +6 -0
- package/dist/apiServer/db/MysqlAdapter.js +131 -20
- package/package.json +1 -1
@@ -95,7 +95,7 @@ async function Initializer(services) {
|
|
95
95
|
exports.Initializer = Initializer;
|
96
96
|
class APIService extends BaseService_1.BaseService {
|
97
97
|
static nextLogID = 0;
|
98
|
-
version = "3.6.
|
98
|
+
version = "3.6.8";
|
99
99
|
options;
|
100
100
|
monitor = null;
|
101
101
|
started = new Date();
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Req } from "badmfck-signal";
|
1
|
+
import Signal, { Req } from "badmfck-signal";
|
2
2
|
import { BaseService } from "./BaseService";
|
3
3
|
import { IDBAdapter } from "./db/IDBAdapter";
|
4
4
|
export interface IDBQueryField {
|
@@ -48,6 +48,10 @@ export interface IDBError {
|
|
48
48
|
export interface IDBResult {
|
49
49
|
data?: any | null;
|
50
50
|
error?: IDBError | null;
|
51
|
+
execution: {
|
52
|
+
started_at: number;
|
53
|
+
finished_at: number;
|
54
|
+
};
|
51
55
|
}
|
52
56
|
export interface IDBSericeOptions {
|
53
57
|
id: string;
|
@@ -64,9 +68,25 @@ export interface DBAdapterOptions {
|
|
64
68
|
queueLimit?: number;
|
65
69
|
transactionFailReport?: (trx: any, message: string) => void;
|
66
70
|
transactionFailReportDir?: string;
|
67
|
-
|
71
|
+
maxTransactionWaitTime?: number;
|
72
|
+
maxQueryTimeout?: number;
|
68
73
|
debug?: boolean;
|
69
74
|
}
|
75
|
+
type ILongQuery = {
|
76
|
+
query: string;
|
77
|
+
started_at: number;
|
78
|
+
finished_at: number;
|
79
|
+
execution: number;
|
80
|
+
};
|
81
|
+
type IQueriesStat = {
|
82
|
+
failed: number;
|
83
|
+
success: number;
|
84
|
+
};
|
85
|
+
export interface IPoolStatus {
|
86
|
+
lastSuccessQueryTime: string;
|
87
|
+
poolConnections: number;
|
88
|
+
acquiredPoolConnections: number;
|
89
|
+
}
|
70
90
|
export declare const REQ_DB: Req<IDBQuery, IDBResult>;
|
71
91
|
export declare const REQ_DBX: Req<IDBBulkQuery, IDBResult[]>;
|
72
92
|
export declare const REQ_DB_TBEGIN: Req<{
|
@@ -80,13 +100,31 @@ export declare const REQ_DB_TROLLBACK: Req<{
|
|
80
100
|
dbid: string;
|
81
101
|
trxid: number;
|
82
102
|
}, IDBResult>;
|
103
|
+
export declare const S_DB_STAT: Signal<{
|
104
|
+
dbid: string;
|
105
|
+
stat: {
|
106
|
+
queriesStat: {
|
107
|
+
hour: string;
|
108
|
+
stat: IQueriesStat;
|
109
|
+
};
|
110
|
+
poolStatus: IPoolStatus;
|
111
|
+
longQueries: ILongQuery[];
|
112
|
+
};
|
113
|
+
}>;
|
83
114
|
export declare class DBService extends BaseService {
|
84
115
|
static allInstances: DBService[];
|
116
|
+
longQueries: ILongQuery[];
|
117
|
+
queriesStat: {
|
118
|
+
hour: string;
|
119
|
+
stat: IQueriesStat;
|
120
|
+
};
|
85
121
|
options: IDBSericeOptions;
|
86
122
|
adapter: IDBAdapter | null;
|
87
123
|
constructor(options: IDBSericeOptions);
|
88
124
|
init(): Promise<void>;
|
89
125
|
getAdapter(dbid?: string | null): IDBAdapter | IDBError;
|
126
|
+
reportQuery(result: IDBResult, req: IDBQuery): Promise<void>;
|
90
127
|
finishService(): Promise<void>;
|
91
128
|
createMysqlDatabase(): Promise<void>;
|
92
129
|
}
|
130
|
+
export {};
|
@@ -1,16 +1,43 @@
|
|
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
|
+
};
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
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");
|
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"));
|
5
28
|
const BaseService_1 = require("./BaseService");
|
6
29
|
const MysqlAdapter_1 = require("./db/MysqlAdapter");
|
30
|
+
const LogService_1 = require("./LogService");
|
7
31
|
exports.REQ_DB = new badmfck_signal_1.Req(undefined, "REQ_DB");
|
8
32
|
exports.REQ_DBX = new badmfck_signal_1.Req(undefined, "REQ_DBX");
|
9
33
|
exports.REQ_DB_TBEGIN = new badmfck_signal_1.Req(undefined, "REQ_DB_TBEGIN");
|
10
34
|
exports.REQ_DB_TCOMMIT = new badmfck_signal_1.Req(undefined, "REQ_DB_TBEGIN");
|
11
35
|
exports.REQ_DB_TROLLBACK = new badmfck_signal_1.Req(undefined, "REQ_DB_TBEGIN");
|
36
|
+
exports.S_DB_STAT = new badmfck_signal_1.default();
|
12
37
|
class DBService extends BaseService_1.BaseService {
|
13
38
|
static allInstances = [];
|
39
|
+
longQueries = [];
|
40
|
+
queriesStat = { hour: "00", stat: { failed: 0, success: 0 } };
|
14
41
|
options;
|
15
42
|
adapter = null;
|
16
43
|
constructor(options) {
|
@@ -23,6 +50,27 @@ class DBService extends BaseService_1.BaseService {
|
|
23
50
|
}
|
24
51
|
}
|
25
52
|
DBService.allInstances.push(this);
|
53
|
+
setInterval(() => {
|
54
|
+
(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);
|
55
|
+
let poolStatus = {
|
56
|
+
lastSuccessQueryTime: "-1",
|
57
|
+
poolConnections: -1,
|
58
|
+
acquiredPoolConnections: -1
|
59
|
+
};
|
60
|
+
if (this.adapter) {
|
61
|
+
poolStatus = this.adapter.poolStatus();
|
62
|
+
(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);
|
63
|
+
}
|
64
|
+
(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")}`);
|
65
|
+
exports.S_DB_STAT.invoke({
|
66
|
+
dbid: options.id,
|
67
|
+
stat: {
|
68
|
+
queriesStat: this.queriesStat,
|
69
|
+
poolStatus: poolStatus,
|
70
|
+
longQueries: this.longQueries
|
71
|
+
}
|
72
|
+
});
|
73
|
+
}, 1000 * 60 * 1);
|
26
74
|
}
|
27
75
|
async init() {
|
28
76
|
super.init();
|
@@ -30,11 +78,20 @@ class DBService extends BaseService_1.BaseService {
|
|
30
78
|
await this.createMysqlDatabase();
|
31
79
|
}
|
32
80
|
exports.REQ_DBX.listener = async (req) => {
|
33
|
-
|
34
|
-
|
81
|
+
const executionStartTime = Date.now();
|
82
|
+
if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter) {
|
83
|
+
const result = await DBService.allInstances[0].adapter.bulk(req);
|
84
|
+
for (let i = 0; i < result.length; i++)
|
85
|
+
this.reportQuery(result[i], req.bulk[i]);
|
86
|
+
return result;
|
87
|
+
}
|
35
88
|
for (let i of DBService.allInstances) {
|
36
|
-
if (i.options.id === req.dbid && i.adapter)
|
37
|
-
|
89
|
+
if (i.options.id === req.dbid && i.adapter) {
|
90
|
+
const result = await i.adapter.bulk(req);
|
91
|
+
for (let i = 0; i < result.length; i++)
|
92
|
+
this.reportQuery(result[i], req.bulk[i]);
|
93
|
+
return result;
|
94
|
+
}
|
38
95
|
}
|
39
96
|
const error = {
|
40
97
|
code: "DB_NOT_FOUND",
|
@@ -47,17 +104,26 @@ class DBService extends BaseService_1.BaseService {
|
|
47
104
|
};
|
48
105
|
let result = [];
|
49
106
|
for (let i of req.bulk) {
|
50
|
-
result.push({
|
107
|
+
result.push({
|
108
|
+
error: error, execution: {
|
109
|
+
started_at: executionStartTime,
|
110
|
+
finished_at: Date.now()
|
111
|
+
}
|
112
|
+
});
|
51
113
|
if (i.throwable)
|
52
114
|
throw error;
|
53
115
|
}
|
54
116
|
return result;
|
55
117
|
};
|
56
118
|
exports.REQ_DB_TBEGIN.listener = async (req) => {
|
119
|
+
const executionStartTime = Date.now();
|
57
120
|
const adapter = this.getAdapter(req.dbid);
|
58
121
|
if ("errno" in adapter) {
|
59
122
|
return {
|
60
|
-
error: adapter
|
123
|
+
error: adapter, execution: {
|
124
|
+
started_at: executionStartTime,
|
125
|
+
finished_at: Date.now()
|
126
|
+
}
|
61
127
|
};
|
62
128
|
}
|
63
129
|
if (adapter.tBegin && typeof adapter.tBegin === "function")
|
@@ -71,14 +137,21 @@ class DBService extends BaseService_1.BaseService {
|
|
71
137
|
name: "TRX_NOT_SUPPORTED",
|
72
138
|
message: "Transactions not supported on selected adapter",
|
73
139
|
isDuplicateError: false
|
140
|
+
}, execution: {
|
141
|
+
started_at: executionStartTime,
|
142
|
+
finished_at: Date.now()
|
74
143
|
}
|
75
144
|
};
|
76
145
|
};
|
77
146
|
exports.REQ_DB_TCOMMIT.listener = async (req) => {
|
147
|
+
const executionStartTime = Date.now();
|
78
148
|
const adapter = this.getAdapter(req.dbid);
|
79
149
|
if ("errno" in adapter) {
|
80
150
|
return {
|
81
|
-
error: adapter
|
151
|
+
error: adapter, execution: {
|
152
|
+
started_at: executionStartTime,
|
153
|
+
finished_at: Date.now()
|
154
|
+
}
|
82
155
|
};
|
83
156
|
}
|
84
157
|
if (adapter.tCommit && typeof adapter.tCommit === "function")
|
@@ -92,14 +165,21 @@ class DBService extends BaseService_1.BaseService {
|
|
92
165
|
name: "TRX_NOT_SUPPORTED",
|
93
166
|
message: "Transactions not supported on selected adapter",
|
94
167
|
isDuplicateError: false
|
168
|
+
}, execution: {
|
169
|
+
started_at: executionStartTime,
|
170
|
+
finished_at: Date.now()
|
95
171
|
}
|
96
172
|
};
|
97
173
|
};
|
98
174
|
exports.REQ_DB_TROLLBACK.listener = async (req) => {
|
175
|
+
const executionStartTime = Date.now();
|
99
176
|
const adapter = this.getAdapter(req.dbid);
|
100
177
|
if ("errno" in adapter) {
|
101
178
|
return {
|
102
|
-
error: adapter
|
179
|
+
error: adapter, execution: {
|
180
|
+
started_at: executionStartTime,
|
181
|
+
finished_at: Date.now()
|
182
|
+
}
|
103
183
|
};
|
104
184
|
}
|
105
185
|
if (adapter.tRollback && typeof adapter.tRollback === "function")
|
@@ -113,15 +193,25 @@ class DBService extends BaseService_1.BaseService {
|
|
113
193
|
name: "TRX_NOT_SUPPORTED",
|
114
194
|
message: "Transactions not supported on selected adapter",
|
115
195
|
isDuplicateError: false
|
196
|
+
}, execution: {
|
197
|
+
started_at: executionStartTime,
|
198
|
+
finished_at: Date.now()
|
116
199
|
}
|
117
200
|
};
|
118
201
|
};
|
119
202
|
exports.REQ_DB.listener = async (req) => {
|
120
|
-
|
121
|
-
|
203
|
+
const executionStartTime = Date.now();
|
204
|
+
if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter) {
|
205
|
+
const result = await DBService.allInstances[0].adapter.query(req);
|
206
|
+
this.reportQuery(result, req);
|
207
|
+
return result;
|
208
|
+
}
|
122
209
|
for (let i of DBService.allInstances) {
|
123
|
-
if (i.options.id === req.dbid && i.adapter)
|
124
|
-
|
210
|
+
if (i.options.id === req.dbid && i.adapter) {
|
211
|
+
const result = await i.adapter.query(req);
|
212
|
+
this.reportQuery(result, req);
|
213
|
+
return result;
|
214
|
+
}
|
125
215
|
}
|
126
216
|
const error = {
|
127
217
|
code: "DB_NOT_FOUND",
|
@@ -132,9 +222,16 @@ class DBService extends BaseService_1.BaseService {
|
|
132
222
|
message: "DB not found: " + req.dbid,
|
133
223
|
isDuplicateError: false
|
134
224
|
};
|
225
|
+
const errorResult = {
|
226
|
+
error: error, execution: {
|
227
|
+
started_at: executionStartTime,
|
228
|
+
finished_at: Date.now()
|
229
|
+
}
|
230
|
+
};
|
231
|
+
this.reportQuery(errorResult, req);
|
135
232
|
if (req.throwable)
|
136
233
|
throw error;
|
137
|
-
return
|
234
|
+
return errorResult;
|
138
235
|
};
|
139
236
|
}
|
140
237
|
getAdapter(dbid) {
|
@@ -155,6 +252,33 @@ class DBService extends BaseService_1.BaseService {
|
|
155
252
|
};
|
156
253
|
return error;
|
157
254
|
}
|
255
|
+
async reportQuery(result, req) {
|
256
|
+
const hour = (new Date().getHours() + 1).toString().padStart(2, "0");
|
257
|
+
if (this.queriesStat.hour !== hour) {
|
258
|
+
this.queriesStat.hour = hour;
|
259
|
+
this.queriesStat.stat = { failed: 0, success: 0 };
|
260
|
+
}
|
261
|
+
if (result.error)
|
262
|
+
this.queriesStat.stat.failed++;
|
263
|
+
else
|
264
|
+
this.queriesStat.stat.success++;
|
265
|
+
const execTime = result.execution.finished_at - result.execution.started_at;
|
266
|
+
if (execTime && execTime > 0) {
|
267
|
+
for (let i of this.longQueries) {
|
268
|
+
if (execTime > i.execution) {
|
269
|
+
this.longQueries.push({
|
270
|
+
execution: execTime,
|
271
|
+
query: req.query,
|
272
|
+
started_at: result.execution.started_at,
|
273
|
+
finished_at: result.execution.finished_at
|
274
|
+
});
|
275
|
+
}
|
276
|
+
}
|
277
|
+
this.longQueries.sort((a, b) => a.execution - b.execution);
|
278
|
+
if (this.longQueries.length > 20)
|
279
|
+
this.longQueries = this.longQueries.slice(-20);
|
280
|
+
}
|
281
|
+
}
|
158
282
|
async finishService() {
|
159
283
|
await this.adapter?.finish();
|
160
284
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { IDBBulkQuery, IDBQuery, IDBResult } from "../DBService";
|
1
|
+
import { IDBBulkQuery, IDBQuery, IDBResult, IPoolStatus } from "../DBService";
|
2
2
|
export interface IDBAdapter {
|
3
3
|
init(): Promise<void>;
|
4
4
|
finish(): Promise<void>;
|
@@ -8,4 +8,5 @@ export interface IDBAdapter {
|
|
8
8
|
prepareQuery(request: IDBQuery): string;
|
9
9
|
query(request: IDBQuery): Promise<IDBResult>;
|
10
10
|
bulk(request: IDBBulkQuery): Promise<IDBResult[]>;
|
11
|
+
poolStatus(): IPoolStatus;
|
11
12
|
}
|
@@ -32,6 +32,11 @@ export declare class MysqlAdapter implements IDBAdapter {
|
|
32
32
|
acquiredPoolConnections: number;
|
33
33
|
constructor(options: DBAdapterOptions);
|
34
34
|
init(): Promise<void>;
|
35
|
+
poolStatus(): {
|
36
|
+
lastSuccessQueryTime: string;
|
37
|
+
poolConnections: number;
|
38
|
+
acquiredPoolConnections: number;
|
39
|
+
};
|
35
40
|
setupTimers(): void;
|
36
41
|
recreatePool(): Promise<boolean>;
|
37
42
|
tBegin(): Promise<IDBResult>;
|
@@ -39,6 +44,7 @@ export declare class MysqlAdapter implements IDBAdapter {
|
|
39
44
|
tRollback(trxid: number): Promise<IDBResult>;
|
40
45
|
bulk(request: IDBBulkQuery): Promise<IDBResult[]>;
|
41
46
|
getConnection(transactionId?: number): Promise<mysql.PoolConnection | IDBError>;
|
47
|
+
withTimeout<T>(promise: Promise<T>, ms: number, query: string): Promise<T>;
|
42
48
|
query(request: IDBQuery, conn?: mysql.PoolConnection): Promise<IDBResult>;
|
43
49
|
finalizeConnection(conn: mysql.PoolConnection): void;
|
44
50
|
prepareQuery(request: IDBQuery): string;
|
@@ -61,6 +61,13 @@ class MysqlAdapter {
|
|
61
61
|
}
|
62
62
|
this.setupTimers();
|
63
63
|
}
|
64
|
+
poolStatus() {
|
65
|
+
return {
|
66
|
+
lastSuccessQueryTime: new Date(this.lastSuccessQueryTime).toISOString(),
|
67
|
+
poolConnections: this.poolConnections,
|
68
|
+
acquiredPoolConnections: this.acquiredPoolConnections
|
69
|
+
};
|
70
|
+
}
|
64
71
|
setupTimers() {
|
65
72
|
setInterval(() => {
|
66
73
|
const now = Date.now();
|
@@ -155,9 +162,16 @@ class MysqlAdapter {
|
|
155
162
|
return true;
|
156
163
|
}
|
157
164
|
async tBegin() {
|
165
|
+
const executionStartTime = Date.now();
|
158
166
|
const conn = await this.getConnection();
|
159
167
|
if (isError(conn))
|
160
|
-
return {
|
168
|
+
return {
|
169
|
+
error: conn,
|
170
|
+
execution: {
|
171
|
+
started_at: executionStartTime,
|
172
|
+
finished_at: Date.now()
|
173
|
+
}
|
174
|
+
};
|
161
175
|
for (let i of this.transactions) {
|
162
176
|
if (i.conn === conn) {
|
163
177
|
(0, LogService_1.logError)("Selected connection is in transaction");
|
@@ -170,6 +184,10 @@ class MysqlAdapter {
|
|
170
184
|
name: "CONN_IN_TRX",
|
171
185
|
message: "Selected connection already in transaction",
|
172
186
|
isDuplicateError: false
|
187
|
+
},
|
188
|
+
execution: {
|
189
|
+
started_at: executionStartTime,
|
190
|
+
finished_at: Date.now()
|
173
191
|
}
|
174
192
|
};
|
175
193
|
}
|
@@ -194,9 +212,16 @@ class MysqlAdapter {
|
|
194
212
|
this.transactions.push(trxRequest);
|
195
213
|
if (this.options.debug)
|
196
214
|
(0, LogService_1.logDB)("Transactions pool: ", this.transactions.map(t => "id:" + t.id + ", queries: " + t.queries.length));
|
197
|
-
return {
|
215
|
+
return {
|
216
|
+
data: { transactionId: trxid },
|
217
|
+
execution: {
|
218
|
+
started_at: executionStartTime,
|
219
|
+
finished_at: Date.now()
|
220
|
+
}
|
221
|
+
};
|
198
222
|
}
|
199
223
|
async tCommit(trxid) {
|
224
|
+
const executionStartTime = Date.now();
|
200
225
|
const trx = this.transactions.find(i => i.id === trxid);
|
201
226
|
if (!trx) {
|
202
227
|
return {
|
@@ -208,6 +233,10 @@ class MysqlAdapter {
|
|
208
233
|
name: "NO_TRX",
|
209
234
|
message: "Transaction not found",
|
210
235
|
isDuplicateError: false
|
236
|
+
},
|
237
|
+
execution: {
|
238
|
+
started_at: executionStartTime,
|
239
|
+
finished_at: Date.now()
|
211
240
|
}
|
212
241
|
};
|
213
242
|
}
|
@@ -218,13 +247,27 @@ class MysqlAdapter {
|
|
218
247
|
(0, LogService_1.logDB)("Transaction pool:", this.transactions);
|
219
248
|
const err = await this.commit(trx);
|
220
249
|
if (err)
|
221
|
-
return {
|
222
|
-
|
250
|
+
return {
|
251
|
+
error: err,
|
252
|
+
execution: {
|
253
|
+
started_at: executionStartTime,
|
254
|
+
finished_at: Date.now()
|
255
|
+
}
|
256
|
+
};
|
257
|
+
return {
|
258
|
+
data: true,
|
259
|
+
execution: {
|
260
|
+
started_at: executionStartTime,
|
261
|
+
finished_at: Date.now()
|
262
|
+
}
|
263
|
+
};
|
223
264
|
}
|
224
265
|
async tRollback(trxid) {
|
266
|
+
const executionStartTime = Date.now();
|
225
267
|
const trx = this.transactions.find(i => i.id === trxid);
|
226
268
|
if (!trx) {
|
227
|
-
return {
|
269
|
+
return {
|
270
|
+
error: {
|
228
271
|
code: "NO_TRX",
|
229
272
|
errno: 100004,
|
230
273
|
fatal: true,
|
@@ -232,12 +275,24 @@ class MysqlAdapter {
|
|
232
275
|
name: "NO_TRX",
|
233
276
|
message: "Transaction not found",
|
234
277
|
isDuplicateError: false
|
235
|
-
}
|
278
|
+
},
|
279
|
+
execution: {
|
280
|
+
started_at: executionStartTime,
|
281
|
+
finished_at: Date.now()
|
282
|
+
}
|
283
|
+
};
|
236
284
|
}
|
237
285
|
await this.rollbackTransaction(trx);
|
238
|
-
return {
|
286
|
+
return {
|
287
|
+
data: true,
|
288
|
+
execution: {
|
289
|
+
started_at: executionStartTime,
|
290
|
+
finished_at: Date.now()
|
291
|
+
}
|
292
|
+
};
|
239
293
|
}
|
240
294
|
async bulk(request) {
|
295
|
+
const executionStartTime = Date.now();
|
241
296
|
const results = [];
|
242
297
|
let queries = [];
|
243
298
|
let conn = null;
|
@@ -245,7 +300,13 @@ class MysqlAdapter {
|
|
245
300
|
conn = await this.getConnection();
|
246
301
|
if (isError(conn)) {
|
247
302
|
for (let i of request.bulk)
|
248
|
-
results.push({
|
303
|
+
results.push({
|
304
|
+
data: null, error: conn,
|
305
|
+
execution: {
|
306
|
+
started_at: executionStartTime,
|
307
|
+
finished_at: Date.now()
|
308
|
+
}
|
309
|
+
});
|
249
310
|
return results;
|
250
311
|
}
|
251
312
|
}
|
@@ -265,15 +326,15 @@ class MysqlAdapter {
|
|
265
326
|
throwable: i.throwable,
|
266
327
|
}));
|
267
328
|
}
|
268
|
-
if (conn) {
|
269
|
-
this.finalizeConnection(conn);
|
270
|
-
conn = null;
|
271
|
-
}
|
272
329
|
if (queries && queries.length > 0) {
|
273
330
|
const responses = await Promise.all(queries);
|
274
331
|
for (let i of responses)
|
275
332
|
results.push(i);
|
276
333
|
}
|
334
|
+
if (conn) {
|
335
|
+
this.finalizeConnection(conn);
|
336
|
+
conn = null;
|
337
|
+
}
|
277
338
|
return results;
|
278
339
|
}
|
279
340
|
async getConnection(transactionId) {
|
@@ -307,8 +368,10 @@ class MysqlAdapter {
|
|
307
368
|
return conn;
|
308
369
|
}
|
309
370
|
catch (e) {
|
310
|
-
if (`${e}`.indexOf('ECONNREFUSED') !== -1)
|
371
|
+
if (`${e}`.indexOf('ECONNREFUSED') !== -1 || `${e}`.indexOf("PROTOCOL_") !== -1 || `${e}`.includes("closed state")) {
|
372
|
+
(0, LogService_1.logError)("Connection looks dead, recreating pool 2");
|
311
373
|
this.recreatePool();
|
374
|
+
}
|
312
375
|
}
|
313
376
|
(0, LogService_1.logCrit)("${MysqlAdapter.js}", `No connection created!`);
|
314
377
|
return {
|
@@ -321,14 +384,44 @@ class MysqlAdapter {
|
|
321
384
|
isDuplicateError: false
|
322
385
|
};
|
323
386
|
}
|
387
|
+
withTimeout(promise, ms, query) {
|
388
|
+
return new Promise((resolve, reject) => {
|
389
|
+
const timeout = setTimeout(() => {
|
390
|
+
reject({
|
391
|
+
code: "TIME_OUT",
|
392
|
+
errno: 100013,
|
393
|
+
sql: query,
|
394
|
+
name: "TIME_OUT",
|
395
|
+
fatal: true,
|
396
|
+
message: "Query timeout reached " + ms
|
397
|
+
});
|
398
|
+
}, ms);
|
399
|
+
promise.then((res) => {
|
400
|
+
clearTimeout(timeout);
|
401
|
+
resolve(res);
|
402
|
+
}).catch((err) => {
|
403
|
+
clearTimeout(timeout);
|
404
|
+
reject(err);
|
405
|
+
});
|
406
|
+
});
|
407
|
+
}
|
324
408
|
async query(request, conn) {
|
409
|
+
let executionStartTime = Date.now();
|
325
410
|
const query = this.prepareQuery(request);
|
326
|
-
if (this.options.debug || request.debug)
|
327
|
-
(
|
411
|
+
if (this.options.debug || request.debug) {
|
412
|
+
if (query.toLowerCase().trim().indexOf("select 1") === -1)
|
413
|
+
(0, LogService_1.logDB)(query);
|
414
|
+
}
|
328
415
|
if (request.parseQueryOnly) {
|
416
|
+
if (conn && !request.transactionID)
|
417
|
+
this.finalizeConnection(conn);
|
329
418
|
return {
|
330
419
|
data: [{ query }],
|
331
420
|
error: null,
|
421
|
+
execution: {
|
422
|
+
started_at: executionStartTime,
|
423
|
+
finished_at: Date.now()
|
424
|
+
}
|
332
425
|
};
|
333
426
|
}
|
334
427
|
let doCloseConnection = false;
|
@@ -339,17 +432,29 @@ class MysqlAdapter {
|
|
339
432
|
return {
|
340
433
|
data: null,
|
341
434
|
error: conn,
|
435
|
+
execution: {
|
436
|
+
started_at: executionStartTime,
|
437
|
+
finished_at: Date.now()
|
438
|
+
}
|
342
439
|
};
|
343
440
|
}
|
344
441
|
}
|
345
442
|
try {
|
346
|
-
|
443
|
+
let result = null;
|
444
|
+
if (this.options.maxQueryTimeout && this.options.maxQueryTimeout > 0)
|
445
|
+
result = await this.withTimeout(conn.query(query), this.options.maxQueryTimeout ?? 1000 * 25, query);
|
446
|
+
else
|
447
|
+
result = await conn.query(query);
|
347
448
|
this.lastSuccessQueryTime = +new Date();
|
348
449
|
if (!request.transactionID && doCloseConnection)
|
349
450
|
this.finalizeConnection(conn);
|
350
451
|
return {
|
351
452
|
data: result[0],
|
352
|
-
error: null
|
453
|
+
error: null,
|
454
|
+
execution: {
|
455
|
+
started_at: executionStartTime,
|
456
|
+
finished_at: Date.now()
|
457
|
+
}
|
353
458
|
};
|
354
459
|
}
|
355
460
|
catch (e) {
|
@@ -364,20 +469,26 @@ class MysqlAdapter {
|
|
364
469
|
this.finalizeConnection(conn);
|
365
470
|
}
|
366
471
|
const error = this.createMysqlQueryError(e);
|
367
|
-
if (`${e}`.indexOf('ECONNREFUSED') !== -1)
|
472
|
+
if (`${e}`.indexOf('ECONNREFUSED') !== -1 || `${e}`.indexOf("PROTOCOL_") !== -1 || error.message.includes("closed state")) {
|
473
|
+
(0, LogService_1.logError)("Connection looks dead, recreating pool 1");
|
368
474
|
this.recreatePool();
|
475
|
+
}
|
369
476
|
if (request.throwable)
|
370
477
|
throw { ...DefaultErrors_1.default.DB_ERROR, details: error.message, stack: error };
|
371
478
|
return {
|
372
479
|
data: null,
|
373
480
|
error: error,
|
481
|
+
execution: {
|
482
|
+
started_at: executionStartTime,
|
483
|
+
finished_at: Date.now()
|
484
|
+
}
|
374
485
|
};
|
375
486
|
}
|
376
487
|
}
|
377
488
|
finalizeConnection(conn) {
|
378
489
|
try {
|
379
|
-
conn.release();
|
380
490
|
conn.removeAllListeners();
|
491
|
+
conn.release();
|
381
492
|
if (this.options.debug)
|
382
493
|
(0, LogService_1.logDB)("Release connection: ", conn.threadId);
|
383
494
|
}
|
@@ -556,7 +667,7 @@ class MysqlAdapter {
|
|
556
667
|
sql: err.sql,
|
557
668
|
name: err.name,
|
558
669
|
message: err.message,
|
559
|
-
fatal: false,
|
670
|
+
fatal: err.fatal && typeof err.fatal === "boolean" ? err.fatal : false,
|
560
671
|
isDuplicateError: err.code.toLowerCase().indexOf("dup_entry") !== -1
|
561
672
|
};
|
562
673
|
}
|