s3db.js 7.3.5 → 7.3.8
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/PLUGINS.md +1285 -157
- package/dist/s3db.cjs.js +314 -83
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +314 -83
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +314 -83
- package/dist/s3db.iife.min.js +1 -1
- package/mcp/server.js +1410 -0
- package/package.json +7 -1
- package/src/plugins/cache/filesystem-cache.class.js +9 -0
- package/src/plugins/replicator.plugin.js +130 -46
- package/src/plugins/replicators/bigquery-replicator.class.js +42 -5
- package/src/plugins/replicators/postgres-replicator.class.js +28 -2
- package/src/plugins/replicators/s3db-replicator.class.js +177 -68
- package/src/plugins/replicators/sqs-replicator.class.js +18 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s3db.js",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.8",
|
|
4
4
|
"description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
|
|
5
5
|
"main": "dist/s3db.cjs.js",
|
|
6
6
|
"module": "dist/s3db.es.js",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"jsdelivr": "dist/s3db.iife.min.js",
|
|
11
11
|
"author": "@stone/martech",
|
|
12
12
|
"license": "UNLICENSED",
|
|
13
|
+
"bin": {
|
|
14
|
+
"s3db-mcp": "./mcp/server.js"
|
|
15
|
+
},
|
|
13
16
|
"repository": {
|
|
14
17
|
"type": "git",
|
|
15
18
|
"url": "git+https://github.com/forattini-dev/s3db.js.git"
|
|
@@ -26,6 +29,9 @@
|
|
|
26
29
|
"type": "module",
|
|
27
30
|
"sideEffects": false,
|
|
28
31
|
"imports": {
|
|
32
|
+
"#mcp/*": "./mcp/*",
|
|
33
|
+
"#dist/*": "./dist/*",
|
|
34
|
+
"#examples/*": "./examples/*",
|
|
29
35
|
"#src/*": "./src/*",
|
|
30
36
|
"#tests/*": "./tests/*"
|
|
31
37
|
},
|
|
@@ -428,6 +428,15 @@ export class FilesystemCache extends Cache {
|
|
|
428
428
|
|
|
429
429
|
async _clear(prefix) {
|
|
430
430
|
try {
|
|
431
|
+
// Check if directory exists before trying to read it
|
|
432
|
+
if (!await this._fileExists(this.directory)) {
|
|
433
|
+
// Directory doesn't exist, nothing to clear
|
|
434
|
+
if (this.enableStats) {
|
|
435
|
+
this.stats.clears++;
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
431
440
|
const files = await readdir(this.directory);
|
|
432
441
|
const cacheFiles = files.filter(file => {
|
|
433
442
|
if (!file.startsWith(this.prefix)) return false;
|
|
@@ -61,19 +61,13 @@ function normalizeResourceName(name) {
|
|
|
61
61
|
* 2. Map: source resource → destination resource name:
|
|
62
62
|
* resources: { users: 'people' }
|
|
63
63
|
*
|
|
64
|
-
* 3. Map: source resource →
|
|
65
|
-
* resources: { users:
|
|
64
|
+
* 3. Map: source resource → { resource, transform }:
|
|
65
|
+
* resources: { users: { resource: 'people', transform: fn } }
|
|
66
66
|
*
|
|
67
|
-
* 4. Map: source resource →
|
|
68
|
-
* resources: { users: { resource: 'people', transformer: fn } }
|
|
69
|
-
*
|
|
70
|
-
* 5. Map: source resource → array of objects (multi-destination):
|
|
71
|
-
* resources: { users: [ { resource: 'people', transformer: fn } ] }
|
|
72
|
-
*
|
|
73
|
-
* 6. Map: source resource → function (transformer only):
|
|
67
|
+
* 4. Map: source resource → function (transformer only):
|
|
74
68
|
* resources: { users: (el) => ({ ...el, fullName: el.name }) }
|
|
75
69
|
*
|
|
76
|
-
*
|
|
70
|
+
* The transform function is optional and applies to data before replication.
|
|
77
71
|
*
|
|
78
72
|
* === Example Plugin Configurations ===
|
|
79
73
|
*
|
|
@@ -93,10 +87,10 @@ function normalizeResourceName(name) {
|
|
|
93
87
|
* ]
|
|
94
88
|
* });
|
|
95
89
|
*
|
|
96
|
-
* // Advanced mapping with
|
|
90
|
+
* // Advanced mapping with transform
|
|
97
91
|
* new ReplicatorPlugin({
|
|
98
92
|
* replicators: [
|
|
99
|
-
* { driver: 's3db', client: dbB, config: { resources: { users:
|
|
93
|
+
* { driver: 's3db', client: dbB, config: { resources: { users: { resource: 'people', transform: (el) => ({ ...el, fullName: el.name }) } } } }
|
|
100
94
|
* ]
|
|
101
95
|
* });
|
|
102
96
|
*
|
|
@@ -177,27 +171,42 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
177
171
|
}
|
|
178
172
|
|
|
179
173
|
resource.on('insert', async (data) => {
|
|
180
|
-
|
|
174
|
+
const [ok, error] = await tryFn(async () => {
|
|
181
175
|
const completeData = { ...data, createdAt: new Date().toISOString() };
|
|
182
176
|
await plugin.processReplicatorEvent('insert', resource.name, completeData.id, completeData);
|
|
183
|
-
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (!ok) {
|
|
180
|
+
if (this.config.verbose) {
|
|
181
|
+
console.warn(`[ReplicatorPlugin] Insert event failed for resource ${resource.name}: ${error.message}`);
|
|
182
|
+
}
|
|
184
183
|
this.emit('error', { operation: 'insert', error: error.message, resource: resource.name });
|
|
185
184
|
}
|
|
186
185
|
});
|
|
187
186
|
|
|
188
187
|
resource.on('update', async (data, beforeData) => {
|
|
189
|
-
|
|
188
|
+
const [ok, error] = await tryFn(async () => {
|
|
190
189
|
const completeData = { ...data, updatedAt: new Date().toISOString() };
|
|
191
190
|
await plugin.processReplicatorEvent('update', resource.name, completeData.id, completeData, beforeData);
|
|
192
|
-
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (!ok) {
|
|
194
|
+
if (this.config.verbose) {
|
|
195
|
+
console.warn(`[ReplicatorPlugin] Update event failed for resource ${resource.name}: ${error.message}`);
|
|
196
|
+
}
|
|
193
197
|
this.emit('error', { operation: 'update', error: error.message, resource: resource.name });
|
|
194
198
|
}
|
|
195
199
|
});
|
|
196
200
|
|
|
197
201
|
resource.on('delete', async (data) => {
|
|
198
|
-
|
|
202
|
+
const [ok, error] = await tryFn(async () => {
|
|
199
203
|
await plugin.processReplicatorEvent('delete', resource.name, data.id, data);
|
|
200
|
-
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (!ok) {
|
|
207
|
+
if (this.config.verbose) {
|
|
208
|
+
console.warn(`[ReplicatorPlugin] Delete event failed for resource ${resource.name}: ${error.message}`);
|
|
209
|
+
}
|
|
201
210
|
this.emit('error', { operation: 'delete', error: error.message, resource: resource.name });
|
|
202
211
|
}
|
|
203
212
|
});
|
|
@@ -219,14 +228,19 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
219
228
|
async setup(database) {
|
|
220
229
|
this.database = database;
|
|
221
230
|
|
|
222
|
-
|
|
231
|
+
const [initOk, initError] = await tryFn(async () => {
|
|
223
232
|
await this.initializeReplicators(database);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!initOk) {
|
|
236
|
+
if (this.config.verbose) {
|
|
237
|
+
console.warn(`[ReplicatorPlugin] Replicator initialization failed: ${initError.message}`);
|
|
238
|
+
}
|
|
239
|
+
this.emit('error', { operation: 'setup', error: initError.message });
|
|
240
|
+
throw initError;
|
|
227
241
|
}
|
|
228
242
|
|
|
229
|
-
|
|
243
|
+
const [logOk, logError] = await tryFn(async () => {
|
|
230
244
|
if (this.config.replicatorLogResource) {
|
|
231
245
|
const logRes = await database.createResource({
|
|
232
246
|
name: this.config.replicatorLogResource,
|
|
@@ -243,8 +257,16 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
243
257
|
}
|
|
244
258
|
});
|
|
245
259
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (!logOk) {
|
|
263
|
+
if (this.config.verbose) {
|
|
264
|
+
console.warn(`[ReplicatorPlugin] Failed to create log resource ${this.config.replicatorLogResource}: ${logError.message}`);
|
|
265
|
+
}
|
|
266
|
+
this.emit('replicator_log_resource_creation_error', {
|
|
267
|
+
resourceName: this.config.replicatorLogResource,
|
|
268
|
+
error: logError.message
|
|
269
|
+
});
|
|
248
270
|
}
|
|
249
271
|
|
|
250
272
|
await this.uploadMetadataFile(database);
|
|
@@ -307,15 +329,24 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
307
329
|
async retryWithBackoff(operation, maxRetries = 3) {
|
|
308
330
|
let lastError;
|
|
309
331
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
332
|
+
const [ok, error] = await tryFn(operation);
|
|
333
|
+
|
|
334
|
+
if (ok) {
|
|
335
|
+
return ok;
|
|
336
|
+
} else {
|
|
313
337
|
lastError = error;
|
|
338
|
+
if (this.config.verbose) {
|
|
339
|
+
console.warn(`[ReplicatorPlugin] Retry attempt ${attempt}/${maxRetries} failed: ${error.message}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
314
342
|
if (attempt === maxRetries) {
|
|
315
343
|
throw error;
|
|
316
344
|
}
|
|
317
345
|
// Simple backoff: wait 1s, 2s, 4s...
|
|
318
346
|
const delay = Math.pow(2, attempt - 1) * 1000;
|
|
347
|
+
if (this.config.verbose) {
|
|
348
|
+
console.warn(`[ReplicatorPlugin] Waiting ${delay}ms before retry...`);
|
|
349
|
+
}
|
|
319
350
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
320
351
|
}
|
|
321
352
|
}
|
|
@@ -323,7 +354,7 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
323
354
|
}
|
|
324
355
|
|
|
325
356
|
async logError(replicator, resourceName, operation, recordId, data, error) {
|
|
326
|
-
|
|
357
|
+
const [ok, logError] = await tryFn(async () => {
|
|
327
358
|
const logResourceName = this.config.replicatorLogResource;
|
|
328
359
|
if (this.database && this.database.resources && this.database.resources[logResourceName]) {
|
|
329
360
|
const logResource = this.database.resources[logResourceName];
|
|
@@ -338,8 +369,20 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
338
369
|
status: 'error'
|
|
339
370
|
});
|
|
340
371
|
}
|
|
341
|
-
}
|
|
342
|
-
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
if (!ok) {
|
|
375
|
+
if (this.config.verbose) {
|
|
376
|
+
console.warn(`[ReplicatorPlugin] Failed to log error for ${resourceName}: ${logError.message}`);
|
|
377
|
+
}
|
|
378
|
+
this.emit('replicator_log_error', {
|
|
379
|
+
replicator: replicator.name || replicator.id,
|
|
380
|
+
resourceName,
|
|
381
|
+
operation,
|
|
382
|
+
recordId,
|
|
383
|
+
originalError: error.message,
|
|
384
|
+
logError: logError.message
|
|
385
|
+
});
|
|
343
386
|
}
|
|
344
387
|
}
|
|
345
388
|
|
|
@@ -356,7 +399,7 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
356
399
|
}
|
|
357
400
|
|
|
358
401
|
const promises = applicableReplicators.map(async (replicator) => {
|
|
359
|
-
|
|
402
|
+
const [ok, error, result] = await tryFn(async () => {
|
|
360
403
|
const result = await this.retryWithBackoff(
|
|
361
404
|
() => replicator.replicate(resourceName, operation, data, recordId, beforeData),
|
|
362
405
|
this.config.maxRetries
|
|
@@ -372,7 +415,15 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
372
415
|
});
|
|
373
416
|
|
|
374
417
|
return result;
|
|
375
|
-
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (ok) {
|
|
421
|
+
return result;
|
|
422
|
+
} else {
|
|
423
|
+
if (this.config.verbose) {
|
|
424
|
+
console.warn(`[ReplicatorPlugin] Replication failed for ${replicator.name || replicator.id} on ${resourceName}: ${error.message}`);
|
|
425
|
+
}
|
|
426
|
+
|
|
376
427
|
this.emit('replicator_error', {
|
|
377
428
|
replicator: replicator.name || replicator.id,
|
|
378
429
|
resourceName,
|
|
@@ -403,12 +454,16 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
403
454
|
}
|
|
404
455
|
|
|
405
456
|
const promises = applicableReplicators.map(async (replicator) => {
|
|
406
|
-
|
|
457
|
+
const [wrapperOk, wrapperError] = await tryFn(async () => {
|
|
407
458
|
const [ok, err, result] = await tryFn(() =>
|
|
408
459
|
replicator.replicate(item.resourceName, item.operation, item.data, item.recordId, item.beforeData)
|
|
409
460
|
);
|
|
410
461
|
|
|
411
462
|
if (!ok) {
|
|
463
|
+
if (this.config.verbose) {
|
|
464
|
+
console.warn(`[ReplicatorPlugin] Replicator item processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${err.message}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
412
467
|
this.emit('replicator_error', {
|
|
413
468
|
replicator: replicator.name || replicator.id,
|
|
414
469
|
resourceName: item.resourceName,
|
|
@@ -434,20 +489,28 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
434
489
|
});
|
|
435
490
|
|
|
436
491
|
return { success: true, result };
|
|
437
|
-
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (wrapperOk) {
|
|
495
|
+
return wrapperOk;
|
|
496
|
+
} else {
|
|
497
|
+
if (this.config.verbose) {
|
|
498
|
+
console.warn(`[ReplicatorPlugin] Wrapper processing failed for ${replicator.name || replicator.id} on ${item.resourceName}: ${wrapperError.message}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
438
501
|
this.emit('replicator_error', {
|
|
439
502
|
replicator: replicator.name || replicator.id,
|
|
440
503
|
resourceName: item.resourceName,
|
|
441
504
|
operation: item.operation,
|
|
442
505
|
recordId: item.recordId,
|
|
443
|
-
error:
|
|
506
|
+
error: wrapperError.message
|
|
444
507
|
});
|
|
445
508
|
|
|
446
509
|
if (this.config.logErrors && this.database) {
|
|
447
|
-
await this.logError(replicator, item.resourceName, item.operation, item.recordId, item.data,
|
|
510
|
+
await this.logError(replicator, item.resourceName, item.operation, item.recordId, item.data, wrapperError);
|
|
448
511
|
}
|
|
449
512
|
|
|
450
|
-
return { success: false, error:
|
|
513
|
+
return { success: false, error: wrapperError.message };
|
|
451
514
|
}
|
|
452
515
|
});
|
|
453
516
|
|
|
@@ -474,9 +537,14 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
474
537
|
timestamp: typeof item.timestamp === 'number' ? item.timestamp : Date.now(),
|
|
475
538
|
createdAt: item.createdAt || new Date().toISOString().slice(0, 10),
|
|
476
539
|
};
|
|
477
|
-
|
|
540
|
+
const [ok, err] = await tryFn(async () => {
|
|
478
541
|
await logRes.insert(logItem);
|
|
479
|
-
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
if (!ok) {
|
|
545
|
+
if (this.config.verbose) {
|
|
546
|
+
console.warn(`[ReplicatorPlugin] Failed to log replicator item: ${err.message}`);
|
|
547
|
+
}
|
|
480
548
|
this.emit('replicator.log.failed', { error: err, item });
|
|
481
549
|
}
|
|
482
550
|
}
|
|
@@ -611,15 +679,24 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
611
679
|
}
|
|
612
680
|
|
|
613
681
|
async cleanup() {
|
|
614
|
-
|
|
682
|
+
const [ok, error] = await tryFn(async () => {
|
|
615
683
|
if (this.replicators && this.replicators.length > 0) {
|
|
616
684
|
const cleanupPromises = this.replicators.map(async (replicator) => {
|
|
617
|
-
|
|
685
|
+
const [replicatorOk, replicatorError] = await tryFn(async () => {
|
|
618
686
|
if (replicator && typeof replicator.cleanup === 'function') {
|
|
619
687
|
await replicator.cleanup();
|
|
620
688
|
}
|
|
621
|
-
}
|
|
622
|
-
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
if (!replicatorOk) {
|
|
692
|
+
if (this.config.verbose) {
|
|
693
|
+
console.warn(`[ReplicatorPlugin] Failed to cleanup replicator ${replicator.name || replicator.id}: ${replicatorError.message}`);
|
|
694
|
+
}
|
|
695
|
+
this.emit('replicator_cleanup_error', {
|
|
696
|
+
replicator: replicator.name || replicator.id || 'unknown',
|
|
697
|
+
driver: replicator.driver || 'unknown',
|
|
698
|
+
error: replicatorError.message
|
|
699
|
+
});
|
|
623
700
|
}
|
|
624
701
|
});
|
|
625
702
|
|
|
@@ -631,8 +708,15 @@ export class ReplicatorPlugin extends Plugin {
|
|
|
631
708
|
this.eventListenersInstalled.clear();
|
|
632
709
|
|
|
633
710
|
this.removeAllListeners();
|
|
634
|
-
}
|
|
635
|
-
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
if (!ok) {
|
|
714
|
+
if (this.config.verbose) {
|
|
715
|
+
console.warn(`[ReplicatorPlugin] Failed to cleanup plugin: ${error.message}`);
|
|
716
|
+
}
|
|
717
|
+
this.emit('replicator_plugin_cleanup_error', {
|
|
718
|
+
error: error.message
|
|
719
|
+
});
|
|
636
720
|
}
|
|
637
721
|
}
|
|
638
722
|
}
|
|
@@ -115,6 +115,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
115
115
|
await super.initialize(database);
|
|
116
116
|
const [ok, err, sdk] = await tryFn(() => import('@google-cloud/bigquery'));
|
|
117
117
|
if (!ok) {
|
|
118
|
+
if (this.config.verbose) {
|
|
119
|
+
console.warn(`[BigqueryReplicator] Failed to import BigQuery SDK: ${err.message}`);
|
|
120
|
+
}
|
|
118
121
|
this.emit('initialization_error', { replicator: this.name, error: err.message });
|
|
119
122
|
throw err;
|
|
120
123
|
}
|
|
@@ -206,21 +209,32 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
206
209
|
let lastError = null;
|
|
207
210
|
|
|
208
211
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
209
|
-
|
|
212
|
+
const [ok, error] = await tryFn(async () => {
|
|
210
213
|
const [updateJob] = await this.bigqueryClient.createQueryJob({
|
|
211
214
|
query,
|
|
212
215
|
params,
|
|
213
216
|
location: this.location
|
|
214
217
|
});
|
|
215
218
|
await updateJob.getQueryResults();
|
|
216
|
-
|
|
219
|
+
return [updateJob];
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (ok) {
|
|
223
|
+
job = ok;
|
|
217
224
|
break;
|
|
218
|
-
}
|
|
225
|
+
} else {
|
|
219
226
|
lastError = error;
|
|
220
227
|
|
|
228
|
+
if (this.config.verbose) {
|
|
229
|
+
console.warn(`[BigqueryReplicator] Update attempt ${attempt} failed: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
221
232
|
// If it's streaming buffer error and not the last attempt
|
|
222
233
|
if (error?.message?.includes('streaming buffer') && attempt < maxRetries) {
|
|
223
234
|
const delaySeconds = 30;
|
|
235
|
+
if (this.config.verbose) {
|
|
236
|
+
console.warn(`[BigqueryReplicator] Retrying in ${delaySeconds} seconds due to streaming buffer issue`);
|
|
237
|
+
}
|
|
224
238
|
await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000));
|
|
225
239
|
continue;
|
|
226
240
|
}
|
|
@@ -277,6 +291,12 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
277
291
|
}
|
|
278
292
|
|
|
279
293
|
const success = errors.length === 0;
|
|
294
|
+
|
|
295
|
+
// Log errors if any occurred
|
|
296
|
+
if (errors.length > 0) {
|
|
297
|
+
console.warn(`[BigqueryReplicator] Replication completed with errors for ${resourceName}:`, errors);
|
|
298
|
+
}
|
|
299
|
+
|
|
280
300
|
this.emit('replicated', {
|
|
281
301
|
replicator: this.name,
|
|
282
302
|
resourceName,
|
|
@@ -298,6 +318,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
298
318
|
|
|
299
319
|
if (ok) return result;
|
|
300
320
|
|
|
321
|
+
if (this.config.verbose) {
|
|
322
|
+
console.warn(`[BigqueryReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
323
|
+
}
|
|
301
324
|
this.emit('replicator_error', {
|
|
302
325
|
replicator: this.name,
|
|
303
326
|
resourceName,
|
|
@@ -321,8 +344,19 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
321
344
|
record.id,
|
|
322
345
|
record.beforeData
|
|
323
346
|
));
|
|
324
|
-
if (ok)
|
|
325
|
-
|
|
347
|
+
if (ok) {
|
|
348
|
+
results.push(res);
|
|
349
|
+
} else {
|
|
350
|
+
if (this.config.verbose) {
|
|
351
|
+
console.warn(`[BigqueryReplicator] Batch replication failed for record ${record.id}: ${err.message}`);
|
|
352
|
+
}
|
|
353
|
+
errors.push({ id: record.id, error: err.message });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Log errors if any occurred during batch processing
|
|
358
|
+
if (errors.length > 0) {
|
|
359
|
+
console.warn(`[BigqueryReplicator] Batch replication completed with ${errors.length} error(s) for ${resourceName}:`, errors);
|
|
326
360
|
}
|
|
327
361
|
|
|
328
362
|
return {
|
|
@@ -340,6 +374,9 @@ class BigqueryReplicator extends BaseReplicator {
|
|
|
340
374
|
return true;
|
|
341
375
|
});
|
|
342
376
|
if (ok) return true;
|
|
377
|
+
if (this.config.verbose) {
|
|
378
|
+
console.warn(`[BigqueryReplicator] Connection test failed: ${err.message}`);
|
|
379
|
+
}
|
|
343
380
|
this.emit('connection_error', { replicator: this.name, error: err.message });
|
|
344
381
|
return false;
|
|
345
382
|
}
|
|
@@ -113,6 +113,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
113
113
|
await super.initialize(database);
|
|
114
114
|
const [ok, err, sdk] = await tryFn(() => import('pg'));
|
|
115
115
|
if (!ok) {
|
|
116
|
+
if (this.config.verbose) {
|
|
117
|
+
console.warn(`[PostgresReplicator] Failed to import pg SDK: ${err.message}`);
|
|
118
|
+
}
|
|
116
119
|
this.emit('initialization_error', {
|
|
117
120
|
replicator: this.name,
|
|
118
121
|
error: err.message
|
|
@@ -258,6 +261,12 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
const success = errors.length === 0;
|
|
264
|
+
|
|
265
|
+
// Log errors if any occurred
|
|
266
|
+
if (errors.length > 0) {
|
|
267
|
+
console.warn(`[PostgresReplicator] Replication completed with errors for ${resourceName}:`, errors);
|
|
268
|
+
}
|
|
269
|
+
|
|
261
270
|
this.emit('replicated', {
|
|
262
271
|
replicator: this.name,
|
|
263
272
|
resourceName,
|
|
@@ -276,6 +285,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
276
285
|
};
|
|
277
286
|
});
|
|
278
287
|
if (ok) return result;
|
|
288
|
+
if (this.config.verbose) {
|
|
289
|
+
console.warn(`[PostgresReplicator] Replication failed for ${resourceName}: ${err.message}`);
|
|
290
|
+
}
|
|
279
291
|
this.emit('replicator_error', {
|
|
280
292
|
replicator: this.name,
|
|
281
293
|
resourceName,
|
|
@@ -298,8 +310,19 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
298
310
|
record.id,
|
|
299
311
|
record.beforeData
|
|
300
312
|
));
|
|
301
|
-
if (ok)
|
|
302
|
-
|
|
313
|
+
if (ok) {
|
|
314
|
+
results.push(res);
|
|
315
|
+
} else {
|
|
316
|
+
if (this.config.verbose) {
|
|
317
|
+
console.warn(`[PostgresReplicator] Batch replication failed for record ${record.id}: ${err.message}`);
|
|
318
|
+
}
|
|
319
|
+
errors.push({ id: record.id, error: err.message });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Log errors if any occurred during batch processing
|
|
324
|
+
if (errors.length > 0) {
|
|
325
|
+
console.warn(`[PostgresReplicator] Batch replication completed with ${errors.length} error(s) for ${resourceName}:`, errors);
|
|
303
326
|
}
|
|
304
327
|
|
|
305
328
|
return {
|
|
@@ -316,6 +339,9 @@ class PostgresReplicator extends BaseReplicator {
|
|
|
316
339
|
return true;
|
|
317
340
|
});
|
|
318
341
|
if (ok) return true;
|
|
342
|
+
if (this.config.verbose) {
|
|
343
|
+
console.warn(`[PostgresReplicator] Connection test failed: ${err.message}`);
|
|
344
|
+
}
|
|
319
345
|
this.emit('connection_error', { replicator: this.name, error: err.message });
|
|
320
346
|
return false;
|
|
321
347
|
}
|