mythix-orm-postgresql 1.7.2 → 1.7.4

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.
@@ -7,10 +7,18 @@ const Nife = require('nife');
7
7
  const { DateTime } = require('luxon');
8
8
  const PG = require('pg');
9
9
  const PGFormat = require('pg-format');
10
- const { Literals } = require('mythix-orm');
10
+ const { Literals, Errors } = require('mythix-orm');
11
11
  const { SQLConnectionBase } = require('mythix-orm-sql-base');
12
12
  const PostgreSQLQueryGenerator = require('./postgresql-query-generator');
13
13
 
14
+ const DEFAULT_TIMEOUT_MS = 5000;
15
+
16
+ function sleep(ms) {
17
+ return new Promise((resolve) => {
18
+ setTimeout(resolve, ms);
19
+ });
20
+ }
21
+
14
22
  class PostgreSQLConnection extends SQLConnectionBase {
15
23
  static dialect = 'postgresql';
16
24
 
@@ -33,6 +41,27 @@ class PostgreSQLConnection extends SQLConnectionBase {
33
41
  return !!this.pool;
34
42
  }
35
43
 
44
+ async getPoolConnection(_retryCount) {
45
+ let retryCount = _retryCount || 0;
46
+
47
+ try {
48
+ return await this.pool.connect();
49
+ } catch (error) {
50
+ let options = this.getOptions();
51
+ let connectMaxRetries = (options.connectMaxRetries == null) ? 5 : options.connectMaxRetries;
52
+ let connectRetryDelay = (options.connectRetryDelay == null) ? 5000 : options.connectRetryDelay;
53
+
54
+ if (error.code === 'ECONNREFUSED') {
55
+ if (retryCount < connectMaxRetries) {
56
+ await sleep(connectRetryDelay);
57
+ return this.getPoolConnection(retryCount + 1);
58
+ }
59
+
60
+ throw new Errors.MythixORMConnectionTimedOutError(error);
61
+ }
62
+ }
63
+ }
64
+
36
65
  async start() {
37
66
  let options = this.getOptions();
38
67
 
@@ -45,11 +74,10 @@ class PostgreSQLConnection extends SQLConnectionBase {
45
74
  },
46
75
  options,
47
76
  {
48
- statement_timeout: options.statementTimeout,
49
- query_timeout: options.queryTimeout,
50
- connectionTimeoutMillis: options.connectionTimeout,
77
+ statement_timeout: options.statementTimeout || DEFAULT_TIMEOUT_MS,
78
+ query_timeout: options.queryTimeout || DEFAULT_TIMEOUT_MS,
79
+ connectionTimeoutMillis: options.connectionTimeout || 60000,
51
80
  idle_in_transaction_session_timeout: options.idleTransactionTimeout,
52
- idleTimeoutMillis: options.idleConnectionTimeout,
53
81
  max: options.maxPoolConnections,
54
82
  },
55
83
  );
@@ -73,6 +101,10 @@ class PostgreSQLConnection extends SQLConnectionBase {
73
101
  pool.on('remove', (client) => {
74
102
  this.emit('disconnect', client);
75
103
  });
104
+
105
+ // Ensure that we can connect to the DB
106
+ let client = await this.getPoolConnection();
107
+ await client.release();
76
108
  }
77
109
 
78
110
  async stop() {
@@ -141,7 +173,7 @@ class PostgreSQLConnection extends SQLConnectionBase {
141
173
  try {
142
174
  client = _client || this.inTransaction;
143
175
  if (!client) {
144
- client = await this.pool.connect();
176
+ client = await this.getPoolConnection();
145
177
  hasOwnClient = true;
146
178
  }
147
179
 
@@ -155,8 +187,14 @@ class PostgreSQLConnection extends SQLConnectionBase {
155
187
  else
156
188
  result = await client.query(sql);
157
189
 
190
+ if (client && hasOwnClient)
191
+ await client.release();
192
+
158
193
  return this.formatResultsResponse(sql, result);
159
194
  } catch (error) {
195
+ if (client && hasOwnClient)
196
+ await client.release();
197
+
160
198
  if (logger) {
161
199
  logger.error(error);
162
200
  logger.error('QUERY: ', sql);
@@ -165,13 +203,10 @@ class PostgreSQLConnection extends SQLConnectionBase {
165
203
  error.query = sql;
166
204
 
167
205
  throw error;
168
- } finally {
169
- if (client && hasOwnClient)
170
- await client.release();
171
206
  }
172
207
  }
173
208
 
174
- async transaction(callback, _options) {
209
+ async transaction(callback, _options, _retryCount) {
175
210
  let options = _options || {};
176
211
  let inheritedThis = Object.create(options.connection || this.getContextValue('connection', this));
177
212
  let lockMode = inheritedThis.getLockMode(options.lock);
@@ -198,15 +233,31 @@ class PostgreSQLConnection extends SQLConnectionBase {
198
233
  }
199
234
 
200
235
  if (!inheritedThis.inTransaction) {
201
- client = inheritedThis.inTransaction = await inheritedThis.pool.connect();
236
+ client = inheritedThis.inTransaction = await inheritedThis.getPoolConnection();
237
+
238
+ let beginSuccess = false;
202
239
 
203
240
  try {
204
241
  await inheritedThis.query(`BEGIN${(options.beginArguments) ? ` ${options.beginArguments}` : ''}`, options, client);
242
+ beginSuccess = true;
243
+
205
244
  if (lockStatement)
206
245
  await inheritedThis.query(lockStatement, options, client);
207
246
 
208
247
  // TODO: Need to handle "busy" error
209
248
  } catch (error) {
249
+ if (beginSuccess)
250
+ await inheritedThis.query('ROLLBACK', options, client);
251
+
252
+ // Transaction timeout (deadlock)
253
+ if (error.query && (/LOCK TABLE/).test(error.query.text)) {
254
+ let retryCount = _retryCount || 0;
255
+ if (retryCount < 5) {
256
+ await client.release();
257
+ return this.transaction(callback, _options, retryCount + 1);
258
+ }
259
+ }
260
+
210
261
  await client.release();
211
262
  throw error;
212
263
  }
@@ -221,7 +272,7 @@ class PostgreSQLConnection extends SQLConnectionBase {
221
272
  }
222
273
 
223
274
  try {
224
- let result = await inheritedThis.createContext(callback, inheritedThis, inheritedThis);
275
+ let result = await await inheritedThis.createContext(callback, inheritedThis, inheritedThis);
225
276
 
226
277
  if (savePointName)
227
278
  await inheritedThis.query(`RELEASE SAVEPOINT ${savePointName}`, options, client);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix-orm-postgresql",
3
- "version": "1.7.2",
3
+ "version": "1.7.4",
4
4
  "description": "PostgreSQL driver for Mythix ORM",
5
5
  "main": "lib/index.js",
6
6
  "type": "commonjs",