badmfck-api-server 2.2.0 → 2.2.2
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.
@@ -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);
|