@powersync/service-module-mongodb-storage 0.17.0 → 0.18.1
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 +27 -0
- package/dist/storage/MongoBucketStorage.d.ts +34 -16
- package/dist/storage/MongoBucketStorage.js +219 -70
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +42 -23
- package/dist/storage/implementation/MongoBucketBatch.js +61 -44
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoChecksums.d.ts +10 -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 +2 -0
- 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 +123 -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 +41 -23
- package/dist/storage/implementation/MongoSyncBucketStorage.js +51 -43
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.js +3 -3
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +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 +1 -2
- package/dist/storage/implementation/common/PersistedBatch.js +5 -0
- package/dist/storage/implementation/common/PersistedBatch.js.map +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.js +9 -14
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +4 -13
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +9 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +42 -26
- 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/MongoSyncBucketStorageV1.d.ts +15 -17
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +47 -45
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.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 +10 -1
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +8 -4
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +286 -259
- 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 +19 -2
- 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 +40 -28
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +232 -116
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +1 -1
- 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 +13 -12
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +29 -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 +64 -21
- 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/storage/MongoBucketStorage.ts +334 -87
- package/src/storage/implementation/MongoBucketBatch.ts +86 -58
- package/src/storage/implementation/MongoChecksums.ts +6 -138
- package/src/storage/implementation/MongoCompactor.ts +19 -9
- 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 +154 -0
- package/src/storage/implementation/MongoStorageProvider.ts +5 -2
- package/src/storage/implementation/MongoSyncBucketStorage.ts +70 -56
- package/src/storage/implementation/MongoSyncRulesLock.ts +7 -4
- package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +7 -0
- package/src/storage/implementation/common/PersistedBatch.ts +7 -3
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
- package/src/storage/implementation/db.ts +11 -19
- package/src/storage/implementation/models.ts +4 -14
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +55 -34
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +137 -4
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +70 -64
- package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
- package/src/storage/implementation/v1/models.ts +11 -1
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +358 -314
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +262 -26
- package/src/storage/implementation/v3/MongoCompactorV3.ts +646 -29
- package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +480 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +315 -157
- package/src/storage/implementation/v3/PersistedBatchV3.ts +62 -36
- 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 +41 -36
- 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 +93 -55
- 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 +1077 -68
- package/test/src/storeCurrentData.test.ts +17 -7
- 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 -15
- 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 -67
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -29
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -76
- 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 -4
- 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 -75
- package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -82
- package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -101
- 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 -11
|
@@ -18,12 +18,13 @@ type ParameterCompactionReadDocument = {
|
|
|
18
18
|
*
|
|
19
19
|
* For background, see the `/docs/parameters-lookups.md` file.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
21
|
+
export class MongoParameterCompactor {
|
|
22
22
|
constructor(
|
|
23
23
|
protected readonly db: VersionedPowerSyncMongo,
|
|
24
24
|
protected readonly group_id: number,
|
|
25
25
|
protected readonly checkpoint: InternalOpId,
|
|
26
|
-
protected readonly options: CompactOptions
|
|
26
|
+
protected readonly options: CompactOptions,
|
|
27
|
+
protected readonly getCollectionsCb?: () => Promise<mongo.Collection<mongo.Document>[]>
|
|
27
28
|
) {}
|
|
28
29
|
|
|
29
30
|
async compact() {
|
|
@@ -33,11 +34,27 @@ export abstract class MongoParameterCompactor {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
protected
|
|
37
|
+
protected async getCollections(): Promise<mongo.Collection<mongo.Document>[]> {
|
|
38
|
+
if (this.getCollectionsCb == null) {
|
|
39
|
+
throw new Error('getCollections callback not provided');
|
|
40
|
+
}
|
|
41
|
+
const collections = await this.getCollectionsCb();
|
|
42
|
+
// Cast from the version-specific collection type to the generic Document type
|
|
43
|
+
// used by the parameter compactor base class.
|
|
44
|
+
return collections.map((collection) => collection as unknown as mongo.Collection<mongo.Document>);
|
|
45
|
+
}
|
|
37
46
|
|
|
38
|
-
protected
|
|
47
|
+
protected collectionFilter(): mongo.Document {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
39
50
|
|
|
40
|
-
protected
|
|
51
|
+
protected deleteFilter(doc: mongo.Document): mongo.Document {
|
|
52
|
+
return {
|
|
53
|
+
lookup: doc.lookup,
|
|
54
|
+
_id: { $lte: doc._id },
|
|
55
|
+
key: doc.key
|
|
56
|
+
};
|
|
57
|
+
}
|
|
41
58
|
|
|
42
59
|
protected async compactCollection(collection: mongo.Collection<mongo.Document>) {
|
|
43
60
|
// This is the currently-active checkpoint.
|
|
@@ -68,6 +85,7 @@ export abstract class MongoParameterCompactor {
|
|
|
68
85
|
|
|
69
86
|
const flush = async (force: boolean) => {
|
|
70
87
|
if (removeIds.length >= 1000 || (force && removeIds.length > 0)) {
|
|
88
|
+
// MongoDB Filter<T> doesn't fully match our dynamic delete filter shape here.
|
|
71
89
|
const results = await collection.deleteMany({ _id: { $in: removeIds } } as any);
|
|
72
90
|
logger.info(`Removed ${results.deletedCount} (${removeIds.length}) superseded parameter entries`);
|
|
73
91
|
removeIds = [];
|
|
@@ -81,6 +99,7 @@ export abstract class MongoParameterCompactor {
|
|
|
81
99
|
};
|
|
82
100
|
|
|
83
101
|
while (await cursor.hasNext()) {
|
|
102
|
+
// readBufferedDocuments returns a generic type; we know the shape from our projection.
|
|
84
103
|
const batch = cursor.readBufferedDocuments() as unknown as ParameterCompactionReadDocument[];
|
|
85
104
|
checkedEntries += batch.length;
|
|
86
105
|
const now = Date.now();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as sqlite from 'node:sqlite';
|
|
2
|
+
|
|
3
|
+
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
4
|
+
import {
|
|
5
|
+
BucketDefinitionMapping,
|
|
6
|
+
MultiSyncConfigBucketDefinitionMapping,
|
|
7
|
+
SingleSyncConfigBucketDefinitionMapping,
|
|
8
|
+
storage,
|
|
9
|
+
SyncConfigWithMapping,
|
|
10
|
+
SyncConfigWithRequiredMapping
|
|
11
|
+
} from '@powersync/service-core';
|
|
12
|
+
import {
|
|
13
|
+
CompatibilityOption,
|
|
14
|
+
DEFAULT_HYDRATION_STATE,
|
|
15
|
+
HydratedSyncConfig,
|
|
16
|
+
HydrationState,
|
|
17
|
+
nodeSqlite,
|
|
18
|
+
SyncConfigWithErrors,
|
|
19
|
+
versionedHydrationState
|
|
20
|
+
} from '@powersync/service-sync-rules';
|
|
21
|
+
import { StorageConfig } from './models.js';
|
|
22
|
+
import { MongoHydrationState } from './MongoHydrationState.js';
|
|
23
|
+
|
|
24
|
+
export class MongoParsedSyncConfigSet implements storage.ParsedSyncConfigSet {
|
|
25
|
+
public readonly hydrationState: HydrationState;
|
|
26
|
+
public readonly syncConfigs: SyncConfigWithErrors[];
|
|
27
|
+
public readonly replicationStreamName: string;
|
|
28
|
+
public readonly mapping: BucketDefinitionMapping;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
public readonly replicationStreamId: number,
|
|
32
|
+
storageConfig: StorageConfig,
|
|
33
|
+
slotName: string,
|
|
34
|
+
syncConfigs: SyncConfigWithMapping[]
|
|
35
|
+
) {
|
|
36
|
+
this.replicationStreamName = slotName;
|
|
37
|
+
this.syncConfigs = syncConfigs.map((config) => config.syncConfig);
|
|
38
|
+
if (this.syncConfigs.length == 0) {
|
|
39
|
+
throw new ServiceAssertionError(`At least one sync config is required`);
|
|
40
|
+
}
|
|
41
|
+
const [firstConfig] = this.syncConfigs;
|
|
42
|
+
const compatibility = firstConfig.config.compatibility;
|
|
43
|
+
for (const config of this.syncConfigs) {
|
|
44
|
+
if (config.config.compatibility.equals(compatibility)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
throw new ServiceAssertionError(
|
|
48
|
+
`All sync configs in a replication stream must use the same compatibility options`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (storageConfig.incrementalReprocessing) {
|
|
53
|
+
if (syncConfigs.some((c) => c.mapping == null)) {
|
|
54
|
+
throw new ServiceAssertionError(`mapping is required for v3 storage`);
|
|
55
|
+
}
|
|
56
|
+
const mappedConfigs = syncConfigs as SyncConfigWithRequiredMapping[];
|
|
57
|
+
this.hydrationState = new MongoHydrationState(mappedConfigs, this.replicationStreamId);
|
|
58
|
+
this.mapping = new MultiSyncConfigBucketDefinitionMapping(mappedConfigs);
|
|
59
|
+
} else if (!compatibility.isEnabled(CompatibilityOption.versionedBucketIds) && !storageConfig.versionedBuckets) {
|
|
60
|
+
const [syncConfig] = syncConfigs;
|
|
61
|
+
if (syncConfigs.length != 1 || syncConfig == null) {
|
|
62
|
+
throw new ServiceAssertionError(`Non-incremental storage requires exactly one sync config`);
|
|
63
|
+
}
|
|
64
|
+
this.hydrationState = DEFAULT_HYDRATION_STATE;
|
|
65
|
+
this.mapping = syncConfig.mapping ?? new SingleSyncConfigBucketDefinitionMapping();
|
|
66
|
+
} else {
|
|
67
|
+
const [syncConfig] = syncConfigs;
|
|
68
|
+
if (syncConfigs.length != 1 || syncConfig == null) {
|
|
69
|
+
throw new ServiceAssertionError(`Non-incremental storage requires exactly one sync config`);
|
|
70
|
+
}
|
|
71
|
+
this.hydrationState = versionedHydrationState(this.replicationStreamId);
|
|
72
|
+
this.mapping = syncConfig.mapping ?? new SingleSyncConfigBucketDefinitionMapping();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#hydratedSyncConfig: HydratedSyncConfig | undefined;
|
|
77
|
+
|
|
78
|
+
get hydratedSyncConfig(): HydratedSyncConfig {
|
|
79
|
+
this.#hydratedSyncConfig ??= new HydratedSyncConfig({
|
|
80
|
+
definitions: this.syncConfigs.map((config) => config.config),
|
|
81
|
+
createParams: {
|
|
82
|
+
hydrationState: this.hydrationState,
|
|
83
|
+
sqlite: nodeSqlite(sqlite)
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return this.#hydratedSyncConfig;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
3
|
+
import { ReplicationStreamStorageIds, SingleSyncConfigBucketDefinitionMapping, storage } from '@powersync/service-core';
|
|
4
|
+
import * as bson from 'bson';
|
|
5
|
+
import { ReplicationStreamDocumentV3, SyncConfigDefinition } from '../storage-index.js';
|
|
6
|
+
import { PowerSyncMongo } from './db.js';
|
|
7
|
+
import { getMongoStorageConfig } from './models.js';
|
|
8
|
+
import { MongoParsedSyncConfigSet } from './MongoParsedSyncConfigSet.js';
|
|
9
|
+
import {
|
|
10
|
+
MongoPersistedSyncConfigContentBase,
|
|
11
|
+
MongoPersistedSyncConfigContentV1,
|
|
12
|
+
MongoPersistedSyncConfigContentV3
|
|
13
|
+
} from './MongoPersistedSyncConfigContent.js';
|
|
14
|
+
import { MongoSyncRulesLock } from './MongoSyncRulesLock.js';
|
|
15
|
+
import { SyncRuleDocumentV1 } from './v1/models.js';
|
|
16
|
+
|
|
17
|
+
export class MongoPersistedReplicationStream extends storage.PersistedReplicationStream {
|
|
18
|
+
public current_lock: MongoSyncRulesLock | null = null;
|
|
19
|
+
public readonly syncConfigContent: readonly MongoPersistedSyncConfigContentBase[];
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private readonly db: PowerSyncMongo,
|
|
23
|
+
private readonly doc: SyncRuleDocumentV1 | ReplicationStreamDocumentV3,
|
|
24
|
+
private readonly configs: SyncConfigDefinition[] = []
|
|
25
|
+
) {
|
|
26
|
+
const storageVersion = doc.storage_version ?? storage.LEGACY_STORAGE_VERSION;
|
|
27
|
+
const replicationJobId =
|
|
28
|
+
configs.length == 0
|
|
29
|
+
? String(doc._id)
|
|
30
|
+
: `${doc._id}:${configs
|
|
31
|
+
.map((config) => config._id.toHexString())
|
|
32
|
+
.sort()
|
|
33
|
+
.join(',')}`;
|
|
34
|
+
|
|
35
|
+
super({
|
|
36
|
+
replicationStreamId: doc._id,
|
|
37
|
+
replicationStreamName: doc.slot_name ?? `powersync_${doc._id}`,
|
|
38
|
+
state: doc.state,
|
|
39
|
+
storageVersion,
|
|
40
|
+
replicationJobId
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.syncConfigContent = this.createSyncConfigContent();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getStorageConfig() {
|
|
47
|
+
return getMongoStorageConfig(this.storageVersion);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private createSyncConfigContent(): MongoPersistedSyncConfigContentBase[] {
|
|
51
|
+
if (this.getStorageConfig().incrementalReprocessing) {
|
|
52
|
+
if (this.configs.length == 0) {
|
|
53
|
+
throw new ServiceAssertionError(`Cannot create v3 storage without sync config definitions`);
|
|
54
|
+
}
|
|
55
|
+
return this.configs.map(
|
|
56
|
+
(config) => new MongoPersistedSyncConfigContentV3(this.db, this.doc as ReplicationStreamDocumentV3, config)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return [new MongoPersistedSyncConfigContentV1(this.db, this.doc as SyncRuleDocumentV1)];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get syncConfigIds(): bson.ObjectId[] {
|
|
64
|
+
return this.configs.map((config) => config._id);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#storageIds: ReplicationStreamStorageIds | undefined;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Persisted storage ids for all sync configs in this replication stream.
|
|
71
|
+
*
|
|
72
|
+
* Derived from the persisted rule_mapping documents - requires no parsed sync configs.
|
|
73
|
+
*/
|
|
74
|
+
get storageIds(): ReplicationStreamStorageIds {
|
|
75
|
+
this.#storageIds ??= new ReplicationStreamStorageIds(this.syncConfigContent.map((content) => content.mapping));
|
|
76
|
+
return this.#storageIds;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Parse the sync configs for this replication stream.
|
|
81
|
+
*
|
|
82
|
+
* This creates a fresh parse on every call. Do not call this from operational paths -
|
|
83
|
+
* use {@link MongoSyncBucketStorage.getParsedSyncConfigSet} instead, which returns a
|
|
84
|
+
* canonical instance so that parsed source objects and mappings stay associated.
|
|
85
|
+
*/
|
|
86
|
+
parsed(options: storage.ParseSyncConfigOptions): MongoParsedSyncConfigSet {
|
|
87
|
+
const storageConfig = this.getStorageConfig();
|
|
88
|
+
if (!storageConfig.incrementalReprocessing) {
|
|
89
|
+
return this.syncConfigContent[0].parsed(options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const syncConfigs = this.configs.map((config) => {
|
|
93
|
+
return {
|
|
94
|
+
syncConfigId: config._id.toHexString(),
|
|
95
|
+
syncConfig: storage.parsePersistedSyncConfigContent({
|
|
96
|
+
content: config.content,
|
|
97
|
+
compiledPlan: config.serialized_plan ?? null,
|
|
98
|
+
storageVersion: this.storageVersion,
|
|
99
|
+
parseOptions: options
|
|
100
|
+
}),
|
|
101
|
+
mapping: SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(config.rule_mapping)
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return new MongoParsedSyncConfigSet(
|
|
106
|
+
this.replicationStreamId,
|
|
107
|
+
storageConfig,
|
|
108
|
+
this.replicationStreamName,
|
|
109
|
+
syncConfigs
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async lock(session?: mongo.ClientSession) {
|
|
114
|
+
const lock = await MongoSyncRulesLock.createLock(this.db.versioned(this.getStorageConfig()), this, session);
|
|
115
|
+
this.current_lock = lock;
|
|
116
|
+
return lock;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
syncConfigState: doc.state
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async getSyncConfigStatus(): Promise<storage.PersistedSyncConfigStatus | null> {
|
|
78
|
+
const doc = await this.db.sync_rules.findOne<SyncRuleDocumentV1>({ _id: this.replicationStreamId });
|
|
79
|
+
if (doc == null) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return syncConfigStatusFromV1(doc);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export class MongoPersistedSyncConfigContentV3 extends MongoPersistedSyncConfigContentBase {
|
|
87
|
+
declare public readonly syncConfigObjectId: bson.ObjectId;
|
|
88
|
+
|
|
89
|
+
constructor(db: PowerSyncMongo, doc: ReplicationStreamDocumentV3, config: SyncConfigDefinition) {
|
|
90
|
+
const state = doc.sync_configs.find((c) => c._id.equals(config._id));
|
|
91
|
+
if (state == null) {
|
|
92
|
+
throw new ServiceAssertionError(`Cannot find sync config ${config._id} in replication stream ${doc._id}`);
|
|
93
|
+
}
|
|
94
|
+
super(db, {
|
|
95
|
+
replicationStreamId: doc._id,
|
|
96
|
+
sync_rules_content: config.content,
|
|
97
|
+
compiled_plan: config.serialized_plan ?? null,
|
|
98
|
+
|
|
99
|
+
replicationStreamName: doc.slot_name ?? `powersync_${doc._id}`,
|
|
100
|
+
storageVersion: doc.storage_version,
|
|
101
|
+
mapping: SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(config.rule_mapping),
|
|
102
|
+
syncConfigId: config._id,
|
|
103
|
+
syncConfigState: state.state
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getSyncConfigStatus(): Promise<storage.PersistedSyncConfigStatus | null> {
|
|
108
|
+
const doc = await this.db.sync_rules.findOne<ReplicationStreamDocumentV3>({
|
|
109
|
+
_id: this.replicationStreamId,
|
|
110
|
+
'sync_configs._id': this.syncConfigObjectId
|
|
111
|
+
});
|
|
112
|
+
if (doc == null) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const state = doc.sync_configs.find((c) => c._id.equals(this.syncConfigObjectId));
|
|
117
|
+
if (state == null) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return syncConfigStatusFromV3(doc, state);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function syncConfigStatusFromV1(doc: SyncRuleDocumentV1): storage.PersistedSyncConfigStatus {
|
|
126
|
+
return {
|
|
127
|
+
id: String(doc._id),
|
|
128
|
+
replicationStreamId: doc._id,
|
|
129
|
+
state: doc.state,
|
|
130
|
+
snapshot_done: doc.snapshot_done,
|
|
131
|
+
last_checkpoint_lsn: doc.last_checkpoint_lsn,
|
|
132
|
+
last_fatal_error: doc.last_fatal_error,
|
|
133
|
+
last_fatal_error_ts: doc.last_fatal_error_ts,
|
|
134
|
+
last_keepalive_ts: doc.last_keepalive_ts,
|
|
135
|
+
last_checkpoint_ts: doc.last_checkpoint_ts
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function syncConfigStatusFromV3(
|
|
140
|
+
doc: ReplicationStreamDocumentV3,
|
|
141
|
+
state: SyncRuleConfigStateV3
|
|
142
|
+
): storage.PersistedSyncConfigStatus {
|
|
143
|
+
return {
|
|
144
|
+
id: state._id.toHexString(),
|
|
145
|
+
replicationStreamId: doc._id,
|
|
146
|
+
state: state.state,
|
|
147
|
+
snapshot_done: state.snapshot_done,
|
|
148
|
+
last_checkpoint_lsn: state.last_checkpoint_lsn,
|
|
149
|
+
last_fatal_error: doc.last_fatal_error,
|
|
150
|
+
last_fatal_error_ts: doc.last_fatal_error_ts,
|
|
151
|
+
last_keepalive_ts: doc.last_keepalive_ts,
|
|
152
|
+
last_checkpoint_ts: doc.last_checkpoint_ts
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -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
|