@powersync/service-module-mongodb-storage 0.16.0 → 0.18.0
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/CHANGELOG.md +53 -0
- package/dist/storage/MongoBucketStorage.d.ts +35 -15
- package/dist/storage/MongoBucketStorage.js +309 -86
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/CheckpointState.d.ts +20 -0
- package/dist/storage/implementation/CheckpointState.js +31 -0
- package/dist/storage/implementation/CheckpointState.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.d.ts +69 -39
- package/dist/storage/implementation/MongoBucketBatch.js +104 -313
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoChecksums.d.ts +11 -9
- package/dist/storage/implementation/MongoChecksums.js +3 -111
- package/dist/storage/implementation/MongoChecksums.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +3 -1
- package/dist/storage/implementation/MongoCompactor.js +18 -9
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoHydrationState.d.ts +11 -0
- package/dist/storage/implementation/MongoHydrationState.js +49 -0
- package/dist/storage/implementation/MongoHydrationState.js.map +1 -0
- package/dist/storage/implementation/MongoParameterCompactor.d.ts +6 -5
- package/dist/storage/implementation/MongoParameterCompactor.js +24 -1
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +13 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js +64 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +36 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.js +90 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +25 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +121 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
- package/dist/storage/implementation/MongoStorageProvider.js +5 -2
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +50 -21
- package/dist/storage/implementation/MongoSyncBucketStorage.js +62 -199
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -1
- package/dist/storage/implementation/MongoSyncRulesLock.js +9 -5
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +14 -0
- package/dist/storage/implementation/SyncRuleStateUpdate.js +36 -0
- package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -0
- package/dist/storage/implementation/common/BucketDataDoc.d.ts +1 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +6 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js +2 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js.map +1 -0
- package/dist/storage/implementation/common/PersistedBatch.d.ts +2 -3
- package/dist/storage/implementation/common/PersistedBatch.js +5 -0
- package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
- package/dist/storage/implementation/common/SourceRecordStore.d.ts +1 -2
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +1 -1
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
- package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
- package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
- package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/db.d.ts +10 -2
- package/dist/storage/implementation/db.js +9 -14
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +25 -50
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +23 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +403 -2
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoChecksumsV1.js +114 -3
- package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +1 -1
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +22 -15
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +100 -27
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
- package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +1 -2
- package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +2 -1
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +3 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -1
- package/dist/storage/implementation/v1/models.d.ts +21 -1
- package/dist/storage/implementation/v1/models.js.map +1 -1
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +21 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +459 -3
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +27 -7
- package/dist/storage/implementation/v3/MongoChecksumsV3.js +202 -18
- package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +20 -3
- package/dist/storage/implementation/v3/MongoCompactorV3.js +511 -22
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +62 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +350 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +50 -16
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +292 -75
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +2 -3
- package/dist/storage/implementation/v3/PersistedBatchV3.js +43 -20
- package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js +52 -16
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -1
- package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +3 -3
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js +13 -11
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +16 -14
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +32 -27
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
- package/dist/storage/implementation/v3/bucket-format.d.ts +4 -0
- package/dist/storage/implementation/v3/bucket-format.js +58 -0
- package/dist/storage/implementation/v3/bucket-format.js.map +1 -0
- package/dist/storage/implementation/v3/chunking.d.ts +10 -0
- package/dist/storage/implementation/v3/chunking.js +28 -0
- package/dist/storage/implementation/v3/chunking.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +111 -10
- package/dist/storage/implementation/v3/models.js +11 -28
- package/dist/storage/implementation/v3/models.js.map +1 -1
- package/dist/storage/implementation/v3/source-table-utils.d.ts +55 -0
- package/dist/storage/implementation/v3/source-table-utils.js +214 -0
- package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
- package/dist/storage/storage-index.d.ts +4 -4
- package/dist/storage/storage-index.js +4 -4
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/types/types.d.ts +2 -2
- package/dist/utils/test-utils.d.ts +1 -2
- package/dist/utils/test-utils.js +5 -1
- package/dist/utils/test-utils.js.map +1 -1
- package/dist/utils/util.d.ts +1 -1
- package/dist/utils/util.js +3 -2
- package/dist/utils/util.js.map +1 -1
- package/package.json +9 -19
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +1 -1
- package/src/storage/MongoBucketStorage.ts +464 -95
- package/src/storage/implementation/CheckpointState.ts +59 -0
- package/src/storage/implementation/MongoBucketBatch.ts +159 -405
- package/src/storage/implementation/MongoChecksums.ts +7 -138
- package/src/storage/implementation/MongoCompactor.ts +20 -10
- package/src/storage/implementation/MongoHydrationState.ts +61 -0
- package/src/storage/implementation/MongoParameterCompactor.ts +24 -5
- package/src/storage/implementation/MongoParsedSyncConfigSet.ts +88 -0
- package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
- package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +152 -0
- package/src/storage/implementation/MongoStorageProvider.ts +5 -2
- package/src/storage/implementation/MongoSyncBucketStorage.ts +89 -250
- package/src/storage/implementation/MongoSyncRulesLock.ts +14 -5
- package/src/storage/implementation/SyncRuleStateUpdate.ts +38 -0
- package/src/storage/implementation/common/BucketDataDoc.ts +1 -1
- package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +7 -0
- package/src/storage/implementation/common/PersistedBatch.ts +8 -4
- package/src/storage/implementation/common/SourceRecordStore.ts +1 -2
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
- package/src/storage/implementation/db.ts +16 -21
- package/src/storage/implementation/models.ts +28 -61
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +503 -5
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +137 -4
- package/src/storage/implementation/v1/MongoCompactorV1.ts +1 -1
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +150 -49
- package/src/storage/implementation/v1/PersistedBatchV1.ts +1 -2
- package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
- package/src/storage/implementation/v1/models.ts +25 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +611 -4
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +262 -26
- package/src/storage/implementation/v3/MongoCompactorV3.ts +647 -30
- package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +480 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +397 -111
- package/src/storage/implementation/v3/PersistedBatchV3.ts +63 -38
- package/src/storage/implementation/v3/SingleBucketStoreV3.ts +59 -19
- package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +48 -38
- package/src/storage/implementation/v3/bucket-format.ts +70 -0
- package/src/storage/implementation/v3/chunking.ts +38 -0
- package/src/storage/implementation/v3/models.ts +150 -44
- package/src/storage/implementation/v3/source-table-utils.ts +392 -0
- package/src/storage/storage-index.ts +12 -4
- package/src/utils/test-utils.ts +6 -3
- package/src/utils/util.ts +3 -2
- package/test/src/__snapshots__/storage.test.ts.snap +0 -75
- package/test/src/__snapshots__/storage_sync.test.ts.snap +48 -48
- package/test/src/cleanup-stopped-sync-configs.test.ts +648 -0
- package/test/src/storage.test.ts +11 -11
- package/test/src/storage_compacting.test.ts +1956 -6
- package/test/src/storage_sync.test.ts +1429 -42
- package/test/src/storeCurrentData.test.ts +221 -0
- package/test/src/util.ts +2 -1
- package/tsconfig.json +1 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -17
- package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
- package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
- package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -64
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -15
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -42
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +0 -9
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +0 -18
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +0 -1
- package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -5
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
- package/src/storage/implementation/BucketDefinitionMapping.ts +0 -72
- package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -76
- package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -56
- package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +0 -15
- package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +0 -24
- package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import * as bson from 'bson';
|
|
3
3
|
import { BaseObserver, container, ErrorCode, errors, ReplicationAssertionError, ServiceError } from '@powersync/lib-services-framework';
|
|
4
|
-
import { deserializeBson, isCompleteRow, PerformanceTracer, SaveOperationTag, storage,
|
|
4
|
+
import { deserializeBson, isCompleteRow, PerformanceTracer, SaveOperationTag, storage, utils } from '@powersync/service-core';
|
|
5
5
|
import * as timers from 'node:timers/promises';
|
|
6
6
|
import { mongoTableId } from '../../utils/util.js';
|
|
7
7
|
import { MAX_ROW_SIZE } from './MongoBucketBatchShared.js';
|
|
@@ -15,38 +15,40 @@ import { OperationBatch, RecordOperation } from './OperationBatch.js';
|
|
|
15
15
|
// In the future, we can investigate allowing multiple replication streams operating independently.
|
|
16
16
|
const replicationMutex = new utils.Mutex();
|
|
17
17
|
export class MongoBucketBatch extends BaseObserver {
|
|
18
|
+
options;
|
|
18
19
|
logger;
|
|
19
20
|
client;
|
|
20
21
|
db;
|
|
21
22
|
session;
|
|
22
23
|
sync_rules;
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
replicationStreamId;
|
|
25
|
+
replicationStreamName;
|
|
26
|
+
/**
|
|
27
|
+
* Source-level setting for whether raw row data should be stored in current_data.
|
|
28
|
+
*
|
|
29
|
+
* Some sources always send complete rows (MongoDB, MySQL with binlog_row_image=full),
|
|
30
|
+
* in which case this is false for the whole batch. For sources where it depends on the
|
|
31
|
+
* table (Postgres REPLICA IDENTITY), this is true and the decision is refined per-table
|
|
32
|
+
* via SourceTable.storeCurrentData. The effective per-record value is the conjunction of
|
|
33
|
+
* the two.
|
|
34
|
+
*/
|
|
25
35
|
storeCurrentData;
|
|
26
36
|
skipExistingRows;
|
|
27
37
|
mapping;
|
|
28
38
|
batch = null;
|
|
29
39
|
write_checkpoint_batch = [];
|
|
30
40
|
markRecordUnavailable;
|
|
41
|
+
hooks;
|
|
31
42
|
clearedError = false;
|
|
32
43
|
tracer;
|
|
33
|
-
/**
|
|
34
|
-
* Last LSN received associated with a checkpoint.
|
|
35
|
-
*
|
|
36
|
-
* This could be either:
|
|
37
|
-
* 1. A commit LSN.
|
|
38
|
-
* 2. A keepalive message LSN.
|
|
39
|
-
*/
|
|
40
|
-
last_checkpoint_lsn = null;
|
|
41
|
-
persisted_op = null;
|
|
42
44
|
/**
|
|
43
45
|
* Last written op, if any. This may not reflect a consistent checkpoint.
|
|
44
46
|
*/
|
|
45
47
|
last_flushed_op = null;
|
|
46
48
|
/**
|
|
47
|
-
*
|
|
49
|
+
* LSN to resume replication from.
|
|
48
50
|
*
|
|
49
|
-
*
|
|
51
|
+
* This is typically the last commit LSN, but there are cases where it differs:
|
|
50
52
|
* 1. After an initial snapshot, we don't have a consistent commit yet, but need to resume from the snapshot LSN.
|
|
51
53
|
* 2. If "no_checkpoint_before_lsn" is set far in advance, it may take a while to reach that point. We
|
|
52
54
|
* may want to resume at incremental points before that.
|
|
@@ -54,35 +56,31 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
54
56
|
* This is set when creating the batch, but may not be updated afterwards.
|
|
55
57
|
*/
|
|
56
58
|
resumeFromLsn = null;
|
|
57
|
-
needsActivation = true;
|
|
58
59
|
constructor(options) {
|
|
59
60
|
super();
|
|
60
61
|
this.logger = options.logger;
|
|
62
|
+
this.options = options;
|
|
61
63
|
this.client = options.db.client;
|
|
62
64
|
this.db = options.db;
|
|
63
|
-
this.
|
|
64
|
-
this.last_checkpoint_lsn = options.lastCheckpointLsn;
|
|
65
|
+
this.replicationStreamId = options.replicationStreamId;
|
|
65
66
|
this.resumeFromLsn = options.resumeFromLsn;
|
|
66
67
|
this.session = this.client.startSession();
|
|
67
|
-
this.
|
|
68
|
-
this.sync_rules = options.
|
|
68
|
+
this.replicationStreamName = options.replicationStreamName;
|
|
69
|
+
this.sync_rules = options.parsedSyncConfig.hydratedSyncConfig;
|
|
69
70
|
this.storeCurrentData = options.storeCurrentData;
|
|
70
|
-
this.mapping = options.mapping;
|
|
71
|
+
this.mapping = options.parsedSyncConfig.mapping;
|
|
71
72
|
this.skipExistingRows = options.skipExistingRows;
|
|
72
73
|
this.markRecordUnavailable = options.markRecordUnavailable;
|
|
74
|
+
this.hooks = options.hooks;
|
|
73
75
|
this.batch = new OperationBatch();
|
|
74
|
-
this.persisted_op = options.keepaliveOp ?? null;
|
|
75
76
|
this.tracer = options.tracer ?? new PerformanceTracer('MongoDB storage');
|
|
76
77
|
}
|
|
77
78
|
addCustomWriteCheckpoint(checkpoint) {
|
|
78
79
|
this.write_checkpoint_batch.push({
|
|
79
80
|
...checkpoint,
|
|
80
|
-
sync_rules_id: this.
|
|
81
|
+
sync_rules_id: this.replicationStreamId
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
|
-
get lastCheckpointLsn() {
|
|
84
|
-
return this.last_checkpoint_lsn;
|
|
85
|
-
}
|
|
86
84
|
async flush(options) {
|
|
87
85
|
let result = null;
|
|
88
86
|
// One flush may be split over multiple transactions.
|
|
@@ -100,6 +98,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
100
98
|
let last_op = null;
|
|
101
99
|
let resumeBatch = null;
|
|
102
100
|
using _ = this.tracer.span('storage', 'flush');
|
|
101
|
+
await this.hooks?.beforeBatchFlush?.(this);
|
|
103
102
|
await this.withReplicationTransaction(`Flushing ${batch?.length ?? 0} ops`, async (session, opSeq) => {
|
|
104
103
|
if (batch != null) {
|
|
105
104
|
resumeBatch = await this.replicateBatch(session, batch, opSeq, options);
|
|
@@ -116,15 +115,19 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
116
115
|
if (last_op == null) {
|
|
117
116
|
throw new ReplicationAssertionError('Unexpected last_op == null');
|
|
118
117
|
}
|
|
119
|
-
this.
|
|
118
|
+
this.recordPersistedOp(last_op);
|
|
120
119
|
this.last_flushed_op = last_op;
|
|
120
|
+
await this.hooks?.afterBatchFlush?.(this);
|
|
121
121
|
return { flushed_op: last_op };
|
|
122
122
|
}
|
|
123
123
|
async replicateBatch(session, batch, op_seq, options) {
|
|
124
124
|
let sizes = undefined;
|
|
125
125
|
using _ = this.tracer.span('storage', 'replicate_batch');
|
|
126
|
-
if
|
|
127
|
-
|
|
126
|
+
// Only look up current_data sizes if the batch stores current_data and at least one
|
|
127
|
+
// table in it does too (per-table can disable it, e.g. Postgres REPLICA IDENTITY FULL).
|
|
128
|
+
const anyTableStoresCurrentData = this.storeCurrentData && batch.batch.some((r) => r.record.sourceTable.storeCurrentData);
|
|
129
|
+
if (anyTableStoresCurrentData && !this.skipExistingRows) {
|
|
130
|
+
// We skip this step if no tables store current_data, since the sizes will
|
|
128
131
|
// always be small in that case.
|
|
129
132
|
// With skipExistingRows, we don't load the full documents into memory,
|
|
130
133
|
// so we can also skip the size lookup step.
|
|
@@ -135,7 +138,11 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
135
138
|
// (automatically limited to 48MB(?) per batch by MongoDB). The issue is that it changes
|
|
136
139
|
// the order of processing, which then becomes really tricky to manage.
|
|
137
140
|
// This now takes 2+ queries, but doesn't have any issues with order of operations.
|
|
138
|
-
|
|
141
|
+
// Within this branch this.storeCurrentData is true, so the per-table flag is the
|
|
142
|
+
// effective value - only look up sizes for tables that actually store current_data.
|
|
143
|
+
const sizeLookups = batch.batch
|
|
144
|
+
.filter((r) => r.record.sourceTable.storeCurrentData)
|
|
145
|
+
.map((r) => ({
|
|
139
146
|
sourceTableId: mongoTableId(r.record.sourceTable.id),
|
|
140
147
|
replicaId: r.beforeId
|
|
141
148
|
}));
|
|
@@ -217,6 +224,9 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
217
224
|
const afterId = operation.afterId;
|
|
218
225
|
let after = record.after;
|
|
219
226
|
const sourceTable = record.sourceTable;
|
|
227
|
+
// Effective per-record flag: store current_data only if both the batch (source-level,
|
|
228
|
+
// e.g. Postgres) and the table (e.g. non-FULL replica identity) require it.
|
|
229
|
+
const storeCurrentData = this.storeCurrentData && sourceTable.storeCurrentData;
|
|
220
230
|
let existing_buckets = [];
|
|
221
231
|
let new_buckets = [];
|
|
222
232
|
let existing_lookups = [];
|
|
@@ -242,7 +252,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
242
252
|
// Not an error if we re-apply a transaction
|
|
243
253
|
existing_buckets = [];
|
|
244
254
|
existing_lookups = [];
|
|
245
|
-
if (!isCompleteRow(
|
|
255
|
+
if (!isCompleteRow(storeCurrentData, after)) {
|
|
246
256
|
if (this.markRecordUnavailable != null) {
|
|
247
257
|
// This will trigger a "resnapshot" of the record.
|
|
248
258
|
// This is not relevant if storeCurrentData is false, since we'll get the full row
|
|
@@ -258,7 +268,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
258
268
|
else {
|
|
259
269
|
existing_buckets = result.buckets;
|
|
260
270
|
existing_lookups = result.lookups;
|
|
261
|
-
if (
|
|
271
|
+
if (storeCurrentData && result.data != null) {
|
|
262
272
|
const data = deserializeBson(result.data.buffer);
|
|
263
273
|
after = storage.mergeToast(after, data);
|
|
264
274
|
}
|
|
@@ -270,7 +280,9 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
270
280
|
// Not an error if we re-apply a transaction
|
|
271
281
|
existing_buckets = [];
|
|
272
282
|
existing_lookups = [];
|
|
273
|
-
// Log to help with debugging if there was a consistency issue
|
|
283
|
+
// Log to help with debugging if there was a consistency issue.
|
|
284
|
+
// Gate on the batch-level flag: FULL tables (per-record flag false) still get a
|
|
285
|
+
// current_data entry, so a missing record on DELETE is meaningful for them too.
|
|
274
286
|
if (this.storeCurrentData && this.markRecordUnavailable == null) {
|
|
275
287
|
this.logger.warn(`Cannot find previous record for delete on ${record.sourceTable.qualifiedName}: ${beforeId} / ${record.before?.id}`);
|
|
276
288
|
}
|
|
@@ -281,7 +293,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
281
293
|
}
|
|
282
294
|
}
|
|
283
295
|
let afterData = null;
|
|
284
|
-
if (afterId != null && !
|
|
296
|
+
if (afterId != null && !storeCurrentData) {
|
|
285
297
|
afterData = null;
|
|
286
298
|
}
|
|
287
299
|
else if (afterId != null) {
|
|
@@ -302,7 +314,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
302
314
|
container.reporter.captureMessage(`Data too big on ${record.sourceTable.qualifiedName}.${record.after?.id}: ${e.message}`, {
|
|
303
315
|
level: errors.ErrorSeverity.WARNING,
|
|
304
316
|
metadata: {
|
|
305
|
-
replication_slot: this.
|
|
317
|
+
replication_slot: this.replicationStreamName,
|
|
306
318
|
table: record.sourceTable.qualifiedName
|
|
307
319
|
}
|
|
308
320
|
});
|
|
@@ -341,18 +353,20 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
341
353
|
// However, it will be valid by the end of the transaction.
|
|
342
354
|
//
|
|
343
355
|
// In this case, we don't save the op, but we do save the current data.
|
|
344
|
-
if (afterId && after && utils.isCompleteRow(
|
|
356
|
+
if (afterId && after && utils.isCompleteRow(storeCurrentData, after)) {
|
|
345
357
|
// Insert or update
|
|
346
358
|
if (sourceTable.syncData) {
|
|
347
|
-
const { results
|
|
359
|
+
const { results, errors: syncErrors } = this.sync_rules.evaluateRowWithErrors({
|
|
348
360
|
record: after,
|
|
349
|
-
sourceTable
|
|
361
|
+
sourceTable: sourceTable.ref,
|
|
362
|
+
bucketDataSources: sourceTable.bucketDataSources
|
|
350
363
|
});
|
|
364
|
+
const evaluated = results;
|
|
351
365
|
for (let error of syncErrors) {
|
|
352
366
|
container.reporter.captureMessage(`Failed to evaluate data query on ${record.sourceTable.qualifiedName}.${record.after?.id}: ${error.error}`, {
|
|
353
367
|
level: errors.ErrorSeverity.WARNING,
|
|
354
368
|
metadata: {
|
|
355
|
-
replication_slot: this.
|
|
369
|
+
replication_slot: this.replicationStreamName,
|
|
356
370
|
table: record.sourceTable.qualifiedName
|
|
357
371
|
}
|
|
358
372
|
});
|
|
@@ -370,12 +384,12 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
370
384
|
}
|
|
371
385
|
if (sourceTable.syncParameters) {
|
|
372
386
|
// Parameters
|
|
373
|
-
const { results: paramEvaluated, errors: paramErrors } = this.sync_rules.evaluateParameterRowWithErrors(sourceTable, after);
|
|
387
|
+
const { results: paramEvaluated, errors: paramErrors } = this.sync_rules.evaluateParameterRowWithErrors(sourceTable.ref, after, { parameterLookupSources: sourceTable.parameterLookupSources });
|
|
374
388
|
for (let error of paramErrors) {
|
|
375
389
|
container.reporter.captureMessage(`Failed to evaluate parameter query on ${record.sourceTable.qualifiedName}.${record.after?.id}: ${error.error}`, {
|
|
376
390
|
level: errors.ErrorSeverity.WARNING,
|
|
377
391
|
metadata: {
|
|
378
|
-
replication_slot: this.
|
|
392
|
+
replication_slot: this.replicationStreamName,
|
|
379
393
|
table: record.sourceTable.qualifiedName
|
|
380
394
|
}
|
|
381
395
|
});
|
|
@@ -481,17 +495,43 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
481
495
|
session
|
|
482
496
|
});
|
|
483
497
|
await this.db.sync_rules.updateOne({
|
|
484
|
-
_id: this.
|
|
498
|
+
_id: this.replicationStreamId
|
|
485
499
|
}, {
|
|
486
500
|
$set: {
|
|
487
501
|
last_keepalive_ts: new Date()
|
|
488
502
|
}
|
|
489
503
|
}, { session });
|
|
504
|
+
// Allow subclasses to persist additional flush-time state in the same transaction
|
|
505
|
+
// (e.g. v3 advances the stream-level last_persisted_op).
|
|
506
|
+
await this.onReplicationTransactionFlush(session, opSeq.last());
|
|
490
507
|
// We don't notify checkpoint here - we don't make any checkpoint updates directly
|
|
491
508
|
});
|
|
492
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Hook called inside the replication flush transaction, after ops have been persisted.
|
|
512
|
+
*
|
|
513
|
+
* The base implementation does nothing; v3 storage overrides this to `$max` the stream-level
|
|
514
|
+
* `last_persisted_op` durably within the same transaction.
|
|
515
|
+
*/
|
|
516
|
+
async onReplicationTransactionFlush(_session, _lastOp) {
|
|
517
|
+
// No-op by default (v1 behaviour unchanged).
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Called after a replication transaction has successfully committed, with the last persisted op id.
|
|
521
|
+
*
|
|
522
|
+
* v1 storage tracks this in memory to fold into the next checkpoint. v3 storage does not need it:
|
|
523
|
+
* the stream-level `last_persisted_op` is already advanced durably by
|
|
524
|
+
* {@link onReplicationTransactionFlush} within the same transaction, and checkpoints read it
|
|
525
|
+
* from the document.
|
|
526
|
+
*
|
|
527
|
+
* Keep calls to this adjacent to {@link withReplicationTransaction} usage - both must observe
|
|
528
|
+
* every path that persists ops.
|
|
529
|
+
*/
|
|
530
|
+
recordPersistedOp(_lastOp) {
|
|
531
|
+
// No-op by default.
|
|
532
|
+
}
|
|
493
533
|
async [Symbol.asyncDispose]() {
|
|
494
|
-
if (this.batch
|
|
534
|
+
if (this.batch?.hasData() || this.write_checkpoint_batch.length > 0) {
|
|
495
535
|
// We don't error here, since:
|
|
496
536
|
// 1. In error states, this is expected (we can't distinguish between disposing after success or error).
|
|
497
537
|
// 2. SuppressedError is messy to deal with.
|
|
@@ -503,221 +543,25 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
503
543
|
async dispose() {
|
|
504
544
|
await this[Symbol.asyncDispose]();
|
|
505
545
|
}
|
|
506
|
-
lastWaitingLogThottled = 0;
|
|
507
|
-
async commit(lsn, options) {
|
|
508
|
-
const { createEmptyCheckpoints } = { ...storage.DEFAULT_BUCKET_BATCH_COMMIT_OPTIONS, ...options };
|
|
509
|
-
await this.flush(options);
|
|
510
|
-
const now = new Date();
|
|
511
|
-
// Mark relevant write checkpoints as "processed".
|
|
512
|
-
// This makes it easier to identify write checkpoints that are "valid" in order.
|
|
513
|
-
await this.db.write_checkpoints.updateMany({
|
|
514
|
-
processed_at_lsn: null,
|
|
515
|
-
'lsns.1': { $lte: lsn }
|
|
516
|
-
}, {
|
|
517
|
-
$set: {
|
|
518
|
-
processed_at_lsn: lsn
|
|
519
|
-
}
|
|
520
|
-
}, {
|
|
521
|
-
session: this.session
|
|
522
|
-
});
|
|
523
|
-
const can_checkpoint = {
|
|
524
|
-
$and: [
|
|
525
|
-
{ $eq: ['$snapshot_done', true] },
|
|
526
|
-
{
|
|
527
|
-
$or: [{ $eq: ['$last_checkpoint_lsn', null] }, { $lte: ['$last_checkpoint_lsn', { $literal: lsn }] }]
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
$or: [{ $eq: ['$no_checkpoint_before', null] }, { $lte: ['$no_checkpoint_before', { $literal: lsn }] }]
|
|
531
|
-
}
|
|
532
|
-
]
|
|
533
|
-
};
|
|
534
|
-
const new_keepalive_op = {
|
|
535
|
-
$cond: [
|
|
536
|
-
can_checkpoint,
|
|
537
|
-
{ $literal: null },
|
|
538
|
-
{
|
|
539
|
-
$toString: {
|
|
540
|
-
$max: [{ $toLong: '$keepalive_op' }, { $literal: this.persisted_op }, 0n]
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
]
|
|
544
|
-
};
|
|
545
|
-
const new_last_checkpoint = {
|
|
546
|
-
$cond: [
|
|
547
|
-
can_checkpoint,
|
|
548
|
-
{
|
|
549
|
-
$max: ['$last_checkpoint', { $literal: this.persisted_op }, { $toLong: '$keepalive_op' }, 0n]
|
|
550
|
-
},
|
|
551
|
-
'$last_checkpoint'
|
|
552
|
-
]
|
|
553
|
-
};
|
|
554
|
-
// For this query, we need to handle multiple cases, depending on the state:
|
|
555
|
-
// 1. Normal commit - advance last_checkpoint to this.persisted_op.
|
|
556
|
-
// 2. Commit delayed by no_checkpoint_before due to snapshot. In this case we only advance keepalive_op.
|
|
557
|
-
// 3. Commit with no new data - here may may set last_checkpoint = keepalive_op, if a delayed commit is relevant.
|
|
558
|
-
// We want to do as much as possible in a single atomic database operation, which makes this somewhat complex.
|
|
559
|
-
let preUpdateDocument = await this.db.sync_rules.findOneAndUpdate({ _id: this.group_id }, [
|
|
560
|
-
{
|
|
561
|
-
$set: {
|
|
562
|
-
_can_checkpoint: can_checkpoint,
|
|
563
|
-
_not_empty: createEmptyCheckpoints
|
|
564
|
-
? true
|
|
565
|
-
: {
|
|
566
|
-
$or: [
|
|
567
|
-
{ $literal: createEmptyCheckpoints },
|
|
568
|
-
{ $ne: ['$keepalive_op', new_keepalive_op] },
|
|
569
|
-
{ $ne: ['$last_checkpoint', new_last_checkpoint] }
|
|
570
|
-
]
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
$set: {
|
|
576
|
-
last_checkpoint_lsn: {
|
|
577
|
-
$cond: [{ $and: ['$_can_checkpoint', '$_not_empty'] }, { $literal: lsn }, '$last_checkpoint_lsn']
|
|
578
|
-
},
|
|
579
|
-
last_checkpoint_ts: {
|
|
580
|
-
$cond: [{ $and: ['$_can_checkpoint', '$_not_empty'] }, { $literal: now }, '$last_checkpoint_ts']
|
|
581
|
-
},
|
|
582
|
-
last_keepalive_ts: { $literal: now },
|
|
583
|
-
last_fatal_error: { $literal: null },
|
|
584
|
-
last_fatal_error_ts: { $literal: null },
|
|
585
|
-
keepalive_op: new_keepalive_op,
|
|
586
|
-
last_checkpoint: new_last_checkpoint,
|
|
587
|
-
// Unset snapshot_lsn on checkpoint
|
|
588
|
-
snapshot_lsn: {
|
|
589
|
-
$cond: [{ $and: ['$_can_checkpoint', '$_not_empty'] }, { $literal: null }, '$snapshot_lsn']
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
$unset: ['_can_checkpoint', '_not_empty']
|
|
595
|
-
}
|
|
596
|
-
], {
|
|
597
|
-
session: this.session,
|
|
598
|
-
// We return the before document, so that we can check the previous state to determine if a checkpoint was actually created or if we were blocked by snapshot/no_checkpoint_before.
|
|
599
|
-
returnDocument: 'before',
|
|
600
|
-
projection: {
|
|
601
|
-
snapshot_done: 1,
|
|
602
|
-
last_checkpoint_lsn: 1,
|
|
603
|
-
no_checkpoint_before: 1,
|
|
604
|
-
keepalive_op: 1,
|
|
605
|
-
last_checkpoint: 1
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
if (preUpdateDocument == null) {
|
|
609
|
-
throw new ReplicationAssertionError('Failed to update checkpoint - no matching sync_rules document for _id: ' + this.group_id);
|
|
610
|
-
}
|
|
611
|
-
// This re-implements the same logic as in the pipeline, to determine what was actually updated.
|
|
612
|
-
// Unfortunately we cannot return these from the pipeline directly, so we need to re-implement the logic.
|
|
613
|
-
const canCheckpoint = preUpdateDocument.snapshot_done === true &&
|
|
614
|
-
(preUpdateDocument.last_checkpoint_lsn == null || preUpdateDocument.last_checkpoint_lsn <= lsn) &&
|
|
615
|
-
(preUpdateDocument.no_checkpoint_before == null || preUpdateDocument.no_checkpoint_before <= lsn);
|
|
616
|
-
const keepaliveOp = preUpdateDocument.keepalive_op == null ? null : BigInt(preUpdateDocument.keepalive_op);
|
|
617
|
-
const maxKeepalive = [keepaliveOp ?? 0n, this.persisted_op ?? 0n, 0n].reduce((a, b) => (a > b ? a : b));
|
|
618
|
-
const newKeepaliveOp = canCheckpoint ? null : maxKeepalive.toString();
|
|
619
|
-
const newLastCheckpoint = canCheckpoint
|
|
620
|
-
? [preUpdateDocument.last_checkpoint ?? 0n, this.persisted_op ?? 0n, keepaliveOp ?? 0n, 0n].reduce((a, b) => a > b ? a : b)
|
|
621
|
-
: preUpdateDocument.last_checkpoint;
|
|
622
|
-
const notEmpty = createEmptyCheckpoints ||
|
|
623
|
-
preUpdateDocument.keepalive_op !== newKeepaliveOp ||
|
|
624
|
-
preUpdateDocument.last_checkpoint !== newLastCheckpoint;
|
|
625
|
-
const checkpointCreated = canCheckpoint && notEmpty;
|
|
626
|
-
const checkpointBlocked = !canCheckpoint;
|
|
627
|
-
if (checkpointBlocked) {
|
|
628
|
-
// Failed on snapshot_done or no_checkpoint_before.
|
|
629
|
-
if (Date.now() - this.lastWaitingLogThottled > 5_000) {
|
|
630
|
-
this.logger.info(`Waiting before creating checkpoint, currently at ${lsn} / ${preUpdateDocument.keepalive_op}. Current state: ${JSON.stringify({
|
|
631
|
-
snapshot_done: preUpdateDocument.snapshot_done,
|
|
632
|
-
last_checkpoint_lsn: preUpdateDocument.last_checkpoint_lsn,
|
|
633
|
-
no_checkpoint_before: preUpdateDocument.no_checkpoint_before
|
|
634
|
-
})}`);
|
|
635
|
-
this.lastWaitingLogThottled = Date.now();
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
if (checkpointCreated) {
|
|
640
|
-
this.logger.debug(`Created checkpoint at ${lsn} / ${newLastCheckpoint}`);
|
|
641
|
-
}
|
|
642
|
-
await this.autoActivate(lsn);
|
|
643
|
-
await this.db.notifyCheckpoint();
|
|
644
|
-
this.persisted_op = null;
|
|
645
|
-
this.last_checkpoint_lsn = lsn;
|
|
646
|
-
if (newLastCheckpoint != null) {
|
|
647
|
-
await this.sourceRecordStore.postCommitCleanup(newLastCheckpoint, this.logger);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
return { checkpointBlocked, checkpointCreated };
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Switch from processing -> active if relevant.
|
|
654
|
-
*
|
|
655
|
-
* Called on new commits.
|
|
656
|
-
*/
|
|
657
|
-
async autoActivate(lsn) {
|
|
658
|
-
if (!this.needsActivation) {
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
// Activate the batch, so it can start processing.
|
|
662
|
-
// This is done automatically when the first save() is called.
|
|
663
|
-
const session = this.session;
|
|
664
|
-
let activated = false;
|
|
665
|
-
await session.withTransaction(async () => {
|
|
666
|
-
const doc = await this.db.sync_rules.findOne({ _id: this.group_id }, { session });
|
|
667
|
-
if (doc && doc.state == SyncRuleState.PROCESSING && doc.snapshot_done && doc.last_checkpoint != null) {
|
|
668
|
-
await this.db.sync_rules.updateOne({
|
|
669
|
-
_id: this.group_id
|
|
670
|
-
}, {
|
|
671
|
-
$set: {
|
|
672
|
-
state: storage.SyncRuleState.ACTIVE
|
|
673
|
-
}
|
|
674
|
-
}, { session });
|
|
675
|
-
await this.db.sync_rules.updateMany({
|
|
676
|
-
_id: { $ne: this.group_id },
|
|
677
|
-
state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
|
|
678
|
-
}, {
|
|
679
|
-
$set: {
|
|
680
|
-
state: storage.SyncRuleState.STOP
|
|
681
|
-
}
|
|
682
|
-
}, { session });
|
|
683
|
-
activated = true;
|
|
684
|
-
}
|
|
685
|
-
else if (doc?.state != SyncRuleState.PROCESSING) {
|
|
686
|
-
this.needsActivation = false;
|
|
687
|
-
}
|
|
688
|
-
});
|
|
689
|
-
if (activated) {
|
|
690
|
-
this.logger.info(`Activated new replication stream at ${lsn}`);
|
|
691
|
-
await this.db.notifyCheckpoint();
|
|
692
|
-
this.needsActivation = false;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
async keepalive(lsn) {
|
|
696
|
-
return await this.commit(lsn, { createEmptyCheckpoints: true });
|
|
697
|
-
}
|
|
698
|
-
async setResumeLsn(lsn) {
|
|
699
|
-
const update = {
|
|
700
|
-
snapshot_lsn: lsn
|
|
701
|
-
};
|
|
702
|
-
await this.db.sync_rules.updateOne({
|
|
703
|
-
_id: this.group_id
|
|
704
|
-
}, {
|
|
705
|
-
$set: update
|
|
706
|
-
}, { session: this.session });
|
|
707
|
-
}
|
|
708
546
|
async save(record) {
|
|
709
547
|
const { after, before, sourceTable, tag } = record;
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
548
|
+
const storeCurrentData = this.storeCurrentData && sourceTable.storeCurrentData;
|
|
549
|
+
// syncEvent is the per-table designation from resolveTables. With v3 storage, multiple
|
|
550
|
+
// SourceTables can exist for the same ref, with a row change saved once per table -
|
|
551
|
+
// only the designated event carrier may fire events, so each event fires once per row.
|
|
552
|
+
if (sourceTable.syncEvent) {
|
|
553
|
+
for (const event of this.getTableEvents(sourceTable)) {
|
|
554
|
+
this.iterateListeners((cb) => cb.replicationEvent?.({
|
|
555
|
+
batch: this,
|
|
556
|
+
table: sourceTable,
|
|
557
|
+
data: {
|
|
558
|
+
op: tag,
|
|
559
|
+
after: after && utils.isCompleteRow(storeCurrentData, after) ? after : undefined,
|
|
560
|
+
before: before && utils.isCompleteRow(storeCurrentData, before) ? before : undefined
|
|
561
|
+
},
|
|
562
|
+
event
|
|
563
|
+
}));
|
|
564
|
+
}
|
|
721
565
|
}
|
|
722
566
|
/**
|
|
723
567
|
* Return if the table is just an event table
|
|
@@ -744,7 +588,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
744
588
|
const result = await this.flush();
|
|
745
589
|
await this.withTransaction(async () => {
|
|
746
590
|
for (let table of sourceTables) {
|
|
747
|
-
await this.db.commonSourceTables(this.
|
|
591
|
+
await this.db.commonSourceTables(this.replicationStreamId).deleteOne({ _id: mongoTableId(table.id) });
|
|
748
592
|
}
|
|
749
593
|
});
|
|
750
594
|
await this.cleanupDroppedSourceTables(sourceTables);
|
|
@@ -757,7 +601,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
757
601
|
last_op = await this.truncateSingle(table);
|
|
758
602
|
}
|
|
759
603
|
if (last_op) {
|
|
760
|
-
this.
|
|
604
|
+
this.recordPersistedOp(last_op);
|
|
761
605
|
return {
|
|
762
606
|
flushed_op: last_op
|
|
763
607
|
};
|
|
@@ -814,7 +658,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
814
658
|
};
|
|
815
659
|
copy.snapshotStatus = snapshotStatus;
|
|
816
660
|
await this.withTransaction(async () => {
|
|
817
|
-
await this.db.commonSourceTables(this.
|
|
661
|
+
await this.db.commonSourceTables(this.replicationStreamId).updateOne({ _id: mongoTableId(table.id) }, {
|
|
818
662
|
$set: {
|
|
819
663
|
snapshot_status: {
|
|
820
664
|
last_key: snapshotStatus.lastKey == null ? null : new bson.Binary(snapshotStatus.lastKey),
|
|
@@ -826,66 +670,13 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
826
670
|
});
|
|
827
671
|
return copy;
|
|
828
672
|
}
|
|
829
|
-
async markAllSnapshotDone(no_checkpoint_before_lsn) {
|
|
830
|
-
await this.db.sync_rules.updateOne({
|
|
831
|
-
_id: this.group_id
|
|
832
|
-
}, {
|
|
833
|
-
$set: {
|
|
834
|
-
snapshot_done: true,
|
|
835
|
-
last_keepalive_ts: new Date()
|
|
836
|
-
},
|
|
837
|
-
$max: {
|
|
838
|
-
no_checkpoint_before: no_checkpoint_before_lsn
|
|
839
|
-
}
|
|
840
|
-
}, { session: this.session });
|
|
841
|
-
}
|
|
842
|
-
async markTableSnapshotRequired(table) {
|
|
843
|
-
await this.db.sync_rules.updateOne({
|
|
844
|
-
_id: this.group_id
|
|
845
|
-
}, {
|
|
846
|
-
$set: {
|
|
847
|
-
snapshot_done: false
|
|
848
|
-
}
|
|
849
|
-
}, { session: this.session });
|
|
850
|
-
}
|
|
851
|
-
async markTableSnapshotDone(tables, no_checkpoint_before_lsn) {
|
|
852
|
-
const session = this.session;
|
|
853
|
-
const ids = tables.map((table) => mongoTableId(table.id));
|
|
854
|
-
await this.withTransaction(async () => {
|
|
855
|
-
await this.db.commonSourceTables(this.group_id).updateMany({ _id: { $in: ids } }, {
|
|
856
|
-
$set: {
|
|
857
|
-
snapshot_done: true
|
|
858
|
-
},
|
|
859
|
-
$unset: {
|
|
860
|
-
snapshot_status: 1
|
|
861
|
-
}
|
|
862
|
-
}, { session });
|
|
863
|
-
if (no_checkpoint_before_lsn != null) {
|
|
864
|
-
await this.db.sync_rules.updateOne({
|
|
865
|
-
_id: this.group_id
|
|
866
|
-
}, {
|
|
867
|
-
$set: {
|
|
868
|
-
last_keepalive_ts: new Date()
|
|
869
|
-
},
|
|
870
|
-
$max: {
|
|
871
|
-
no_checkpoint_before: no_checkpoint_before_lsn
|
|
872
|
-
}
|
|
873
|
-
}, { session: this.session });
|
|
874
|
-
}
|
|
875
|
-
});
|
|
876
|
-
return tables.map((table) => {
|
|
877
|
-
const copy = table.clone();
|
|
878
|
-
copy.snapshotComplete = true;
|
|
879
|
-
return copy;
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
673
|
async clearError() {
|
|
883
674
|
// No need to clear an error more than once per batch, since an error would always result in restarting the batch.
|
|
884
675
|
if (this.clearedError) {
|
|
885
676
|
return;
|
|
886
677
|
}
|
|
887
678
|
await this.db.sync_rules.updateOne({
|
|
888
|
-
_id: this.
|
|
679
|
+
_id: this.replicationStreamId
|
|
889
680
|
}, {
|
|
890
681
|
$set: {
|
|
891
682
|
last_fatal_error: null,
|
|
@@ -898,7 +689,7 @@ export class MongoBucketBatch extends BaseObserver {
|
|
|
898
689
|
* Gets relevant {@link SqlEventDescriptor}s for the given {@link SourceTable}
|
|
899
690
|
*/
|
|
900
691
|
getTableEvents(table) {
|
|
901
|
-
return this.sync_rules.eventDescriptors.filter((evt) => [...evt.getSourceTables()].some((sourceTable) => sourceTable.matches(table)));
|
|
692
|
+
return this.sync_rules.eventDescriptors.filter((evt) => [...evt.getSourceTables()].some((sourceTable) => sourceTable.matches(table.ref)));
|
|
902
693
|
}
|
|
903
694
|
}
|
|
904
695
|
//# sourceMappingURL=MongoBucketBatch.js.map
|