@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
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
2
|
+
import { SingleSyncConfigBucketDefinitionMapping, storage } from '@powersync/service-core';
|
|
3
|
+
import * as bson from 'bson';
|
|
4
|
+
import {
|
|
5
|
+
ReplicationStreamDocumentV3,
|
|
6
|
+
SyncConfigDefinition,
|
|
7
|
+
SyncRuleConfigStateV3,
|
|
8
|
+
SyncRuleDocumentV1
|
|
9
|
+
} from '../storage-index.js';
|
|
10
|
+
import { PowerSyncMongo } from './db.js';
|
|
11
|
+
import { getMongoStorageConfig } from './models.js';
|
|
12
|
+
import { MongoParsedSyncConfigSet } from './MongoParsedSyncConfigSet.js';
|
|
13
|
+
|
|
14
|
+
export abstract class MongoPersistedSyncConfigContentBase extends storage.PersistedSyncConfigContent {
|
|
15
|
+
public readonly mapping: SingleSyncConfigBucketDefinitionMapping;
|
|
16
|
+
public readonly syncConfigObjectId: bson.ObjectId | null;
|
|
17
|
+
|
|
18
|
+
protected constructor(
|
|
19
|
+
protected readonly db: PowerSyncMongo,
|
|
20
|
+
options: Omit<storage.PersistedSyncConfigContentData, 'syncConfigId'> & {
|
|
21
|
+
mapping: SingleSyncConfigBucketDefinitionMapping;
|
|
22
|
+
syncConfigId: bson.ObjectId | null;
|
|
23
|
+
}
|
|
24
|
+
) {
|
|
25
|
+
const { mapping, syncConfigId, ...base } = options;
|
|
26
|
+
super({
|
|
27
|
+
...base,
|
|
28
|
+
syncConfigId: syncConfigId?.toHexString() ?? null
|
|
29
|
+
});
|
|
30
|
+
this.mapping = mapping;
|
|
31
|
+
this.syncConfigObjectId = syncConfigId;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getStorageConfig() {
|
|
35
|
+
return getMongoStorageConfig(this.storageVersion);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
parsed(options: storage.ParseSyncConfigOptions): MongoParsedSyncConfigSet {
|
|
39
|
+
const parsed = super.parsed(options);
|
|
40
|
+
const storageConfig = this.getStorageConfig();
|
|
41
|
+
const [syncConfig] = parsed.syncConfigs;
|
|
42
|
+
if (syncConfig == null) {
|
|
43
|
+
throw new ServiceAssertionError(`Expected one parsed sync config`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (storageConfig.incrementalReprocessing) {
|
|
47
|
+
const syncConfigId = this.syncConfigId;
|
|
48
|
+
if (syncConfigId == null) {
|
|
49
|
+
throw new ServiceAssertionError(`syncConfigId is required for v3 sync config content`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new MongoParsedSyncConfigSet(parsed.replicationStreamId, storageConfig, parsed.replicationStreamName, [
|
|
53
|
+
{ syncConfigId, syncConfig, mapping: this.mapping }
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return new MongoParsedSyncConfigSet(parsed.replicationStreamId, storageConfig, parsed.replicationStreamName, [
|
|
58
|
+
{ syncConfig, mapping: null }
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class MongoPersistedSyncConfigContentV1 extends MongoPersistedSyncConfigContentBase {
|
|
63
|
+
constructor(db: PowerSyncMongo, doc: SyncRuleDocumentV1) {
|
|
64
|
+
super(db, {
|
|
65
|
+
replicationStreamId: doc._id,
|
|
66
|
+
sync_rules_content: doc.content,
|
|
67
|
+
compiled_plan: doc.serialized_plan ?? null,
|
|
68
|
+
// Handle legacy values
|
|
69
|
+
replicationStreamName: doc.slot_name ?? `powersync_${doc._id}`,
|
|
70
|
+
storageVersion: doc.storage_version ?? storage.LEGACY_STORAGE_VERSION,
|
|
71
|
+
mapping: new SingleSyncConfigBucketDefinitionMapping(),
|
|
72
|
+
syncConfigId: null
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getSyncConfigStatus(): Promise<storage.PersistedSyncConfigStatus | null> {
|
|
77
|
+
const doc = await this.db.sync_rules.findOne<SyncRuleDocumentV1>({ _id: this.replicationStreamId });
|
|
78
|
+
if (doc == null) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return syncConfigStatusFromV1(doc);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class MongoPersistedSyncConfigContentV3 extends MongoPersistedSyncConfigContentBase {
|
|
86
|
+
declare public readonly syncConfigObjectId: bson.ObjectId;
|
|
87
|
+
|
|
88
|
+
constructor(db: PowerSyncMongo, doc: ReplicationStreamDocumentV3, config: SyncConfigDefinition) {
|
|
89
|
+
const state = doc.sync_configs.find((c) => c._id.equals(config._id));
|
|
90
|
+
if (state == null) {
|
|
91
|
+
throw new ServiceAssertionError(`Cannot find sync config ${config._id} in replication stream ${doc._id}`);
|
|
92
|
+
}
|
|
93
|
+
super(db, {
|
|
94
|
+
replicationStreamId: doc._id,
|
|
95
|
+
sync_rules_content: config.content,
|
|
96
|
+
compiled_plan: config.serialized_plan ?? null,
|
|
97
|
+
|
|
98
|
+
replicationStreamName: doc.slot_name ?? `powersync_${doc._id}`,
|
|
99
|
+
storageVersion: doc.storage_version,
|
|
100
|
+
mapping: SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(config.rule_mapping),
|
|
101
|
+
syncConfigId: config._id
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getSyncConfigStatus(): Promise<storage.PersistedSyncConfigStatus | null> {
|
|
106
|
+
const doc = await this.db.sync_rules.findOne<ReplicationStreamDocumentV3>({
|
|
107
|
+
_id: this.replicationStreamId,
|
|
108
|
+
'sync_configs._id': this.syncConfigObjectId
|
|
109
|
+
});
|
|
110
|
+
if (doc == null) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const state = doc.sync_configs.find((c) => c._id.equals(this.syncConfigObjectId));
|
|
115
|
+
if (state == null) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return syncConfigStatusFromV3(doc, state);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function syncConfigStatusFromV1(doc: SyncRuleDocumentV1): storage.PersistedSyncConfigStatus {
|
|
124
|
+
return {
|
|
125
|
+
id: String(doc._id),
|
|
126
|
+
replicationStreamId: doc._id,
|
|
127
|
+
state: doc.state,
|
|
128
|
+
snapshot_done: doc.snapshot_done,
|
|
129
|
+
last_checkpoint_lsn: doc.last_checkpoint_lsn,
|
|
130
|
+
last_fatal_error: doc.last_fatal_error,
|
|
131
|
+
last_fatal_error_ts: doc.last_fatal_error_ts,
|
|
132
|
+
last_keepalive_ts: doc.last_keepalive_ts,
|
|
133
|
+
last_checkpoint_ts: doc.last_checkpoint_ts
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function syncConfigStatusFromV3(
|
|
138
|
+
doc: ReplicationStreamDocumentV3,
|
|
139
|
+
state: SyncRuleConfigStateV3
|
|
140
|
+
): storage.PersistedSyncConfigStatus {
|
|
141
|
+
return {
|
|
142
|
+
id: state._id.toHexString(),
|
|
143
|
+
replicationStreamId: doc._id,
|
|
144
|
+
state: state.state,
|
|
145
|
+
snapshot_done: state.snapshot_done,
|
|
146
|
+
last_checkpoint_lsn: state.last_checkpoint_lsn,
|
|
147
|
+
last_fatal_error: doc.last_fatal_error,
|
|
148
|
+
last_fatal_error_ts: doc.last_fatal_error_ts,
|
|
149
|
+
last_keepalive_ts: doc.last_keepalive_ts,
|
|
150
|
+
last_checkpoint_ts: doc.last_checkpoint_ts
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -39,8 +39,11 @@ export class MongoStorageProvider implements storage.StorageProvider {
|
|
|
39
39
|
|
|
40
40
|
const database = new PowerSyncMongo(client, { database: resolvedConfig.storage.database });
|
|
41
41
|
const syncStorageFactory = new MongoBucketStorage(database, {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
replicationStreamNamePrefix: resolvedConfig.slot_name_prefix,
|
|
43
|
+
// Right now, only MongoDB source databases supports incremental reprocessing.
|
|
44
|
+
// Remove this filter when we support it for other source databases.
|
|
45
|
+
// This assumes a single source connection - revisit if we ever support multiple connections.
|
|
46
|
+
supportsMultipleSyncConfigs: resolvedConfig.connections?.[0]?.type == lib_mongo.MONGO_CONNECTION_TYPE
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
// Storage factory for reports
|
|
@@ -13,33 +13,33 @@ import {
|
|
|
13
13
|
CheckpointChanges,
|
|
14
14
|
GetCheckpointChangesOptions,
|
|
15
15
|
InternalOpId,
|
|
16
|
-
maxLsn,
|
|
17
16
|
mergeAsyncIterables,
|
|
18
17
|
PopulateChecksumCacheOptions,
|
|
19
18
|
PopulateChecksumCacheResults,
|
|
20
19
|
ReplicationCheckpoint,
|
|
20
|
+
ReplicationStreamStorageIds,
|
|
21
21
|
storage,
|
|
22
22
|
utils,
|
|
23
23
|
WatchWriteCheckpointOptions
|
|
24
24
|
} from '@powersync/service-core';
|
|
25
|
-
import {
|
|
25
|
+
import { HydratedSyncConfig, ParameterLookupRows, ScopedParameterLookup } from '@powersync/service-sync-rules';
|
|
26
26
|
import * as bson from 'bson';
|
|
27
27
|
import { LRUCache } from 'lru-cache';
|
|
28
28
|
import * as timers from 'timers/promises';
|
|
29
29
|
import { retryOnMongoMaxTimeMSExpired } from '../../utils/util.js';
|
|
30
30
|
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
31
|
-
import { MongoSyncBucketStorageContext } from './common/MongoSyncBucketStorageContext.js';
|
|
32
31
|
import type { VersionedPowerSyncMongo } from './db.js';
|
|
33
|
-
import {
|
|
32
|
+
import { StorageConfig } from './models.js';
|
|
34
33
|
import { MongoBucketBatchOptions } from './MongoBucketBatch.js';
|
|
35
34
|
import { MongoChecksumOptions, MongoChecksums } from './MongoChecksums.js';
|
|
36
35
|
import { MongoCompactOptions, MongoCompactor } from './MongoCompactor.js';
|
|
37
36
|
import { MongoParameterCompactor } from './MongoParameterCompactor.js';
|
|
38
|
-
import {
|
|
37
|
+
import { MongoParsedSyncConfigSet } from './MongoParsedSyncConfigSet.js';
|
|
38
|
+
import { MongoPersistedReplicationStream } from './MongoPersistedReplicationStream.js';
|
|
39
39
|
import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
|
|
40
40
|
|
|
41
41
|
export interface MongoSyncBucketStorageOptions {
|
|
42
|
-
checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig'
|
|
42
|
+
checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig'>;
|
|
43
43
|
storageConfig: StorageConfig;
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -64,32 +64,42 @@ export abstract class MongoSyncBucketStorage
|
|
|
64
64
|
implements storage.SyncRulesBucketStorage
|
|
65
65
|
{
|
|
66
66
|
readonly db: VersionedPowerSyncMongo;
|
|
67
|
+
|
|
67
68
|
[DO_NOT_LOG] = true;
|
|
68
69
|
|
|
69
70
|
readonly checksums: MongoChecksums;
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Canonical parsed sync config sets, keyed by defaultSchema.
|
|
74
|
+
*
|
|
75
|
+
* Entries are never evicted: each parse options value maps to exactly one parsed set for
|
|
76
|
+
* the lifetime of this storage instance, so parsed source objects and mappings always
|
|
77
|
+
* stay associated.
|
|
78
|
+
*/
|
|
79
|
+
private readonly parsedSyncConfigSets = new Map<string, MongoParsedSyncConfigSet>();
|
|
72
80
|
private writeCheckpointAPI: MongoWriteCheckpointAPI;
|
|
73
81
|
public readonly logger: Logger;
|
|
82
|
+
public readonly storageConfig: StorageConfig;
|
|
74
83
|
#storageInitialized = false;
|
|
75
84
|
|
|
76
85
|
constructor(
|
|
77
86
|
public readonly factory: MongoBucketStorage,
|
|
78
|
-
public readonly
|
|
79
|
-
|
|
80
|
-
public readonly
|
|
87
|
+
public readonly replicationStreamId: number,
|
|
88
|
+
public readonly replicationStream: MongoPersistedReplicationStream,
|
|
89
|
+
public readonly replicationStreamName: string,
|
|
81
90
|
writeCheckpointMode: storage.WriteCheckpointMode | undefined,
|
|
82
91
|
options: MongoSyncBucketStorageOptions
|
|
83
92
|
) {
|
|
84
93
|
super();
|
|
85
|
-
this.
|
|
94
|
+
this.storageConfig = options.storageConfig;
|
|
95
|
+
this.db = factory.db.versioned(this.storageConfig);
|
|
86
96
|
this.checksums = this.createMongoChecksums(options);
|
|
87
97
|
this.writeCheckpointAPI = new MongoWriteCheckpointAPI({
|
|
88
98
|
db: this.db,
|
|
89
99
|
mode: writeCheckpointMode ?? storage.WriteCheckpointMode.MANAGED,
|
|
90
|
-
sync_rules_id:
|
|
100
|
+
sync_rules_id: replicationStreamId
|
|
91
101
|
});
|
|
92
|
-
this.logger =
|
|
102
|
+
this.logger = replicationStream.logger;
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
/**
|
|
@@ -109,16 +119,11 @@ export abstract class MongoSyncBucketStorage
|
|
|
109
119
|
return this.writeCheckpointAPI.writeCheckpointMode;
|
|
110
120
|
}
|
|
111
121
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
db: this.db,
|
|
119
|
-
group_id: this.group_id,
|
|
120
|
-
mapping: this.mapping
|
|
121
|
-
};
|
|
122
|
+
/**
|
|
123
|
+
* Persisted storage ids of all sync configs in this replication stream. Parse-free.
|
|
124
|
+
*/
|
|
125
|
+
get storageIds(): ReplicationStreamStorageIds {
|
|
126
|
+
return this.replicationStream.storageIds;
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
setWriteCheckpointMode(mode: storage.WriteCheckpointMode): void {
|
|
@@ -132,33 +137,35 @@ export abstract class MongoSyncBucketStorage
|
|
|
132
137
|
lastWriteCheckpoint(filters: storage.SyncStorageLastWriteCheckpointFilters): Promise<bigint | null> {
|
|
133
138
|
return this.writeCheckpointAPI.lastWriteCheckpoint({
|
|
134
139
|
...filters,
|
|
135
|
-
sync_rules_id: this.
|
|
140
|
+
sync_rules_id: this.replicationStreamId
|
|
136
141
|
});
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
144
|
+
getParsedSyncConfigSet(options: storage.ParseSyncConfigOptions): MongoParsedSyncConfigSet {
|
|
145
|
+
let parsed = this.parsedSyncConfigSets.get(options.defaultSchema);
|
|
146
|
+
if (parsed == null) {
|
|
147
|
+
parsed = this.replicationStream.parsed(options);
|
|
148
|
+
this.parsedSyncConfigSets.set(options.defaultSchema, parsed);
|
|
143
149
|
}
|
|
150
|
+
return parsed;
|
|
151
|
+
}
|
|
144
152
|
|
|
145
|
-
|
|
153
|
+
getParsedSyncRules(options: storage.ParseSyncConfigOptions): HydratedSyncConfig {
|
|
154
|
+
return this.getParsedSyncConfigSet(options).hydratedSyncConfig;
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
async getCheckpoint(): Promise<storage.ReplicationCheckpoint> {
|
|
149
158
|
return (await this.getCheckpointInternal()) ?? new EmptyReplicationCheckpoint();
|
|
150
159
|
}
|
|
151
160
|
|
|
161
|
+
protected abstract fetchCheckpointState(
|
|
162
|
+
session: mongo.ClientSession
|
|
163
|
+
): Promise<{ checkpoint: bigint; lsn: string | null } | null>;
|
|
164
|
+
|
|
152
165
|
async getCheckpointInternal(): Promise<storage.ReplicationCheckpoint | null> {
|
|
153
166
|
return await this.db.client.withSession({ snapshot: true }, async (session) => {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
{
|
|
157
|
-
session,
|
|
158
|
-
projection: { _id: 1, state: 1, last_checkpoint: 1, last_checkpoint_lsn: 1, snapshot_done: 1 }
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
if (!doc?.snapshot_done || !['ACTIVE', 'ERRORED'].includes(doc.state)) {
|
|
167
|
+
const state = await this.fetchCheckpointState(session);
|
|
168
|
+
if (state == null) {
|
|
162
169
|
return null;
|
|
163
170
|
}
|
|
164
171
|
|
|
@@ -166,12 +173,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
166
173
|
if (snapshotTime == null) {
|
|
167
174
|
throw new ServiceAssertionError('Missing snapshotTime in getCheckpoint()');
|
|
168
175
|
}
|
|
169
|
-
return new MongoReplicationCheckpoint(
|
|
170
|
-
this,
|
|
171
|
-
doc.last_checkpoint ?? 0n,
|
|
172
|
-
doc.last_checkpoint_lsn ?? null,
|
|
173
|
-
snapshotTime
|
|
174
|
-
);
|
|
176
|
+
return new MongoReplicationCheckpoint(this, state.checkpoint, state.lsn, snapshotTime);
|
|
175
177
|
});
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -182,40 +184,40 @@ export abstract class MongoSyncBucketStorage
|
|
|
182
184
|
return;
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
await this.db.initializeStreamStorage(this.
|
|
187
|
+
await this.db.initializeStreamStorage(this.replicationStreamId);
|
|
186
188
|
await this.initializeVersionStorage();
|
|
187
189
|
this.#storageInitialized = true;
|
|
188
190
|
}
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
{
|
|
197
|
-
_id: this.group_id
|
|
198
|
-
},
|
|
199
|
-
{ projection: { last_checkpoint_lsn: 1, no_checkpoint_before: 1, keepalive_op: 1, snapshot_lsn: 1 } }
|
|
200
|
-
);
|
|
201
|
-
const checkpoint_lsn = doc?.last_checkpoint_lsn ?? null;
|
|
192
|
+
/**
|
|
193
|
+
* Create the version-specific writer. Implementations fetch their own resume state
|
|
194
|
+
* (e.g. resume LSN, v1 keepalive op) and construct the batch from
|
|
195
|
+
* {@link writerBatchOptions} plus the version-specific fields.
|
|
196
|
+
*/
|
|
197
|
+
protected abstract createWriterImpl(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch>;
|
|
202
198
|
|
|
203
|
-
|
|
199
|
+
/**
|
|
200
|
+
* The version-independent part of the batch options.
|
|
201
|
+
*/
|
|
202
|
+
protected writerBatchOptions(options: storage.CreateWriterOptions): Omit<MongoBucketBatchOptions, 'resumeFromLsn'> {
|
|
203
|
+
return {
|
|
204
204
|
logger: options.logger ?? this.logger,
|
|
205
205
|
db: this.db,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
slotName: this.slot_name,
|
|
210
|
-
lastCheckpointLsn: checkpoint_lsn,
|
|
211
|
-
resumeFromLsn: maxLsn(checkpoint_lsn, doc?.snapshot_lsn),
|
|
212
|
-
keepaliveOp: doc?.keepalive_op ? BigInt(doc.keepalive_op) : null,
|
|
206
|
+
parsedSyncConfig: this.getParsedSyncConfigSet(options),
|
|
207
|
+
replicationStreamId: this.replicationStreamId,
|
|
208
|
+
replicationStreamName: this.replicationStreamName,
|
|
213
209
|
storeCurrentData: options.storeCurrentData,
|
|
214
210
|
skipExistingRows: options.skipExistingRows ?? false,
|
|
215
211
|
markRecordUnavailable: options.markRecordUnavailable,
|
|
212
|
+
hooks: options.hooks,
|
|
216
213
|
tracer: options.tracer
|
|
217
214
|
};
|
|
218
|
-
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async createWriter(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch> {
|
|
218
|
+
await this.initializeStorage();
|
|
219
|
+
|
|
220
|
+
const writer = await this.createWriterImpl(options);
|
|
219
221
|
this.iterateListeners((cb) => cb.batchStarted?.(writer));
|
|
220
222
|
return writer;
|
|
221
223
|
}
|
|
@@ -230,134 +232,6 @@ export abstract class MongoSyncBucketStorage
|
|
|
230
232
|
return writer.last_flushed_op != null ? { flushed_op: writer.last_flushed_op } : null;
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
protected abstract sourceTableBaseId(): Partial<CommonSourceTableDocument>;
|
|
234
|
-
|
|
235
|
-
protected abstract augmentCreatedSourceTableDocument(
|
|
236
|
-
createDoc: CommonSourceTableDocument,
|
|
237
|
-
options: storage.ResolveTableOptions,
|
|
238
|
-
candidateSourceTable: storage.SourceTable
|
|
239
|
-
): void;
|
|
240
|
-
|
|
241
|
-
protected abstract initializeResolvedSourceRecords(sourceTableId: bson.ObjectId): Promise<void>;
|
|
242
|
-
|
|
243
|
-
async resolveTable(options: storage.ResolveTableOptions): Promise<storage.ResolveTableResult> {
|
|
244
|
-
const { group_id, connection_id, connection_tag, entity_descriptor } = options;
|
|
245
|
-
|
|
246
|
-
const { schema, name, objectId, replicaIdColumns } = entity_descriptor;
|
|
247
|
-
|
|
248
|
-
const normalizedReplicaIdColumns = replicaIdColumns.map((column) => ({
|
|
249
|
-
name: column.name,
|
|
250
|
-
type: column.type,
|
|
251
|
-
type_oid: column.typeId
|
|
252
|
-
}));
|
|
253
|
-
let result: storage.ResolveTableResult | null = null;
|
|
254
|
-
let initializeSourceRecordsFor: bson.ObjectId | null = null;
|
|
255
|
-
|
|
256
|
-
const baseId = this.sourceTableBaseId();
|
|
257
|
-
await this.db.client.withSession(async (session) => {
|
|
258
|
-
const col = this.db.commonSourceTables(group_id);
|
|
259
|
-
let filter: Partial<CommonSourceTableDocument> = {
|
|
260
|
-
...baseId,
|
|
261
|
-
connection_id: connection_id,
|
|
262
|
-
schema_name: schema,
|
|
263
|
-
table_name: name,
|
|
264
|
-
replica_id_columns2: normalizedReplicaIdColumns
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
if (objectId != null) {
|
|
268
|
-
filter.relation_id = objectId;
|
|
269
|
-
}
|
|
270
|
-
let doc = await col.findOne(filter, { session });
|
|
271
|
-
if (doc == null) {
|
|
272
|
-
const candidateSourceTable = new storage.SourceTable({
|
|
273
|
-
id: new bson.ObjectId(),
|
|
274
|
-
connectionTag: connection_tag,
|
|
275
|
-
objectId: objectId,
|
|
276
|
-
schema: schema,
|
|
277
|
-
name: name,
|
|
278
|
-
replicaIdColumns: replicaIdColumns,
|
|
279
|
-
snapshotComplete: false
|
|
280
|
-
});
|
|
281
|
-
const createDoc: CommonSourceTableDocument = {
|
|
282
|
-
_id: candidateSourceTable.id as bson.ObjectId,
|
|
283
|
-
...(baseId as any),
|
|
284
|
-
connection_id: connection_id,
|
|
285
|
-
relation_id: objectId,
|
|
286
|
-
schema_name: schema,
|
|
287
|
-
table_name: name,
|
|
288
|
-
replica_id_columns: null,
|
|
289
|
-
replica_id_columns2: normalizedReplicaIdColumns,
|
|
290
|
-
snapshot_done: false,
|
|
291
|
-
snapshot_status: undefined
|
|
292
|
-
};
|
|
293
|
-
this.augmentCreatedSourceTableDocument(createDoc, options, candidateSourceTable);
|
|
294
|
-
doc = createDoc;
|
|
295
|
-
|
|
296
|
-
await col.insertOne(doc, { session });
|
|
297
|
-
initializeSourceRecordsFor = doc._id;
|
|
298
|
-
}
|
|
299
|
-
const sourceTable = new storage.SourceTable({
|
|
300
|
-
id: doc._id,
|
|
301
|
-
connectionTag: connection_tag,
|
|
302
|
-
objectId: objectId,
|
|
303
|
-
schema: schema,
|
|
304
|
-
name: name,
|
|
305
|
-
replicaIdColumns: replicaIdColumns,
|
|
306
|
-
snapshotComplete: doc.snapshot_done ?? true
|
|
307
|
-
});
|
|
308
|
-
sourceTable.syncEvent = options.sync_rules.tableTriggersEvent(sourceTable);
|
|
309
|
-
sourceTable.syncData = options.sync_rules.tableSyncsData(sourceTable);
|
|
310
|
-
sourceTable.syncParameters = options.sync_rules.tableSyncsParameters(sourceTable);
|
|
311
|
-
sourceTable.snapshotStatus =
|
|
312
|
-
doc.snapshot_status == null
|
|
313
|
-
? undefined
|
|
314
|
-
: {
|
|
315
|
-
lastKey: doc.snapshot_status.last_key?.buffer ?? null,
|
|
316
|
-
totalEstimatedCount: doc.snapshot_status.total_estimated_count,
|
|
317
|
-
replicatedCount: doc.snapshot_status.replicated_count
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
let dropTables: storage.SourceTable[] = [];
|
|
321
|
-
let truncateFilter = [{ schema_name: schema, table_name: name }] as any[];
|
|
322
|
-
if (objectId != null) {
|
|
323
|
-
truncateFilter.push({ relation_id: objectId });
|
|
324
|
-
}
|
|
325
|
-
const truncate = await col
|
|
326
|
-
.find(
|
|
327
|
-
{
|
|
328
|
-
...baseId,
|
|
329
|
-
connection_id: connection_id,
|
|
330
|
-
_id: { $ne: doc._id },
|
|
331
|
-
$or: truncateFilter
|
|
332
|
-
},
|
|
333
|
-
{ session }
|
|
334
|
-
)
|
|
335
|
-
.toArray();
|
|
336
|
-
dropTables = truncate.map(
|
|
337
|
-
(doc) =>
|
|
338
|
-
new storage.SourceTable({
|
|
339
|
-
id: doc._id,
|
|
340
|
-
connectionTag: connection_tag,
|
|
341
|
-
objectId: doc.relation_id,
|
|
342
|
-
schema: doc.schema_name,
|
|
343
|
-
name: doc.table_name,
|
|
344
|
-
replicaIdColumns:
|
|
345
|
-
doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid, type: c.type })) ?? [],
|
|
346
|
-
snapshotComplete: doc.snapshot_done ?? true
|
|
347
|
-
})
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
result = {
|
|
351
|
-
table: sourceTable,
|
|
352
|
-
dropTables: dropTables
|
|
353
|
-
};
|
|
354
|
-
});
|
|
355
|
-
if (initializeSourceRecordsFor != null) {
|
|
356
|
-
await this.initializeResolvedSourceRecords(initializeSourceRecordsFor);
|
|
357
|
-
}
|
|
358
|
-
return result!;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
235
|
protected abstract getParameterSetsImpl(
|
|
362
236
|
checkpoint: MongoReplicationCheckpoint,
|
|
363
237
|
lookups: ScopedParameterLookup[],
|
|
@@ -397,49 +271,20 @@ export abstract class MongoSyncBucketStorage
|
|
|
397
271
|
this.checksums.clearCache();
|
|
398
272
|
}
|
|
399
273
|
|
|
274
|
+
protected abstract terminateSyncRuleState(): Promise<void>;
|
|
275
|
+
|
|
400
276
|
async terminate(options?: storage.TerminateOptions) {
|
|
401
277
|
if (!options || options?.clearStorage) {
|
|
402
278
|
await this.clear(options);
|
|
403
279
|
}
|
|
404
|
-
await this.
|
|
405
|
-
{
|
|
406
|
-
_id: this.group_id
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
$set: {
|
|
410
|
-
state: storage.SyncRuleState.TERMINATED,
|
|
411
|
-
persisted_lsn: null,
|
|
412
|
-
snapshot_done: false
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
);
|
|
280
|
+
await this.terminateSyncRuleState();
|
|
416
281
|
await this.db.notifyCheckpoint();
|
|
417
282
|
}
|
|
418
283
|
|
|
419
|
-
|
|
420
|
-
const doc = await this.db.sync_rules.findOne(
|
|
421
|
-
{
|
|
422
|
-
_id: this.group_id
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
projection: {
|
|
426
|
-
snapshot_done: 1,
|
|
427
|
-
last_checkpoint_lsn: 1,
|
|
428
|
-
state: 1,
|
|
429
|
-
snapshot_lsn: 1
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
);
|
|
433
|
-
if (doc == null) {
|
|
434
|
-
throw new ServiceAssertionError('Cannot find replication stream status');
|
|
435
|
-
}
|
|
284
|
+
protected abstract getStatusImpl(): Promise<storage.ReplicationStreamStatus>;
|
|
436
285
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
snapshot_lsn: doc.snapshot_lsn ?? null,
|
|
440
|
-
active: doc.state == 'ACTIVE',
|
|
441
|
-
checkpoint_lsn: doc.last_checkpoint_lsn
|
|
442
|
-
};
|
|
286
|
+
async getStatus(): Promise<storage.ReplicationStreamStatus> {
|
|
287
|
+
return this.getStatusImpl();
|
|
443
288
|
}
|
|
444
289
|
|
|
445
290
|
protected abstract clearBucketData(signal?: AbortSignal): Promise<void>;
|
|
@@ -451,6 +296,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
451
296
|
protected abstract clearBucketState(signal?: AbortSignal): Promise<void>;
|
|
452
297
|
|
|
453
298
|
protected abstract clearSourceTables(signal?: AbortSignal): Promise<void>;
|
|
299
|
+
protected abstract clearSyncRuleState(): Promise<void>;
|
|
454
300
|
|
|
455
301
|
async clear(options?: storage.ClearStorageOptions): Promise<void> {
|
|
456
302
|
const signal = options?.signal;
|
|
@@ -459,24 +305,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
459
305
|
throw new ReplicationAbortedError('Aborted clearing data', signal.reason);
|
|
460
306
|
}
|
|
461
307
|
|
|
462
|
-
await this.
|
|
463
|
-
{
|
|
464
|
-
_id: this.group_id
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
$set: {
|
|
468
|
-
snapshot_done: false,
|
|
469
|
-
persisted_lsn: null,
|
|
470
|
-
last_checkpoint_lsn: null,
|
|
471
|
-
last_checkpoint: null,
|
|
472
|
-
no_checkpoint_before: null
|
|
473
|
-
},
|
|
474
|
-
$unset: {
|
|
475
|
-
snapshot_lsn: 1
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
479
|
-
);
|
|
308
|
+
await this.clearSyncRuleState();
|
|
480
309
|
|
|
481
310
|
await this.clearBucketData(signal);
|
|
482
311
|
await this.clearParameterIndexes(signal);
|
|
@@ -508,7 +337,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
508
337
|
const message = String(e.message ?? 'Replication failure');
|
|
509
338
|
await this.db.sync_rules.updateOne(
|
|
510
339
|
{
|
|
511
|
-
_id: this.
|
|
340
|
+
_id: this.replicationStreamId
|
|
512
341
|
},
|
|
513
342
|
{
|
|
514
343
|
$set: {
|
|
@@ -532,11 +361,21 @@ export abstract class MongoSyncBucketStorage
|
|
|
532
361
|
}
|
|
533
362
|
}
|
|
534
363
|
|
|
364
|
+
/**
|
|
365
|
+
* The highest op id persisted for this stream, whether or not covered by a checkpoint.
|
|
366
|
+
*
|
|
367
|
+
* Used as the default `maxOpId` for {@link populatePersistentChecksumCache}, which runs after
|
|
368
|
+
* initial replication but before the first checkpoint exists.
|
|
369
|
+
*/
|
|
370
|
+
protected abstract fetchPersistedOpHead(): Promise<InternalOpId | null>;
|
|
371
|
+
|
|
535
372
|
async populatePersistentChecksumCache(options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults> {
|
|
536
373
|
this.logger.info(`Populating persistent checksum cache...`);
|
|
537
374
|
const start = Date.now();
|
|
375
|
+
const maxOpId = options.maxOpId ?? (await this.fetchPersistedOpHead()) ?? undefined;
|
|
538
376
|
const compactor = this.createMongoCompactor({
|
|
539
377
|
...options,
|
|
378
|
+
maxOpId,
|
|
540
379
|
memoryLimitMB: 0,
|
|
541
380
|
logger: this.logger
|
|
542
381
|
});
|
|
@@ -588,7 +427,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
588
427
|
for await (const nextCheckpoint of iter) {
|
|
589
428
|
if (nextCheckpoint.lsn != null && !queriedInitialWriteCheckpoint) {
|
|
590
429
|
writeCheckpoint = await this.writeCheckpointAPI.lastWriteCheckpoint({
|
|
591
|
-
sync_rules_id: this.
|
|
430
|
+
sync_rules_id: this.replicationStreamId,
|
|
592
431
|
user_id: options.user_id,
|
|
593
432
|
heads: {
|
|
594
433
|
'1': nextCheckpoint.lsn
|
|
@@ -621,7 +460,7 @@ export abstract class MongoSyncBucketStorage
|
|
|
621
460
|
let updatedWriteCheckpoint = updates.updatedWriteCheckpoints.get(options.user_id) ?? null;
|
|
622
461
|
if (updates.invalidateWriteCheckpoints) {
|
|
623
462
|
updatedWriteCheckpoint = await this.writeCheckpointAPI.lastWriteCheckpoint({
|
|
624
|
-
sync_rules_id: this.
|
|
463
|
+
sync_rules_id: this.replicationStreamId,
|
|
625
464
|
user_id: options.user_id,
|
|
626
465
|
heads: {
|
|
627
466
|
'1': nextCheckpoint.lsn!
|
|
@@ -792,7 +631,7 @@ class EmptyReplicationCheckpoint implements ReplicationCheckpoint {
|
|
|
792
631
|
readonly checkpoint: InternalOpId = 0n;
|
|
793
632
|
readonly lsn: string | null = null;
|
|
794
633
|
|
|
795
|
-
async getParameterSets(_lookups: ScopedParameterLookup[]): Promise<ParameterLookupRows[]> {
|
|
634
|
+
async getParameterSets(_lookups: ScopedParameterLookup[], _limit: number): Promise<ParameterLookupRows[]> {
|
|
796
635
|
return [];
|
|
797
636
|
}
|
|
798
637
|
}
|