1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const prom_client_1 = require("prom-client");
|
4 | const TransactionManager_1 = require("../transaction/TransactionManager");
|
5 | const LogManager_1 = require("../log/LogManager");
|
6 | const LOGGER = LogManager_1.LogManager.getLogger(__filename);
|
7 | const transactionExecutionDurationsHistogram = new prom_client_1.Histogram({
|
8 | name: 'db_transaction_execute_time',
|
9 | help: 'Time required to execute a transaction',
|
10 | labelNames: ['clientName', 'readonly'],
|
11 | buckets: [0.003, 0.005, 0.01, 0.05, 0.1, 0.3, 1, 5],
|
12 | });
|
13 | const rolledBackTransactionsCounter = new prom_client_1.Counter({
|
14 | name: 'db_transaction_rollback_counter',
|
15 | help: 'Number of times transactions have been rolled back',
|
16 | labelNames: ['clientName', 'readonly'],
|
17 | });
|
18 | class DBTransaction {
|
19 | |
20 |
|
21 |
|
22 |
|
23 | constructor(clientName, readonly, connectionPool) {
|
24 | this.rolledBack = false;
|
25 | this.transaction = new TransactionManager_1.Transaction();
|
26 | this.clientName = clientName;
|
27 | this.readonly = readonly;
|
28 | this.connectionPool = connectionPool;
|
29 | const labels = [clientName, readonly ? 'true' : 'false'];
|
30 | this.transactionExecutionDurationsHistogram = transactionExecutionDurationsHistogram.labels(...labels);
|
31 | this.rolledBackTransactionsCounter = rolledBackTransactionsCounter.labels(...labels);
|
32 | }
|
33 | async runQueryWithoutTransaction(sql, bindsArr) {
|
34 | await this.obtainConnection();
|
35 | try {
|
36 | return await this.sanitizeAndRunQueryInConnection(sql, bindsArr);
|
37 | }
|
38 | finally {
|
39 | this.releaseConnection();
|
40 | }
|
41 | }
|
42 | getTransaction() {
|
43 | return this.transaction;
|
44 | }
|
45 | async begin() {
|
46 | this.timer = this.transactionExecutionDurationsHistogram.startTimer();
|
47 | await this.obtainConnection();
|
48 | await this.doTransactionBegin();
|
49 | this.transaction.begin();
|
50 | this.transaction.addCommitListener(() => this.doTransactionCommit());
|
51 | this.transaction.addRollbackListener(() => this.doTransactionRollback());
|
52 | this.transaction.addEndListener(() => this.releaseConnection());
|
53 | }
|
54 | async obtainConnection() {
|
55 | this.connection = await this.connectionPool.getConnection();
|
56 | }
|
57 | query(sql, ...bindArrs) {
|
58 | return this.sanitizeAndRunQueryInConnection(sql, bindArrs);
|
59 | }
|
60 | queryAssoc(sql, bindObj) {
|
61 | return this.runQueryAssocPrivate(sql, bindObj);
|
62 | }
|
63 | markError(error) {
|
64 | this.transaction.markError(error);
|
65 | }
|
66 | sanitizeAndRunQueryInConnection(sql, bindsArr) {
|
67 | LOGGER.debug(`sql: ${sql} ${(bindsArr && (bindsArr.length > 0)) ? `| ${bindsArr}` : ''}`);
|
68 | if (!Array.isArray(bindsArr)) {
|
69 | bindsArr = [];
|
70 | }
|
71 | return this.runQueryInConnection(`/* Transaction Id ${this.transaction.id} */ ${sql}`, bindsArr);
|
72 | }
|
73 | runQueryAssocPrivate(sql, bindsObj) {
|
74 | if (sql.indexOf('::') < 0 || !bindsObj) {
|
75 |
|
76 | return this.sanitizeAndRunQueryInConnection.call(this, sql, []);
|
77 | }
|
78 | sql.replace(/::(\w)+::/g, (substr, key) => {
|
79 | if (bindsObj.hasOwnProperty(key)) {
|
80 | return bindsObj[key];
|
81 | }
|
82 | return substr;
|
83 | });
|
84 | }
|
85 | doTransactionBegin() {
|
86 | return this.sanitizeAndRunQueryInConnection(this.getTransactionBeginSQL());
|
87 | }
|
88 |
|
89 | getTransactionBeginSQL() {
|
90 | return 'BEGIN';
|
91 | }
|
92 | doTransactionCommit() {
|
93 | return this.sanitizeAndRunQueryInConnection(this.getTransactionCommitSQL());
|
94 | }
|
95 |
|
96 | getTransactionCommitSQL() {
|
97 | return 'COMMIT';
|
98 | }
|
99 | doTransactionRollback() {
|
100 | this.rolledBack = true;
|
101 | this.rolledBackTransactionsCounter.inc();
|
102 | return this.sanitizeAndRunQueryInConnection(this.getTransactionRollbackSQL());
|
103 | }
|
104 |
|
105 | getTransactionRollbackSQL() {
|
106 | return 'ROLLBACK';
|
107 | }
|
108 | async releaseConnection() {
|
109 | if (this.connection) {
|
110 | this.connectionPool.release(this.connection);
|
111 | this.connection = null;
|
112 | }
|
113 | }
|
114 | async end() {
|
115 | try {
|
116 | await this.transaction.end();
|
117 | }
|
118 | finally {
|
119 | if (this.timer) {
|
120 | this.timer();
|
121 | }
|
122 | }
|
123 | }
|
124 | isRolledBack() {
|
125 | return this.rolledBack;
|
126 | }
|
127 | isReadonly() {
|
128 | return this.readonly;
|
129 | }
|
130 | }
|
131 | exports.DBTransaction = DBTransaction;
|
132 |
|
\ | No newline at end of file |