badmfck-api-server 2.2.0 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
|
|
1
|
+
/// <reference types="node" />
|
1
2
|
import { BaseService } from "./BaseService";
|
2
3
|
import Signal, { Req } from "badmfck-signal";
|
3
4
|
import * as mysql from "mysql2/promise";
|
5
|
+
import fs from "fs";
|
4
6
|
export declare const S_MYSQL_STARTED: Signal<void>;
|
5
7
|
export declare const REQ_MYSQL_QUERY: Req<MySqlQuery | MySqlQuery[], MysqlResult[]>;
|
6
8
|
export declare const REQ_MYSQL_TRANSACTION: Req<MySqlQuery[], MysqlResult>;
|
@@ -20,6 +22,7 @@ export interface MysqlServiceOptions {
|
|
20
22
|
database: string;
|
21
23
|
queueLimit?: number;
|
22
24
|
transactionFailReport?: (trx: ITransaction, message: string) => void;
|
25
|
+
transactionFailReportDir?: string;
|
23
26
|
migrations?: {
|
24
27
|
dir: string;
|
25
28
|
callback: () => void;
|
@@ -81,9 +84,13 @@ export declare class MysqlService extends BaseService {
|
|
81
84
|
static nextTransactionID: number;
|
82
85
|
transactions: ITransaction[];
|
83
86
|
maxTransactionWaitTime: number;
|
87
|
+
failReportFileStream: fs.WriteStream | null;
|
88
|
+
failReportFileStreamName: string | null;
|
89
|
+
failReportLastAccessTime: number;
|
84
90
|
constructor(options: MysqlServiceOptions);
|
85
91
|
static executeQuery(query: MySqlQuery | MySqlQuery[]): Promise<MysqlResult[]>;
|
86
92
|
init(): Promise<void>;
|
93
|
+
finishApp(): Promise<void>;
|
87
94
|
storeTransactionAsProblem(trx: ITransaction, message: string): Promise<void>;
|
88
95
|
recreatePool(): Promise<boolean>;
|
89
96
|
onApplicationReady(): Promise<void>;
|
@@ -22,6 +22,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
22
|
__setModuleDefault(result, mod);
|
23
23
|
return result;
|
24
24
|
};
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
27
|
+
};
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
26
29
|
exports.MysqlService = exports.executeQuery = exports.REQ_MYSQL_TEND = exports.REQ_MYSQL_TQUERY = exports.REQ_MYSQL_TBEGIN = exports.REQ_MYSQL_TRANSACTION = exports.REQ_MYSQL_QUERY = exports.S_MYSQL_STARTED = void 0;
|
27
30
|
const BaseService_1 = require("./BaseService");
|
@@ -29,6 +32,8 @@ const badmfck_signal_1 = __importStar(require("badmfck-signal"));
|
|
29
32
|
const crypto_1 = require("crypto");
|
30
33
|
const LogService_1 = require("./LogService");
|
31
34
|
const mysql = __importStar(require("mysql2/promise"));
|
35
|
+
const fs_1 = __importDefault(require("fs"));
|
36
|
+
const path_1 = __importDefault(require("path"));
|
32
37
|
exports.S_MYSQL_STARTED = new badmfck_signal_1.default();
|
33
38
|
exports.REQ_MYSQL_QUERY = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_QUERY");
|
34
39
|
exports.REQ_MYSQL_TRANSACTION = new badmfck_signal_1.Req(undefined, "REQ_MYSQL_TRANSACTION");
|
@@ -48,6 +53,9 @@ class MysqlService extends BaseService_1.BaseService {
|
|
48
53
|
static nextTransactionID = 0;
|
49
54
|
transactions = [];
|
50
55
|
maxTransactionWaitTime = 1000 * 60 * 2;
|
56
|
+
failReportFileStream = null;
|
57
|
+
failReportFileStreamName = null;
|
58
|
+
failReportLastAccessTime = 0;
|
51
59
|
constructor(options) {
|
52
60
|
super("mysql");
|
53
61
|
this.options = options;
|
@@ -76,35 +84,37 @@ class MysqlService extends BaseService_1.BaseService {
|
|
76
84
|
return false;
|
77
85
|
});
|
78
86
|
}, 1000 * 30);
|
87
|
+
setInterval(() => {
|
88
|
+
if (!this.failReportFileStream)
|
89
|
+
return;
|
90
|
+
if (Date.now() - this.failReportLastAccessTime > 1000 * 60 * 15) {
|
91
|
+
this.failReportFileStream.end();
|
92
|
+
this.failReportFileStream = null;
|
93
|
+
}
|
94
|
+
}, 1000 * 60 * 5);
|
95
|
+
if (options.transactionFailReportDir) {
|
96
|
+
try {
|
97
|
+
if (!fs_1.default.existsSync(options.transactionFailReportDir)) {
|
98
|
+
fs_1.default.mkdirSync(options.transactionFailReportDir);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
catch (e) {
|
102
|
+
options.transactionFailReportDir = undefined;
|
103
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't create transaction fail report dir");
|
104
|
+
}
|
105
|
+
}
|
79
106
|
}
|
80
107
|
static async executeQuery(query) { return await exports.REQ_MYSQL_QUERY.request(query); }
|
81
108
|
async init() {
|
82
109
|
super.init();
|
83
110
|
process.on('SIGINT', async () => {
|
84
111
|
console.log('1. Received SIGINT. Performing cleanup...');
|
85
|
-
|
86
|
-
try {
|
87
|
-
this.storeTransactionAsProblem(i, "SIGINT");
|
88
|
-
i.conn.removeAllListeners();
|
89
|
-
await i.conn.rollback();
|
90
|
-
i.conn.release();
|
91
|
-
}
|
92
|
-
catch (e) {
|
93
|
-
(0, LogService_1.logError)("Can't release transaction connection", e);
|
94
|
-
}
|
95
|
-
}
|
96
|
-
if (this.pool) {
|
97
|
-
try {
|
98
|
-
await this.pool.end();
|
99
|
-
}
|
100
|
-
catch (e) {
|
101
|
-
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't close MYSQL pool!");
|
102
|
-
}
|
103
|
-
}
|
112
|
+
await this.finishApp();
|
104
113
|
process.exit(0);
|
105
114
|
});
|
106
|
-
process.on('SIGTERM', () => {
|
115
|
+
process.on('SIGTERM', async () => {
|
107
116
|
console.log('2. Received SIGTERM. Performing cleanup...');
|
117
|
+
await this.finishApp();
|
108
118
|
process.exit(0);
|
109
119
|
});
|
110
120
|
this.serviceStarted = false;
|
@@ -220,15 +230,42 @@ class MysqlService extends BaseService_1.BaseService {
|
|
220
230
|
const conn = await this.pool.getConnection();
|
221
231
|
if (!conn)
|
222
232
|
return { error: { code: "NO_CONN", errno: 100001, fatal: true, sql: "", name: "NO_CONN", message: "Mysql pool cant get connection" }, data: null };
|
223
|
-
|
233
|
+
const trx = {
|
234
|
+
id: MysqlService.nextTransactionID++,
|
235
|
+
timestamp: Date.now(),
|
236
|
+
conn: conn,
|
237
|
+
queries: []
|
238
|
+
};
|
224
239
|
let income = [];
|
225
240
|
try {
|
226
241
|
await conn.beginTransaction();
|
227
242
|
await conn.query("SET autocommit=0");
|
228
|
-
|
229
|
-
|
243
|
+
}
|
244
|
+
catch (e) {
|
245
|
+
const err = this.createMysqlQueryError(e);
|
246
|
+
this.storeTransactionAsProblem(trx, err.message);
|
247
|
+
return { error: err, data: null };
|
248
|
+
}
|
249
|
+
for (let i of data) {
|
250
|
+
const query = MysqlService.prepareQuery(i.query, i.fields);
|
251
|
+
let err = null;
|
252
|
+
try {
|
253
|
+
const d = await conn.query(query);
|
230
254
|
income.push(d[0]);
|
231
255
|
}
|
256
|
+
catch (e) {
|
257
|
+
err = this.createMysqlQueryError(e);
|
258
|
+
}
|
259
|
+
trx.queries.push({
|
260
|
+
sql: query,
|
261
|
+
status: err ? err.message : "completed"
|
262
|
+
});
|
263
|
+
if (err) {
|
264
|
+
this.storeTransactionAsProblem(trx, err.message);
|
265
|
+
return { error: err, data: null };
|
266
|
+
}
|
267
|
+
}
|
268
|
+
try {
|
232
269
|
await conn.commit();
|
233
270
|
}
|
234
271
|
catch (e) {
|
@@ -240,25 +277,64 @@ class MysqlService extends BaseService_1.BaseService {
|
|
240
277
|
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't rollback transaction!");
|
241
278
|
rollbackError = this.createMysqlQueryError(e2);
|
242
279
|
}
|
243
|
-
|
280
|
+
const err = this.createMysqlQueryError(e);
|
281
|
+
this.storeTransactionAsProblem(trx, err.message);
|
282
|
+
return { error: err, data: null, rollbackError: rollbackError };
|
244
283
|
}
|
284
|
+
return { error: null, data: income };
|
285
|
+
};
|
286
|
+
}
|
287
|
+
async finishApp() {
|
288
|
+
for (let i of this.transactions) {
|
245
289
|
try {
|
246
|
-
|
290
|
+
this.storeTransactionAsProblem(i, "SIGINT");
|
291
|
+
i.conn.removeAllListeners();
|
292
|
+
await i.conn.rollback();
|
293
|
+
i.conn.release();
|
247
294
|
}
|
248
295
|
catch (e) {
|
249
|
-
(0, LogService_1.
|
296
|
+
(0, LogService_1.logError)("Can't release transaction connection", e);
|
250
297
|
}
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
298
|
+
}
|
299
|
+
if (this.pool) {
|
300
|
+
try {
|
301
|
+
await this.pool.end();
|
302
|
+
}
|
303
|
+
catch (e) {
|
304
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't close MYSQL pool!");
|
305
|
+
}
|
306
|
+
}
|
307
|
+
if (this.failReportFileStream)
|
308
|
+
this.failReportFileStream.end();
|
309
|
+
this.failReportFileStream = null;
|
255
310
|
}
|
256
311
|
async storeTransactionAsProblem(trx, message) {
|
257
|
-
if (!this.options.transactionFailReport) {
|
258
|
-
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options");
|
312
|
+
if (!this.options.transactionFailReport && !this.options.transactionFailReportDir) {
|
313
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't report failed transaction, no report function: transactionFailReport in options, and transactionFailReportDir isnt set");
|
259
314
|
return;
|
260
315
|
}
|
261
|
-
this.options.
|
316
|
+
if (this.options.transactionFailReportDir) {
|
317
|
+
const yyymmdd = new Date().toISOString().substring(0, 10).replaceAll("-", "");
|
318
|
+
const date = new Date();
|
319
|
+
const file = path_1.default.resolve(this.options.transactionFailReportDir, yyymmdd + ".json");
|
320
|
+
if (this.failReportFileStream) {
|
321
|
+
if (this.failReportFileStreamName !== file) {
|
322
|
+
this.failReportFileStream.end();
|
323
|
+
this.failReportFileStream = null;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
if (this.failReportFileStreamName) {
|
327
|
+
this.failReportFileStream = fs_1.default.createWriteStream(file, { flags: 'a' });
|
328
|
+
this.failReportFileStreamName = file;
|
329
|
+
}
|
330
|
+
this.failReportFileStream?.write(JSON.stringify({ trx, date, message }) + "\n{'s':'&$5__1AzZa'}\n", err => {
|
331
|
+
if (err)
|
332
|
+
(0, LogService_1.logCrit)("${MysqlService.js}", "Can't write to transaction fail report file");
|
333
|
+
});
|
334
|
+
this.failReportLastAccessTime = Date.now();
|
335
|
+
}
|
336
|
+
if (this.options.transactionFailReport)
|
337
|
+
this.options.transactionFailReport(trx, message);
|
262
338
|
}
|
263
339
|
async recreatePool() {
|
264
340
|
(0, LogService_1.logInfo)("${MysqlService.js}", "Connecting to mysql: \n HOST: " + this.options.host + '\n PORT:' + this.options.port);
|