relq 1.0.110 → 1.0.112
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.
- package/dist/cjs/core/relq-base.cjs +52 -3
- package/dist/esm/core/relq-base.js +52 -3
- package/dist/index.d.ts +16 -0
- package/package.json +1 -1
|
@@ -93,9 +93,32 @@ class RelqBase {
|
|
|
93
93
|
}
|
|
94
94
|
await this.ensureInitialized();
|
|
95
95
|
const startTime = performance.now();
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
const retryConfig = this.config.retry;
|
|
97
|
+
if (!retryConfig) {
|
|
98
|
+
const result = await this._query(sql);
|
|
99
|
+
return { result, duration: performance.now() - startTime };
|
|
100
|
+
}
|
|
101
|
+
const opts = typeof retryConfig === 'object'
|
|
102
|
+
? { maxRetries: retryConfig.maxRetries ?? 3, initialDelayMs: retryConfig.initialDelayMs ?? 250, maxDelayMs: retryConfig.maxDelayMs ?? 5000 }
|
|
103
|
+
: { maxRetries: 3, initialDelayMs: 250, maxDelayMs: 5000 };
|
|
104
|
+
let lastError;
|
|
105
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await this._query(sql);
|
|
108
|
+
return { result, duration: performance.now() - startTime };
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
lastError = error;
|
|
112
|
+
if (attempt < opts.maxRetries && isTransientError(error)) {
|
|
113
|
+
const delay = Math.min(opts.initialDelayMs * 2 ** attempt, opts.maxDelayMs);
|
|
114
|
+
(0, helpers_1.debugLog)(this.config, `Transient error (attempt ${attempt + 1}/${opts.maxRetries}), retrying in ${delay}ms: ${error.code || error.message}`);
|
|
115
|
+
await sleep(delay);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
throw lastError;
|
|
99
122
|
}
|
|
100
123
|
buildMetadata(result, duration) {
|
|
101
124
|
return {
|
|
@@ -220,3 +243,29 @@ class RelqBase {
|
|
|
220
243
|
}
|
|
221
244
|
}
|
|
222
245
|
exports.RelqBase = RelqBase;
|
|
246
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
247
|
+
'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ESERVFAIL',
|
|
248
|
+
'ETIMEDOUT', 'EPIPE', 'EAI_AGAIN', 'EHOSTUNREACH',
|
|
249
|
+
'CONNECTION_LOST', 'PROTOCOL_CONNECTION_LOST',
|
|
250
|
+
]);
|
|
251
|
+
const TRANSIENT_PG_CODES = new Set([
|
|
252
|
+
'40P01',
|
|
253
|
+
'40001',
|
|
254
|
+
'08006',
|
|
255
|
+
'08001',
|
|
256
|
+
'08004',
|
|
257
|
+
'57P01',
|
|
258
|
+
'57P03',
|
|
259
|
+
]);
|
|
260
|
+
function isTransientError(error) {
|
|
261
|
+
if (TRANSIENT_NETWORK_CODES.has(error.code))
|
|
262
|
+
return true;
|
|
263
|
+
if (error.cause && TRANSIENT_NETWORK_CODES.has(error.cause.code))
|
|
264
|
+
return true;
|
|
265
|
+
if (TRANSIENT_PG_CODES.has(error.code))
|
|
266
|
+
return true;
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
function sleep(ms) {
|
|
270
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
271
|
+
}
|
|
@@ -90,9 +90,32 @@ export class RelqBase {
|
|
|
90
90
|
}
|
|
91
91
|
await this.ensureInitialized();
|
|
92
92
|
const startTime = performance.now();
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
const retryConfig = this.config.retry;
|
|
94
|
+
if (!retryConfig) {
|
|
95
|
+
const result = await this._query(sql);
|
|
96
|
+
return { result, duration: performance.now() - startTime };
|
|
97
|
+
}
|
|
98
|
+
const opts = typeof retryConfig === 'object'
|
|
99
|
+
? { maxRetries: retryConfig.maxRetries ?? 3, initialDelayMs: retryConfig.initialDelayMs ?? 250, maxDelayMs: retryConfig.maxDelayMs ?? 5000 }
|
|
100
|
+
: { maxRetries: 3, initialDelayMs: 250, maxDelayMs: 5000 };
|
|
101
|
+
let lastError;
|
|
102
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
103
|
+
try {
|
|
104
|
+
const result = await this._query(sql);
|
|
105
|
+
return { result, duration: performance.now() - startTime };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
lastError = error;
|
|
109
|
+
if (attempt < opts.maxRetries && isTransientError(error)) {
|
|
110
|
+
const delay = Math.min(opts.initialDelayMs * 2 ** attempt, opts.maxDelayMs);
|
|
111
|
+
debugLog(this.config, `Transient error (attempt ${attempt + 1}/${opts.maxRetries}), retrying in ${delay}ms: ${error.code || error.message}`);
|
|
112
|
+
await sleep(delay);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
throw lastError;
|
|
96
119
|
}
|
|
97
120
|
buildMetadata(result, duration) {
|
|
98
121
|
return {
|
|
@@ -216,3 +239,29 @@ export class RelqBase {
|
|
|
216
239
|
return this._isClosed;
|
|
217
240
|
}
|
|
218
241
|
}
|
|
242
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
243
|
+
'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ESERVFAIL',
|
|
244
|
+
'ETIMEDOUT', 'EPIPE', 'EAI_AGAIN', 'EHOSTUNREACH',
|
|
245
|
+
'CONNECTION_LOST', 'PROTOCOL_CONNECTION_LOST',
|
|
246
|
+
]);
|
|
247
|
+
const TRANSIENT_PG_CODES = new Set([
|
|
248
|
+
'40P01',
|
|
249
|
+
'40001',
|
|
250
|
+
'08006',
|
|
251
|
+
'08001',
|
|
252
|
+
'08004',
|
|
253
|
+
'57P01',
|
|
254
|
+
'57P03',
|
|
255
|
+
]);
|
|
256
|
+
function isTransientError(error) {
|
|
257
|
+
if (TRANSIENT_NETWORK_CODES.has(error.code))
|
|
258
|
+
return true;
|
|
259
|
+
if (error.cause && TRANSIENT_NETWORK_CODES.has(error.cause.code))
|
|
260
|
+
return true;
|
|
261
|
+
if (TRANSIENT_PG_CODES.has(error.code))
|
|
262
|
+
return true;
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
function sleep(ms) {
|
|
266
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
267
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6760,6 +6760,22 @@ export interface RelqBaseConfig {
|
|
|
6760
6760
|
commitLimit?: number;
|
|
6761
6761
|
/** Schema relations for type-safe joins */
|
|
6762
6762
|
relations?: SchemaRelations | SchemaRelationsArray;
|
|
6763
|
+
/**
|
|
6764
|
+
* Retry transient query failures (DNS, connection reset, timeout).
|
|
6765
|
+
* Disabled by default. Set `true` for 3 retries, or pass options.
|
|
6766
|
+
*/
|
|
6767
|
+
retry?: boolean | RetryOptions;
|
|
6768
|
+
}
|
|
6769
|
+
/**
|
|
6770
|
+
* Configuration for automatic query retry on transient errors.
|
|
6771
|
+
*/
|
|
6772
|
+
export interface RetryOptions {
|
|
6773
|
+
/** Maximum number of retry attempts @default 3 */
|
|
6774
|
+
maxRetries?: number;
|
|
6775
|
+
/** Initial delay in ms before first retry (doubles each attempt) @default 250 */
|
|
6776
|
+
initialDelayMs?: number;
|
|
6777
|
+
/** Maximum delay in ms between retries @default 5000 */
|
|
6778
|
+
maxDelayMs?: number;
|
|
6763
6779
|
}
|
|
6764
6780
|
export interface PgConnectionFields {
|
|
6765
6781
|
/** Database host @default 'localhost' */
|