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.
@@ -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.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
- naxTransactionWaitTime?: number;
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
- if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter)
34
- return DBService.allInstances[0].adapter.bulk(req);
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
- return i.adapter.bulk(req);
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({ error: error });
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
- if (!req.dbid && DBService.allInstances.length === 1 && DBService.allInstances[0].adapter)
121
- return DBService.allInstances[0].adapter.query(req);
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
- return i.adapter.query(req);
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 { error: error };
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 { error: conn };
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 { data: { transactionId: trxid } };
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 { error: err };
222
- return { data: true };
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 { error: {
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 { data: true };
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({ data: null, error: conn });
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
- (0, LogService_1.logDB)(query);
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
- const result = await conn.query(query);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badmfck-api-server",
3
- "version": "3.6.6",
3
+ "version": "3.6.8",
4
4
  "description": "Simple API http server based on express",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",