@powersync/service-module-mongodb-storage 0.15.4 → 0.16.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 +35 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +1 -1
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -1
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +2 -2
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +2 -2
- package/dist/storage/MongoBucketStorage.js +47 -34
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.d.ts +16 -14
- package/dist/storage/implementation/MongoBucketBatch.js +80 -115
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
- package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
- package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
- package/dist/storage/implementation/MongoChecksums.d.ts +28 -17
- package/dist/storage/implementation/MongoChecksums.js +13 -72
- package/dist/storage/implementation/MongoChecksums.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +98 -58
- package/dist/storage/implementation/MongoCompactor.js +229 -296
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParameterCompactor.d.ts +11 -6
- package/dist/storage/implementation/MongoParameterCompactor.js +11 -8
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
- package/dist/storage/implementation/MongoPersistedSyncRules.js +64 -0
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +9 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +47 -29
- package/dist/storage/implementation/MongoSyncBucketStorage.js +94 -387
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -3
- package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/OperationBatch.js +1 -1
- package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
- package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
- package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
- package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
- package/dist/storage/implementation/common/PersistedBatch.js +237 -0
- package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
- package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
- package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
- package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
- package/dist/storage/implementation/common/SourceRecordStore.d.ts +36 -0
- package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
- package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
- package/dist/storage/implementation/db.d.ts +32 -35
- package/dist/storage/implementation/db.js +77 -99
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +62 -33
- package/dist/storage/implementation/models.js +20 -1
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +13 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +41 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +283 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
- package/dist/storage/implementation/v1/models.d.ts +34 -0
- package/dist/storage/implementation/v1/models.js +37 -0
- package/dist/storage/implementation/v1/models.js.map +1 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +13 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +5 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +41 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +407 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +21 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +43 -0
- package/dist/storage/implementation/v3/models.js +34 -0
- package/dist/storage/implementation/v3/models.js.map +1 -0
- package/dist/storage/storage-index.d.ts +6 -3
- package/dist/storage/storage-index.js +6 -3
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/utils/util.d.ts +10 -3
- package/dist/utils/util.js +24 -3
- package/dist/utils/util.js.map +1 -1
- package/package.json +9 -9
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +6 -6
- package/src/storage/MongoBucketStorage.ts +92 -59
- package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
- package/src/storage/implementation/MongoBucketBatch.ts +110 -144
- package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
- package/src/storage/implementation/MongoChecksums.ts +52 -75
- package/src/storage/implementation/MongoCompactor.ts +374 -404
- package/src/storage/implementation/MongoParameterCompactor.ts +37 -24
- package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
- package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +17 -0
- package/src/storage/implementation/MongoSyncBucketStorage.ts +181 -455
- package/src/storage/implementation/MongoSyncRulesLock.ts +11 -13
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +3 -1
- package/src/storage/implementation/OperationBatch.ts +1 -1
- package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
- package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
- package/src/storage/implementation/common/PersistedBatch.ts +364 -0
- package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
- package/src/storage/implementation/common/SourceRecordStore.ts +49 -0
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
- package/src/storage/implementation/db.ts +105 -129
- package/src/storage/implementation/models.ts +82 -36
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -0
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
- package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
- package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +448 -0
- package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -0
- package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
- package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
- package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
- package/src/storage/implementation/v1/models.ts +84 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -0
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
- package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
- package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
- package/src/storage/implementation/v3/MongoParameterLookupV3.ts +12 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +550 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -0
- package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
- package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +112 -0
- package/src/storage/implementation/v3/models.ts +96 -0
- package/src/storage/storage-index.ts +6 -3
- package/src/utils/util.ts +34 -5
- package/test/src/storage_compacting.test.ts +57 -29
- package/test/src/storage_sync.test.ts +351 -5
- package/test/tsconfig.json +0 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/PersistedBatch.d.ts +0 -71
- package/dist/storage/implementation/PersistedBatch.js +0 -354
- package/dist/storage/implementation/PersistedBatch.js.map +0 -1
- package/src/storage/implementation/PersistedBatch.ts +0 -432
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { ReplicationAssertionError } from '@powersync/lib-services-framework';
|
|
3
|
+
import { storage } from '@powersync/service-core';
|
|
4
|
+
import { BucketDefinitionId } from '../BucketDefinitionMapping.js';
|
|
5
|
+
import { SingleBucketStore } from '../common/SingleBucketStore.js';
|
|
6
|
+
import { BucketStateDocumentBase, LEGACY_BUCKET_DATA_DEFINITION_ID } from '../models.js';
|
|
7
|
+
import { DirtyBucket, MongoCompactor } from '../MongoCompactor.js';
|
|
8
|
+
import { BucketStateDocumentV1 } from './models.js';
|
|
9
|
+
import type { MongoSyncBucketStorageV1 } from './MongoSyncBucketStorageV1.js';
|
|
10
|
+
import { SingleBucketStoreV1 } from './SingleBucketStoreV1.js';
|
|
11
|
+
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
12
|
+
|
|
13
|
+
export class MongoCompactorV1 extends MongoCompactor {
|
|
14
|
+
// Override types to the more specific ones
|
|
15
|
+
declare protected readonly db: VersionedPowerSyncMongoV1;
|
|
16
|
+
declare protected readonly storage: MongoSyncBucketStorageV1;
|
|
17
|
+
|
|
18
|
+
public async *dirtyBucketBatches(options: {
|
|
19
|
+
minBucketChanges: number;
|
|
20
|
+
minChangeRatio: number;
|
|
21
|
+
}): AsyncGenerator<DirtyBucket[]> {
|
|
22
|
+
if (options.minBucketChanges <= 0) {
|
|
23
|
+
throw new ReplicationAssertionError('minBucketChanges must be >= 1');
|
|
24
|
+
}
|
|
25
|
+
// Previously, we used an index on {_id.g: 1, estimate_since_compact.count: 1} to only scan buckets with changes.
|
|
26
|
+
// That works well if there are only a small number of dirty buckets, but it causes repeated rescans while data is
|
|
27
|
+
// still changing. We now iterate through all V1 bucket_state rows for the group and filter after projecting.
|
|
28
|
+
yield* this.dirtyBucketBatchesForCollection(
|
|
29
|
+
this.db.bucketStateV1,
|
|
30
|
+
{ g: this.group_id, b: new mongo.MinKey() as any },
|
|
31
|
+
{ g: this.group_id, b: new mongo.MaxKey() as any },
|
|
32
|
+
options,
|
|
33
|
+
() => null
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public async dirtyBucketBatchForChecksums(options: { minBucketChanges: number }): Promise<DirtyBucket[]> {
|
|
38
|
+
if (options.minBucketChanges <= 0) {
|
|
39
|
+
throw new ReplicationAssertionError('minBucketChanges must be >= 1');
|
|
40
|
+
}
|
|
41
|
+
// Unlike dirtyBucketBatches, this path is resumable after restart because populateChecksums resets
|
|
42
|
+
// estimate_since_compact as it progresses.
|
|
43
|
+
return this.dirtyBucketBatchForChecksumsForCollection(
|
|
44
|
+
this.db.bucketStateV1,
|
|
45
|
+
{
|
|
46
|
+
'_id.g': this.group_id,
|
|
47
|
+
'estimate_since_compact.count': { $gte: options.minBucketChanges }
|
|
48
|
+
},
|
|
49
|
+
() => null
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected async writeBucketStateUpdates(): Promise<void> {
|
|
54
|
+
await this.db.bucketStateV1.bulkWrite(
|
|
55
|
+
this.bucketStateUpdates as mongo.AnyBulkWriteOperation<BucketStateDocumentV1>[],
|
|
56
|
+
{ ordered: false }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected async computeChecksumsForBuckets(
|
|
61
|
+
buckets: Pick<DirtyBucket, 'bucket' | 'definitionId'>[]
|
|
62
|
+
): Promise<storage.PartialChecksumMap> {
|
|
63
|
+
return this.storage.checksums.computePartialChecksumsDirectByBucket(
|
|
64
|
+
buckets.map(({ bucket }) => ({
|
|
65
|
+
bucket,
|
|
66
|
+
end: this.maxOpId
|
|
67
|
+
}))
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected bucketStateFilter(
|
|
72
|
+
bucket: string,
|
|
73
|
+
_definitionId: BucketDefinitionId | null
|
|
74
|
+
): mongo.Filter<BucketStateDocumentBase> {
|
|
75
|
+
return {
|
|
76
|
+
_id: {
|
|
77
|
+
g: this.group_id,
|
|
78
|
+
b: bucket
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected async getBucketDataContext(
|
|
84
|
+
bucket: string,
|
|
85
|
+
_definitionId: BucketDefinitionId | null
|
|
86
|
+
): Promise<SingleBucketStore | null> {
|
|
87
|
+
return new SingleBucketStoreV1(this.db, {
|
|
88
|
+
replicationStreamId: this.group_id,
|
|
89
|
+
definitionId: LEGACY_BUCKET_DATA_DEFINITION_ID,
|
|
90
|
+
bucket
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
|
|
3
|
+
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
4
|
+
|
|
5
|
+
export class MongoParameterCompactorV1 extends MongoParameterCompactor {
|
|
6
|
+
declare protected readonly db: VersionedPowerSyncMongoV1;
|
|
7
|
+
|
|
8
|
+
protected async getCollections(): Promise<mongo.Collection<mongo.Document>[]> {
|
|
9
|
+
return [this.db.parameterIndexV1 as unknown as mongo.Collection<mongo.Document>];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected collectionFilter(): mongo.Document {
|
|
13
|
+
return {
|
|
14
|
+
'key.g': this.group_id
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected deleteFilter(doc: mongo.Document): mongo.Document {
|
|
19
|
+
return {
|
|
20
|
+
'key.g': doc.key.g as number,
|
|
21
|
+
lookup: doc.lookup,
|
|
22
|
+
_id: { $lte: doc._id },
|
|
23
|
+
key: doc.key
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
3
|
+
import {
|
|
4
|
+
CheckpointChanges,
|
|
5
|
+
deserializeParameterLookup,
|
|
6
|
+
GetCheckpointChangesOptions,
|
|
7
|
+
InternalOpId,
|
|
8
|
+
internalToExternalOpId,
|
|
9
|
+
ParameterSetLimitExceededError,
|
|
10
|
+
ProtocolOpId,
|
|
11
|
+
storage,
|
|
12
|
+
utils
|
|
13
|
+
} from '@powersync/service-core';
|
|
14
|
+
import { JSONBig } from '@powersync/service-jsonbig';
|
|
15
|
+
import { ParameterLookupRows, ScopedParameterLookup, SqliteJsonRow } from '@powersync/service-sync-rules';
|
|
16
|
+
import * as bson from 'bson';
|
|
17
|
+
import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../../utils/util.js';
|
|
18
|
+
import { MongoBucketStorage } from '../../MongoBucketStorage.js';
|
|
19
|
+
import {
|
|
20
|
+
MongoSyncBucketStorageCheckpoint,
|
|
21
|
+
MongoSyncBucketStorageContext
|
|
22
|
+
} from '../common/MongoSyncBucketStorageContext.js';
|
|
23
|
+
import { CommonSourceTableDocument, SourceKey } from '../models.js';
|
|
24
|
+
import { MongoBucketBatchOptions } from '../MongoBucketBatch.js';
|
|
25
|
+
import { MongoChecksums } from '../MongoChecksums.js';
|
|
26
|
+
import { MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
|
|
27
|
+
import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
|
|
28
|
+
import { MongoPersistedSyncRulesContent } from '../MongoPersistedSyncRulesContent.js';
|
|
29
|
+
import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from '../MongoSyncBucketStorage.js';
|
|
30
|
+
import { BucketDataDocumentV1, BucketDataKeyV1, BucketStateDocument, loadBucketDataDocumentV1 } from './models.js';
|
|
31
|
+
import { MongoBucketBatchV1 } from './MongoBucketBatchV1.js';
|
|
32
|
+
import { MongoChecksumsV1 } from './MongoChecksumsV1.js';
|
|
33
|
+
import { MongoCompactorV1 } from './MongoCompactorV1.js';
|
|
34
|
+
import { MongoParameterCompactorV1 } from './MongoParameterCompactorV1.js';
|
|
35
|
+
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
36
|
+
|
|
37
|
+
export class MongoSyncBucketStorageV1 extends MongoSyncBucketStorage {
|
|
38
|
+
// Declare types to be more specific
|
|
39
|
+
declare readonly db: VersionedPowerSyncMongoV1;
|
|
40
|
+
declare readonly checksums: MongoChecksumsV1;
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
factory: MongoBucketStorage,
|
|
44
|
+
group_id: number,
|
|
45
|
+
sync_rules: MongoPersistedSyncRulesContent,
|
|
46
|
+
slot_name: string,
|
|
47
|
+
writeCheckpointMode: storage.WriteCheckpointMode | undefined,
|
|
48
|
+
options: MongoSyncBucketStorageOptions
|
|
49
|
+
) {
|
|
50
|
+
super(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected async initializeVersionStorage(): Promise<void> {}
|
|
54
|
+
|
|
55
|
+
protected createWriterImpl(batchOptions: MongoBucketBatchOptions): storage.BucketStorageBatch {
|
|
56
|
+
return new MongoBucketBatchV1(batchOptions);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums {
|
|
60
|
+
return new MongoChecksumsV1(this.db, this.group_id, {
|
|
61
|
+
...options.checksumOptions,
|
|
62
|
+
storageConfig: options?.storageConfig,
|
|
63
|
+
mapping: this.sync_rules.mapping
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
createMongoCompactor(options: MongoCompactOptions): MongoCompactor {
|
|
68
|
+
return new MongoCompactorV1(this, this.db, options);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected createMongoParameterCompactor(
|
|
72
|
+
checkpoint: InternalOpId,
|
|
73
|
+
options: storage.CompactOptions
|
|
74
|
+
): MongoParameterCompactor {
|
|
75
|
+
return new MongoParameterCompactorV1(this.db, this.group_id, checkpoint, options);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected sourceTableBaseId(): Partial<CommonSourceTableDocument> {
|
|
79
|
+
return { group_id: this.group_id };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected augmentCreatedSourceTableDocument(
|
|
83
|
+
_createDoc: CommonSourceTableDocument,
|
|
84
|
+
_options: storage.ResolveTableOptions,
|
|
85
|
+
_candidateSourceTable: storage.SourceTable
|
|
86
|
+
): void {}
|
|
87
|
+
|
|
88
|
+
protected async initializeResolvedSourceRecords(_sourceTableId: bson.ObjectId): Promise<void> {}
|
|
89
|
+
|
|
90
|
+
protected override get versionContext(): MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1> {
|
|
91
|
+
return {
|
|
92
|
+
db: this.db,
|
|
93
|
+
group_id: this.group_id,
|
|
94
|
+
mapping: this.mapping
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected getParameterSetsImpl(
|
|
99
|
+
checkpoint: MongoSyncBucketStorageCheckpoint,
|
|
100
|
+
lookups: ScopedParameterLookup[],
|
|
101
|
+
limit: number
|
|
102
|
+
): Promise<ParameterLookupRows[]> {
|
|
103
|
+
return getParameterSetsV1(this.versionContext, checkpoint, lookups, limit);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
protected getBucketDataBatchImpl(
|
|
107
|
+
checkpoint: utils.InternalOpId,
|
|
108
|
+
dataBuckets: storage.BucketDataRequest[],
|
|
109
|
+
options?: storage.BucketDataBatchOptions
|
|
110
|
+
): AsyncIterable<storage.SyncBucketDataChunk> {
|
|
111
|
+
return getBucketDataBatchV1(this.versionContext, checkpoint, dataBuckets, options);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected async clearBucketData(signal?: AbortSignal): Promise<void> {
|
|
115
|
+
await this.clearDeleteMany(
|
|
116
|
+
'bucket data',
|
|
117
|
+
() =>
|
|
118
|
+
this.db.bucket_data.deleteMany(
|
|
119
|
+
{
|
|
120
|
+
_id: idPrefixFilter<BucketDataKeyV1>({ g: this.group_id }, ['b', 'o'])
|
|
121
|
+
},
|
|
122
|
+
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
123
|
+
),
|
|
124
|
+
signal
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected async clearParameterIndexes(signal?: AbortSignal): Promise<void> {
|
|
129
|
+
await this.clearDeleteMany(
|
|
130
|
+
'parameter index',
|
|
131
|
+
() =>
|
|
132
|
+
this.db.parameterIndexV1.deleteMany(
|
|
133
|
+
{
|
|
134
|
+
'key.g': this.group_id
|
|
135
|
+
},
|
|
136
|
+
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
137
|
+
),
|
|
138
|
+
signal
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
protected async clearSourceRecords(signal?: AbortSignal): Promise<void> {
|
|
143
|
+
await this.clearDeleteMany(
|
|
144
|
+
'source records',
|
|
145
|
+
() =>
|
|
146
|
+
this.db.sourceRecordsV1.deleteMany(
|
|
147
|
+
{
|
|
148
|
+
_id: idPrefixFilter<SourceKey>({ g: this.group_id }, ['t', 'k'])
|
|
149
|
+
},
|
|
150
|
+
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
151
|
+
),
|
|
152
|
+
signal
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected async clearBucketState(signal?: AbortSignal): Promise<void> {
|
|
157
|
+
await this.clearDeleteMany(
|
|
158
|
+
'bucket state',
|
|
159
|
+
() =>
|
|
160
|
+
this.db.bucketStateV1.deleteMany(
|
|
161
|
+
{
|
|
162
|
+
_id: idPrefixFilter<BucketStateDocument['_id']>({ g: this.group_id }, ['b'])
|
|
163
|
+
},
|
|
164
|
+
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
165
|
+
),
|
|
166
|
+
signal
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected async clearSourceTables(signal?: AbortSignal): Promise<void> {
|
|
171
|
+
await this.clearDeleteMany(
|
|
172
|
+
'source tables',
|
|
173
|
+
() =>
|
|
174
|
+
this.db.commonSourceTables(this.group_id).deleteMany(
|
|
175
|
+
{
|
|
176
|
+
group_id: this.group_id
|
|
177
|
+
},
|
|
178
|
+
{ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
179
|
+
),
|
|
180
|
+
signal
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
protected getDataBucketChangesImpl(
|
|
185
|
+
options: GetCheckpointChangesOptions
|
|
186
|
+
): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
|
|
187
|
+
return getDataBucketChangesV1(this.versionContext, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected getParameterBucketChangesImpl(
|
|
191
|
+
options: GetCheckpointChangesOptions
|
|
192
|
+
): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
|
|
193
|
+
return getParameterBucketChangesV1(this.versionContext, options);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function getParameterSetsV1(
|
|
198
|
+
ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
|
|
199
|
+
checkpoint: MongoSyncBucketStorageCheckpoint,
|
|
200
|
+
lookups: ScopedParameterLookup[],
|
|
201
|
+
limit: number
|
|
202
|
+
): Promise<ParameterLookupRows[]> {
|
|
203
|
+
return ctx.db.client.withSession({ snapshot: true }, async (session) => {
|
|
204
|
+
setSessionSnapshotTime(session, checkpoint.snapshotTime);
|
|
205
|
+
const lookupFilter = lookups.map((lookup) => {
|
|
206
|
+
return storage.serializeLookup(lookup);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const rows = await ctx.db.parameterIndexV1
|
|
210
|
+
.aggregate<{ lookup: number; bucket_parameters: SqliteJsonRow }>(
|
|
211
|
+
[
|
|
212
|
+
{
|
|
213
|
+
$match: {
|
|
214
|
+
'key.g': ctx.group_id,
|
|
215
|
+
lookup: { $in: lookupFilter },
|
|
216
|
+
_id: { $lte: checkpoint.checkpoint }
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
$set: {
|
|
221
|
+
index: { $indexOfArray: [lookupFilter, '$lookup'] }
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
$sort: {
|
|
226
|
+
_id: -1
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
$group: {
|
|
231
|
+
_id: { key: '$key', lookup: '$index' },
|
|
232
|
+
bucket_parameters: {
|
|
233
|
+
$first: '$bucket_parameters'
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
$project: {
|
|
239
|
+
_id: false,
|
|
240
|
+
lookup: '$_id.lookup',
|
|
241
|
+
bucket_parameters: true
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
{ $unwind: '$bucket_parameters' },
|
|
245
|
+
{
|
|
246
|
+
$limit: limit + 1
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
{
|
|
250
|
+
session,
|
|
251
|
+
readConcern: 'snapshot',
|
|
252
|
+
maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
.toArray()
|
|
256
|
+
.catch((e) => {
|
|
257
|
+
throw lib_mongo.mapQueryError(e, 'while evaluating parameter queries');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (rows.length > limit) {
|
|
261
|
+
throw new ParameterSetLimitExceededError(limit);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const byLookup = Map.groupBy(rows, (row) => lookups[row.lookup]);
|
|
265
|
+
const results: ParameterLookupRows[] = [];
|
|
266
|
+
byLookup.forEach((value, lookup) => results.push({ lookup, rows: value.map((r) => r.bucket_parameters) }));
|
|
267
|
+
return results;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export async function* getBucketDataBatchV1(
|
|
272
|
+
ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
|
|
273
|
+
checkpoint: utils.InternalOpId,
|
|
274
|
+
dataBuckets: storage.BucketDataRequest[],
|
|
275
|
+
options?: storage.BucketDataBatchOptions
|
|
276
|
+
): AsyncIterable<storage.SyncBucketDataChunk> {
|
|
277
|
+
if (dataBuckets.length == 0) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
let filters: mongo.Filter<BucketDataDocumentV1>[] = [];
|
|
281
|
+
const bucketMap = new Map(dataBuckets.map((request) => [request.bucket, request.start]));
|
|
282
|
+
|
|
283
|
+
if (checkpoint == null) {
|
|
284
|
+
throw new Error('checkpoint is null');
|
|
285
|
+
}
|
|
286
|
+
const end = checkpoint;
|
|
287
|
+
for (let { bucket: name, start } of dataBuckets) {
|
|
288
|
+
filters.push({
|
|
289
|
+
_id: {
|
|
290
|
+
$gt: {
|
|
291
|
+
g: ctx.group_id,
|
|
292
|
+
b: name,
|
|
293
|
+
o: start
|
|
294
|
+
},
|
|
295
|
+
$lte: {
|
|
296
|
+
g: ctx.group_id,
|
|
297
|
+
b: name,
|
|
298
|
+
o: end as any
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const batchLimit = options?.limit ?? storage.DEFAULT_DOCUMENT_BATCH_LIMIT;
|
|
305
|
+
const chunkSizeLimitBytes = options?.chunkLimitBytes ?? storage.DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES;
|
|
306
|
+
|
|
307
|
+
const cursor = ctx.db.bucket_data.find(
|
|
308
|
+
{
|
|
309
|
+
$or: filters
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
session: undefined,
|
|
313
|
+
sort: { _id: 1 },
|
|
314
|
+
limit: batchLimit,
|
|
315
|
+
batchSize: batchLimit + 1,
|
|
316
|
+
raw: true,
|
|
317
|
+
maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
|
|
318
|
+
}
|
|
319
|
+
) as unknown as mongo.FindCursor<Buffer>;
|
|
320
|
+
|
|
321
|
+
let { data, hasMore: batchHasMore } = await readSingleBatch(cursor).catch((e) => {
|
|
322
|
+
throw lib_mongo.mapQueryError(e, 'while reading bucket data');
|
|
323
|
+
});
|
|
324
|
+
if (data.length == batchLimit) {
|
|
325
|
+
batchHasMore = true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let chunkSizeBytes = 0;
|
|
329
|
+
let currentChunk: utils.SyncBucketData | null = null;
|
|
330
|
+
let targetOp: InternalOpId | null = null;
|
|
331
|
+
|
|
332
|
+
for (let rawData of data) {
|
|
333
|
+
const row = loadBucketDataDocumentV1(
|
|
334
|
+
bson.deserialize(rawData, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV1
|
|
335
|
+
);
|
|
336
|
+
const bucket = row.bucketKey.bucket;
|
|
337
|
+
|
|
338
|
+
if (currentChunk == null || currentChunk.bucket != bucket || chunkSizeBytes >= chunkSizeLimitBytes) {
|
|
339
|
+
let start: ProtocolOpId | undefined = undefined;
|
|
340
|
+
if (currentChunk != null) {
|
|
341
|
+
if (currentChunk.bucket == bucket) {
|
|
342
|
+
currentChunk.has_more = true;
|
|
343
|
+
start = currentChunk.next_after;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const yieldChunk = currentChunk;
|
|
347
|
+
currentChunk = null;
|
|
348
|
+
chunkSizeBytes = 0;
|
|
349
|
+
yield { chunkData: yieldChunk, targetOp: targetOp };
|
|
350
|
+
targetOp = null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (start == null) {
|
|
354
|
+
const startOpId = bucketMap.get(bucket);
|
|
355
|
+
if (startOpId == null) {
|
|
356
|
+
throw new Error(`data for unexpected bucket: ${bucket}`);
|
|
357
|
+
}
|
|
358
|
+
start = internalToExternalOpId(startOpId);
|
|
359
|
+
}
|
|
360
|
+
currentChunk = {
|
|
361
|
+
bucket,
|
|
362
|
+
after: start,
|
|
363
|
+
has_more: false,
|
|
364
|
+
data: [],
|
|
365
|
+
next_after: start
|
|
366
|
+
};
|
|
367
|
+
targetOp = null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const entry = mapOpEntry(row);
|
|
371
|
+
|
|
372
|
+
if (row.target_op != null && (targetOp == null || row.target_op > targetOp)) {
|
|
373
|
+
targetOp = row.target_op;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
currentChunk.data.push(entry);
|
|
377
|
+
currentChunk.next_after = entry.op_id;
|
|
378
|
+
chunkSizeBytes += rawData.byteLength;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (currentChunk != null) {
|
|
382
|
+
const yieldChunk = currentChunk;
|
|
383
|
+
yieldChunk.has_more = batchHasMore;
|
|
384
|
+
yield { chunkData: yieldChunk, targetOp: targetOp };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export async function getDataBucketChangesV1(
|
|
389
|
+
ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
|
|
390
|
+
options: GetCheckpointChangesOptions
|
|
391
|
+
): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
|
|
392
|
+
const limit = 1000;
|
|
393
|
+
const bucketStateUpdates = await ctx.db.bucketStateV1
|
|
394
|
+
.find(
|
|
395
|
+
{
|
|
396
|
+
'_id.g': ctx.group_id,
|
|
397
|
+
last_op: { $gt: options.lastCheckpoint.checkpoint }
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
projection: {
|
|
401
|
+
'_id.b': 1
|
|
402
|
+
},
|
|
403
|
+
limit: limit + 1,
|
|
404
|
+
batchSize: limit + 2,
|
|
405
|
+
singleBatch: true
|
|
406
|
+
}
|
|
407
|
+
)
|
|
408
|
+
.toArray();
|
|
409
|
+
|
|
410
|
+
const buckets = bucketStateUpdates.map((doc) => doc._id.b);
|
|
411
|
+
const invalidateDataBuckets = buckets.length > limit;
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
invalidateDataBuckets,
|
|
415
|
+
updatedDataBuckets: invalidateDataBuckets ? new Set<string>() : new Set(buckets)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export async function getParameterBucketChangesV1(
|
|
420
|
+
ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
|
|
421
|
+
options: GetCheckpointChangesOptions
|
|
422
|
+
): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
|
|
423
|
+
const limit = 1000;
|
|
424
|
+
const parameterUpdates = await ctx.db.parameterIndexV1
|
|
425
|
+
.find(
|
|
426
|
+
{
|
|
427
|
+
_id: { $gt: options.lastCheckpoint.checkpoint, $lte: options.nextCheckpoint.checkpoint },
|
|
428
|
+
'key.g': ctx.group_id
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
projection: {
|
|
432
|
+
lookup: 1
|
|
433
|
+
},
|
|
434
|
+
limit: limit + 1,
|
|
435
|
+
batchSize: limit + 2,
|
|
436
|
+
singleBatch: true
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
.toArray();
|
|
440
|
+
const invalidateParameterUpdates = parameterUpdates.length > limit;
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
invalidateParameterBuckets: invalidateParameterUpdates,
|
|
444
|
+
updatedParameterLookups: invalidateParameterUpdates
|
|
445
|
+
? new Set<string>()
|
|
446
|
+
: new Set<string>(parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookup(p.lookup))))
|
|
447
|
+
};
|
|
448
|
+
}
|