@powersync/service-module-mongodb-storage 0.0.0-dev-20260602073133 → 0.0.0-dev-20260909133214
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 +177 -5
- package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.d.ts +3 -0
- package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.js +41 -0
- package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.js.map +1 -0
- package/dist/storage/MongoBucketStorage.d.ts +71 -18
- package/dist/storage/MongoBucketStorage.js +516 -202
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +71 -29
- package/dist/storage/implementation/MongoBucketBatch.js +140 -66
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoChecksums.d.ts +41 -13
- package/dist/storage/implementation/MongoChecksums.js +36 -121
- package/dist/storage/implementation/MongoChecksums.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +28 -90
- package/dist/storage/implementation/MongoCompactor.js +87 -481
- 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 +82 -8
- package/dist/storage/implementation/MongoParameterCompactor.js +312 -76
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +21 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js +93 -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 +125 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
- package/dist/storage/implementation/MongoStorageProvider.js +41 -4
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +81 -37
- package/dist/storage/implementation/MongoSyncBucketStorage.js +159 -86
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.js +23 -11
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +11 -8
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +159 -33
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +11 -5
- package/dist/storage/implementation/SyncRuleStateUpdate.js +14 -6
- package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +28 -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 +34 -2
- package/dist/storage/implementation/common/PersistedBatch.js +63 -10
- 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.d.ts +4 -1
- package/dist/storage/implementation/db.js +31 -16
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +55 -13
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +15 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +135 -107
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +4 -4
- package/dist/storage/implementation/v1/MongoChecksumsV1.js +119 -7
- package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +47 -7
- package/dist/storage/implementation/v1/MongoCompactorV1.js +478 -8
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +16 -2
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -9
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +33 -27
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +146 -73
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
- package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +9 -10
- package/dist/storage/implementation/v1/SingleBucketStoreV1.js +2 -8
- package/dist/storage/implementation/v1/SingleBucketStoreV1.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/v1/models.js +1 -0
- package/dist/storage/implementation/v1/models.js.map +1 -1
- package/dist/storage/implementation/v3/BucketDataContextV3.d.ts +13 -0
- package/dist/storage/implementation/v3/BucketDataContextV3.js +23 -0
- package/dist/storage/implementation/v3/BucketDataContextV3.js.map +1 -0
- package/dist/storage/implementation/v3/CompactionLease.d.ts +50 -0
- package/dist/storage/implementation/v3/CompactionLease.js +131 -0
- package/dist/storage/implementation/v3/CompactionLease.js.map +1 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +16 -5
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +393 -262
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +30 -9
- package/dist/storage/implementation/v3/MongoChecksumsV3.js +161 -20
- package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +108 -18
- package/dist/storage/implementation/v3/MongoCompactorV3.js +1115 -47
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +12 -3
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +13 -8
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +54 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +319 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +61 -36
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +353 -161
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.d.ts +26 -0
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js +82 -0
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js.map +1 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +8 -3
- package/dist/storage/implementation/v3/PersistedBatchV3.js +131 -29
- package/dist/storage/implementation/v3/PersistedBatchV3.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 +26 -14
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +73 -29
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
- package/dist/storage/implementation/v3/bucket-format.d.ts +13 -0
- package/dist/storage/implementation/v3/bucket-format.js +79 -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 +30 -0
- package/dist/storage/implementation/v3/chunking.js.map +1 -0
- package/dist/storage/implementation/v3/compact-utils.d.ts +107 -0
- package/dist/storage/implementation/v3/compact-utils.js +216 -0
- package/dist/storage/implementation/v3/compact-utils.js.map +1 -0
- package/dist/storage/implementation/v3/compaction-constants.d.ts +5 -0
- package/dist/storage/implementation/v3/compaction-constants.js +6 -0
- package/dist/storage/implementation/v3/compaction-constants.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +187 -23
- package/dist/storage/implementation/v3/models.js +11 -28
- package/dist/storage/implementation/v3/models.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +20 -0
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +48 -0
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorage.d.ts +33 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorage.js +12 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorage.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +68 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +154 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.d.ts +37 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js +167 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.d.ts +67 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +439 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -0
- package/dist/storage/implementation/v3/source-table-utils.d.ts +63 -0
- package/dist/storage/implementation/v3/source-table-utils.js +206 -0
- package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
- package/dist/storage/storage-index.d.ts +5 -4
- package/dist/storage/storage-index.js +5 -4
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/types/types.d.ts +71 -3
- package/dist/types/types.js +58 -2
- package/dist/types/types.js.map +1 -1
- package/dist/utils/test-utils.d.ts +1 -2
- package/dist/utils/test-utils.js +7 -1
- package/dist/utils/test-utils.js.map +1 -1
- package/dist/utils/util.d.ts +9 -2
- package/dist/utils/util.js +131 -5
- package/dist/utils/util.js.map +1 -1
- package/package.json +13 -19
- package/src/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.ts +55 -0
- package/src/storage/MongoBucketStorage.ts +728 -242
- package/src/storage/implementation/MongoBucketBatch.ts +196 -91
- package/src/storage/implementation/MongoChecksums.ts +93 -153
- package/src/storage/implementation/MongoCompactor.ts +106 -631
- package/src/storage/implementation/MongoHydrationState.ts +61 -0
- package/src/storage/implementation/MongoParameterCompactor.ts +392 -87
- package/src/storage/implementation/MongoParsedSyncConfigSet.ts +122 -0
- package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
- package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +156 -0
- package/src/storage/implementation/MongoStorageProvider.ts +48 -4
- package/src/storage/implementation/MongoSyncBucketStorage.ts +226 -118
- package/src/storage/implementation/MongoSyncRulesLock.ts +28 -12
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +194 -52
- package/src/storage/implementation/SyncRuleStateUpdate.ts +13 -6
- package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +31 -0
- package/src/storage/implementation/common/PersistedBatch.ts +83 -15
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
- package/src/storage/implementation/db.ts +40 -22
- package/src/storage/implementation/models.ts +58 -14
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +174 -125
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +154 -12
- package/src/storage/implementation/v1/MongoCompactorV1.ts +594 -25
- package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +25 -9
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +226 -129
- package/src/storage/implementation/v1/SingleBucketStoreV1.ts +18 -27
- package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
- package/src/storage/implementation/v1/models.ts +12 -1
- package/src/storage/implementation/v3/BucketDataContextV3.ts +32 -0
- package/src/storage/implementation/v3/CompactionLease.ts +152 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +502 -324
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +250 -32
- package/src/storage/implementation/v3/MongoCompactorV3.ts +1439 -70
- package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +19 -9
- package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +473 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +453 -211
- package/src/storage/implementation/v3/MongoWriteCheckpointAPIV3.ts +111 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +169 -45
- package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +99 -38
- package/src/storage/implementation/v3/bucket-format.ts +99 -0
- package/src/storage/implementation/v3/chunking.ts +40 -0
- package/src/storage/implementation/v3/compact-utils.ts +320 -0
- package/src/storage/implementation/v3/compaction-constants.ts +5 -0
- package/src/storage/implementation/v3/models.ts +228 -58
- package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +62 -0
- package/src/storage/implementation/v3/object-storage/ObjectStorage.ts +44 -0
- package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +182 -0
- package/src/storage/implementation/v3/object-storage/ObjectStorageUsage.ts +230 -0
- package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +573 -0
- package/src/storage/implementation/v3/source-table-utils.ts +398 -0
- package/src/storage/storage-index.ts +14 -4
- package/src/types/types.ts +76 -2
- package/src/utils/test-utils.ts +8 -3
- package/src/utils/util.ts +188 -7
- package/test/src/__snapshots__/storage.test.ts.snap +4 -54
- package/test/src/__snapshots__/storage_sync.test.ts.snap +652 -645
- package/test/src/cleanup-stopped-sync-configs.test.ts +698 -0
- package/test/src/compact-utils.test.ts +226 -0
- package/test/src/helpers/MemoryObjectStorage.ts +61 -0
- package/test/src/helpers/s3TestFactory.ts +69 -0
- package/test/src/object_storage_lifecycle.test.ts +59 -0
- package/test/src/object_storage_usage.test.ts +255 -0
- package/test/src/parameter_compacting_v1.test.ts +120 -0
- package/test/src/parameter_compacting_v3.test.ts +192 -0
- package/test/src/parameter_compaction_fence.test.ts +276 -0
- package/test/src/setup.ts +6 -1
- package/test/src/storage.test.ts +478 -13
- package/test/src/storage_compacting.test.ts +2516 -107
- package/test/src/storage_object_storage_inline_threshold.test.ts +116 -0
- package/test/src/storage_s3_checksums.test.ts +190 -0
- package/test/src/storage_s3_compaction_lifecycle.test.ts +543 -0
- package/test/src/storage_s3_reading.test.ts +768 -0
- package/test/src/storage_s3_writing.test.ts +136 -0
- package/test/src/storage_sync.test.ts +1507 -76
- package/test/src/storeCurrentData.test.ts +227 -0
- package/test/src/util.ts +3 -2
- 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/common/SingleBucketStore.d.ts +0 -54
- package/dist/storage/implementation/common/SingleBucketStore.js +0 -3
- package/dist/storage/implementation/common/SingleBucketStore.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/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +0 -18
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js +0 -48
- package/dist/storage/implementation/v3/SingleBucketStoreV3.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/common/SingleBucketStore.ts +0 -63
- package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -11
- package/src/storage/implementation/v3/SingleBucketStoreV3.ts +0 -68
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
1
2
|
import { ReplicationAssertionError } from '@powersync/lib-services-framework';
|
|
2
|
-
import { ColumnDescriptor, SourceTable, storage } from '@powersync/service-core';
|
|
3
|
+
import { ColumnDescriptor, InternalOpId, SourceTable, storage } from '@powersync/service-core';
|
|
4
|
+
import { HydratedSyncConfig } from '@powersync/service-sync-rules';
|
|
3
5
|
import * as bson from 'bson';
|
|
4
6
|
import { mongoTableId } from '../../../utils/util.js';
|
|
5
7
|
import { calculateCheckpointState } from '../CheckpointState.js';
|
|
@@ -12,19 +14,32 @@ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
|
12
14
|
import { SourceTableDocumentV1, SyncRuleDocumentV1 } from './models.js';
|
|
13
15
|
|
|
14
16
|
export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
15
|
-
declare
|
|
17
|
+
declare readonly db: VersionedPowerSyncMongoV1;
|
|
16
18
|
|
|
17
19
|
private readonly store: SourceRecordStore;
|
|
18
20
|
private needsActivation = true;
|
|
19
21
|
private lastWaitingLogThrottled = 0;
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The last persisted op that is not yet covered by a checkpoint, if any.
|
|
25
|
+
*
|
|
26
|
+
* Seeded from the persisted keepalive_op, so that ops persisted before a restart can still be
|
|
27
|
+
* included in the next checkpoint.
|
|
28
|
+
*/
|
|
29
|
+
private persisted_op: InternalOpId | null = null;
|
|
30
|
+
|
|
21
31
|
constructor(options: MongoBucketBatchOptions) {
|
|
22
32
|
super(options);
|
|
23
|
-
this.
|
|
33
|
+
this.persisted_op = options.keepaliveOp ?? null;
|
|
34
|
+
this.store = new SourceRecordStoreV1(this.db, this.replicationStreamId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected override recordPersistedOp(lastOp: InternalOpId): void {
|
|
38
|
+
this.persisted_op = lastOp;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
41
|
protected createPersistedBatch(writtenSize: number): PersistedBatch {
|
|
27
|
-
return new PersistedBatchV1(this.db, this.
|
|
42
|
+
return new PersistedBatchV1(this.db, this.replicationStreamId, this.mapping, writtenSize, {
|
|
28
43
|
logger: this.logger
|
|
29
44
|
});
|
|
30
45
|
}
|
|
@@ -32,15 +47,45 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
32
47
|
protected get sourceRecordStore(): SourceRecordStore {
|
|
33
48
|
return this.store;
|
|
34
49
|
}
|
|
35
|
-
|
|
36
50
|
protected async cleanupDroppedSourceTables(_tables: SourceTable[]) {
|
|
37
51
|
// No-op for V1: source records live in a shared collection.
|
|
38
52
|
}
|
|
39
53
|
|
|
54
|
+
protected override async batchCreateCustomWriteCheckpoints(
|
|
55
|
+
session: mongo.ClientSession,
|
|
56
|
+
opId: InternalOpId
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
await this.db.custom_write_checkpoints.bulkWrite(
|
|
59
|
+
this.write_checkpoint_batch.map((checkpointOptions) => {
|
|
60
|
+
const set: Record<string, unknown> = {
|
|
61
|
+
checkpoint: checkpointOptions.checkpoint,
|
|
62
|
+
sync_rules_id: checkpointOptions.sync_rules_id,
|
|
63
|
+
op_id: opId
|
|
64
|
+
};
|
|
65
|
+
if (checkpointOptions.checkpoint_requested_at != null) {
|
|
66
|
+
set.checkpoint_requested_at = checkpointOptions.checkpoint_requested_at;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
updateOne: {
|
|
71
|
+
filter: { user_id: checkpointOptions.user_id, sync_rules_id: checkpointOptions.sync_rules_id },
|
|
72
|
+
update: {
|
|
73
|
+
$set: set,
|
|
74
|
+
...(checkpointOptions.checkpoint_requested_at == null ? { $unset: { checkpoint_requested_at: 1 } } : {})
|
|
75
|
+
},
|
|
76
|
+
upsert: true
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}),
|
|
80
|
+
{ session }
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
40
84
|
async resolveTables(options: storage.ResolveTablesOptions): Promise<storage.ResolveTablesResult> {
|
|
41
|
-
const syncRules = options.
|
|
85
|
+
const syncRules = options.parsedSyncConfig?.hydratedSyncConfig ?? this.sync_rules;
|
|
86
|
+
const reconcile = options.reconcileSourceTables ?? storage.defaultSourceTableReconciler;
|
|
42
87
|
const { connection_id, source } = options;
|
|
43
|
-
const { schema, name, objectId, replicaIdColumns, connectionTag } = source;
|
|
88
|
+
const { schema, name, objectId, replicaIdColumns, connectionTag, sendsCompleteRows } = source;
|
|
44
89
|
|
|
45
90
|
const normalizedReplicaIdColumns = replicaIdColumns.map((column) => ({
|
|
46
91
|
name: column.name,
|
|
@@ -50,23 +95,41 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
50
95
|
|
|
51
96
|
let result: storage.ResolveTablesResult | null = null;
|
|
52
97
|
await this.db.client.withSession(async (session) => {
|
|
53
|
-
const col = this.db.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
schema_name: schema,
|
|
58
|
-
table_name: name,
|
|
59
|
-
replica_id_columns2: normalizedReplicaIdColumns
|
|
60
|
-
};
|
|
98
|
+
const col = this.db.sourceTablesV1(this.replicationStreamId);
|
|
99
|
+
|
|
100
|
+
// Find records that overlap by name or relation id.
|
|
101
|
+
const orClauses: Record<string, unknown>[] = [{ schema_name: schema, table_name: name }];
|
|
61
102
|
if (objectId != null) {
|
|
62
|
-
|
|
103
|
+
orClauses.push({ relation_id: objectId });
|
|
104
|
+
}
|
|
105
|
+
const candidateDocs = await col
|
|
106
|
+
.find({ group_id: this.replicationStreamId, connection_id, $or: orClauses }, { session })
|
|
107
|
+
.toArray();
|
|
108
|
+
|
|
109
|
+
const candidateTables = candidateDocs.map((doc) =>
|
|
110
|
+
this.hydrateSourceTable(doc, connectionTag, syncRules, sendsCompleteRows)
|
|
111
|
+
);
|
|
112
|
+
const candidates = candidateTables.map((table) => table.clone());
|
|
113
|
+
const resolution = await reconcile({ source, candidates });
|
|
114
|
+
storage.validateSourceTableCandidateResolution(candidates, resolution);
|
|
115
|
+
|
|
116
|
+
for (const { id, sourceMetadata } of storage.diffSourceTableUpdates(candidateTables, resolution)) {
|
|
117
|
+
await col.updateOne({ _id: mongoTableId(id) }, { $set: { source_metadata: sourceMetadata } }, { session });
|
|
63
118
|
}
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
120
|
+
const materializedResolution = storage.materializeSourceTableResolution(candidateTables, resolution);
|
|
121
|
+
const compatibleTables = materializedResolution.compatibleTables;
|
|
122
|
+
|
|
123
|
+
// V1 keeps one record, preferring one that has already been snapshotted.
|
|
124
|
+
const reuse = compatibleTables.find((table) => table.snapshotComplete) ?? compatibleTables[0] ?? null;
|
|
125
|
+
|
|
126
|
+
let sourceTable: storage.SourceTable;
|
|
127
|
+
if (reuse != null) {
|
|
128
|
+
sourceTable = reuse;
|
|
129
|
+
} else {
|
|
130
|
+
const doc: SourceTableDocumentV1 = {
|
|
68
131
|
_id: options.idGenerator ? (options.idGenerator() as bson.ObjectId) : new bson.ObjectId(),
|
|
69
|
-
group_id: this.
|
|
132
|
+
group_id: this.replicationStreamId,
|
|
70
133
|
connection_id,
|
|
71
134
|
relation_id: objectId,
|
|
72
135
|
schema_name: schema,
|
|
@@ -74,68 +137,18 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
74
137
|
replica_id_columns: null,
|
|
75
138
|
replica_id_columns2: normalizedReplicaIdColumns,
|
|
76
139
|
snapshot_done: false,
|
|
77
|
-
snapshot_status: undefined
|
|
140
|
+
snapshot_status: undefined,
|
|
141
|
+
source_metadata: resolution.newTableValues.sourceMetadata
|
|
78
142
|
};
|
|
79
143
|
await col.insertOne(doc, { session });
|
|
144
|
+
sourceTable = this.hydrateSourceTable(doc, connectionTag, syncRules, sendsCompleteRows);
|
|
80
145
|
}
|
|
81
146
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
snapshotComplete: doc.snapshot_done ?? true,
|
|
88
|
-
...syncRules.getMatchingSources(source)
|
|
89
|
-
});
|
|
90
|
-
sourceTable.syncEvent = syncRules.tableTriggersEvent(source);
|
|
91
|
-
sourceTable.syncData = sourceTable.bucketDataSources.length > 0;
|
|
92
|
-
sourceTable.syncParameters = sourceTable.parameterLookupSources.length > 0;
|
|
93
|
-
sourceTable.snapshotStatus =
|
|
94
|
-
doc.snapshot_status == null
|
|
95
|
-
? undefined
|
|
96
|
-
: {
|
|
97
|
-
lastKey: doc.snapshot_status.last_key?.buffer ?? null,
|
|
98
|
-
totalEstimatedCount: doc.snapshot_status.total_estimated_count,
|
|
99
|
-
replicatedCount: doc.snapshot_status.replicated_count
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const truncateFilter = [{ schema_name: schema, table_name: name }] as any[];
|
|
103
|
-
if (objectId != null) {
|
|
104
|
-
truncateFilter.push({ relation_id: objectId });
|
|
105
|
-
}
|
|
106
|
-
const truncate = await col
|
|
107
|
-
.find(
|
|
108
|
-
{
|
|
109
|
-
group_id: this.group_id,
|
|
110
|
-
connection_id,
|
|
111
|
-
_id: { $ne: doc._id },
|
|
112
|
-
$or: truncateFilter
|
|
113
|
-
},
|
|
114
|
-
{ session }
|
|
115
|
-
)
|
|
116
|
-
.toArray();
|
|
117
|
-
const dropTables = truncate.map((dropDoc) => {
|
|
118
|
-
const ref = {
|
|
119
|
-
connectionTag,
|
|
120
|
-
schema: dropDoc.schema_name,
|
|
121
|
-
name: dropDoc.table_name
|
|
122
|
-
};
|
|
123
|
-
const table = new storage.SourceTable({
|
|
124
|
-
id: dropDoc._id,
|
|
125
|
-
ref,
|
|
126
|
-
objectId: dropDoc.relation_id,
|
|
127
|
-
replicaIdColumns:
|
|
128
|
-
dropDoc.replica_id_columns2?.map(
|
|
129
|
-
(c) => ({ name: c.name, typeId: c.type_oid, type: c.type }) satisfies ColumnDescriptor
|
|
130
|
-
) ?? [],
|
|
131
|
-
snapshotComplete: dropDoc.snapshot_done ?? true,
|
|
132
|
-
...syncRules.getMatchingSources(ref)
|
|
133
|
-
});
|
|
134
|
-
table.syncEvent = syncRules.tableTriggersEvent(ref);
|
|
135
|
-
table.syncData = table.bucketDataSources.length > 0;
|
|
136
|
-
table.syncParameters = table.parameterLookupSources.length > 0;
|
|
137
|
-
return table;
|
|
138
|
-
});
|
|
147
|
+
const dropTables = [
|
|
148
|
+
...materializedResolution.incompatibleTables,
|
|
149
|
+
// V1 only keeps one SourceTable per physical table.
|
|
150
|
+
...compatibleTables.filter((table) => !storage.sourceTableIdEquals(table.id, sourceTable.id))
|
|
151
|
+
];
|
|
139
152
|
|
|
140
153
|
result = { tables: [sourceTable], dropTables };
|
|
141
154
|
});
|
|
@@ -143,38 +156,33 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
143
156
|
return result!;
|
|
144
157
|
}
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const ref = {
|
|
159
|
-
connectionTag: table.ref.connectionTag,
|
|
160
|
-
schema: doc.schema_name,
|
|
161
|
-
name: doc.table_name
|
|
162
|
-
};
|
|
163
|
-
const sourceTable = new storage.SourceTable({
|
|
159
|
+
/**
|
|
160
|
+
* Build a SourceTable from its persisted v1 record.
|
|
161
|
+
*/
|
|
162
|
+
private hydrateSourceTable(
|
|
163
|
+
doc: SourceTableDocumentV1,
|
|
164
|
+
connectionTag: string,
|
|
165
|
+
syncRules: HydratedSyncConfig,
|
|
166
|
+
sendsCompleteRows: boolean | undefined
|
|
167
|
+
): storage.SourceTable {
|
|
168
|
+
const ref = { connectionTag, schema: doc.schema_name, name: doc.table_name };
|
|
169
|
+
const table = new storage.SourceTable({
|
|
164
170
|
id: doc._id,
|
|
165
171
|
ref,
|
|
166
172
|
objectId: doc.relation_id,
|
|
167
173
|
replicaIdColumns:
|
|
168
174
|
doc.replica_id_columns2?.map(
|
|
169
|
-
(
|
|
175
|
+
(column) => ({ name: column.name, typeId: column.type_oid, type: column.type }) satisfies ColumnDescriptor
|
|
170
176
|
) ?? [],
|
|
171
177
|
snapshotComplete: doc.snapshot_done ?? true,
|
|
172
|
-
|
|
178
|
+
sourceMetadata: doc.source_metadata ?? null,
|
|
179
|
+
...syncRules.getMatchingSources(ref)
|
|
173
180
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
table.syncEvent = syncRules.tableTriggersEvent(ref);
|
|
182
|
+
table.syncData = table.bucketDataSources.length > 0;
|
|
183
|
+
table.syncParameters = table.parameterLookupSources.length > 0;
|
|
184
|
+
table.storeCurrentData = sendsCompleteRows !== true;
|
|
185
|
+
table.snapshotStatus =
|
|
178
186
|
doc.snapshot_status == null
|
|
179
187
|
? undefined
|
|
180
188
|
: {
|
|
@@ -182,7 +190,23 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
182
190
|
totalEstimatedCount: doc.snapshot_status.total_estimated_count,
|
|
183
191
|
replicatedCount: doc.snapshot_status.replicated_count
|
|
184
192
|
};
|
|
185
|
-
return
|
|
193
|
+
return table;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null> {
|
|
197
|
+
const doc = await this.db.sourceTablesV1(this.replicationStreamId).findOne(
|
|
198
|
+
{
|
|
199
|
+
group_id: this.replicationStreamId,
|
|
200
|
+
_id: mongoTableId(table.id)
|
|
201
|
+
},
|
|
202
|
+
{ session: this.session }
|
|
203
|
+
);
|
|
204
|
+
if (doc == null) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Use the default storeCurrentData value when reading outside resolution.
|
|
209
|
+
return this.hydrateSourceTable(doc, table.ref.connectionTag, this.sync_rules, undefined);
|
|
186
210
|
}
|
|
187
211
|
|
|
188
212
|
async commit(lsn: string, options?: storage.BucketBatchCommitOptions): Promise<storage.CheckpointResult> {
|
|
@@ -190,6 +214,8 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
190
214
|
|
|
191
215
|
await this.flush(options);
|
|
192
216
|
|
|
217
|
+
using _ = this.tracer.span('storage', 'commit');
|
|
218
|
+
|
|
193
219
|
const now = new Date();
|
|
194
220
|
|
|
195
221
|
await this.db.write_checkpoints.updateMany(
|
|
@@ -242,7 +268,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
242
268
|
};
|
|
243
269
|
|
|
244
270
|
const preUpdateDocument = (await this.db.sync_rules.findOneAndUpdate(
|
|
245
|
-
{ _id: this.
|
|
271
|
+
{ _id: this.replicationStreamId },
|
|
246
272
|
[
|
|
247
273
|
{
|
|
248
274
|
$set: {
|
|
@@ -295,7 +321,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
295
321
|
|
|
296
322
|
if (preUpdateDocument == null) {
|
|
297
323
|
throw new ReplicationAssertionError(
|
|
298
|
-
'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.
|
|
324
|
+
'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.replicationStreamId
|
|
299
325
|
);
|
|
300
326
|
}
|
|
301
327
|
|
|
@@ -329,7 +355,6 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
329
355
|
await this.autoActivate(lsn);
|
|
330
356
|
await this.db.notifyCheckpoint();
|
|
331
357
|
this.persisted_op = null;
|
|
332
|
-
this.last_checkpoint_lsn = lsn;
|
|
333
358
|
if (checkpointState.newLastCheckpoint != null) {
|
|
334
359
|
await this.sourceRecordStore.postCommitCleanup(checkpointState.newLastCheckpoint, this.logger);
|
|
335
360
|
}
|
|
@@ -346,23 +371,30 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
346
371
|
}
|
|
347
372
|
|
|
348
373
|
async setResumeLsn(lsn: string): Promise<void> {
|
|
374
|
+
using _ = this.tracer.span('storage', 'set_resume_lsn');
|
|
349
375
|
await this.db.sync_rules.updateOne(
|
|
350
376
|
{
|
|
351
|
-
_id: this.
|
|
377
|
+
_id: this.replicationStreamId
|
|
352
378
|
},
|
|
353
379
|
{
|
|
354
380
|
$set: {
|
|
355
381
|
snapshot_lsn: lsn
|
|
356
382
|
}
|
|
357
383
|
},
|
|
358
|
-
{
|
|
384
|
+
{
|
|
385
|
+
session: this.session,
|
|
386
|
+
// Losing occasional resume LSN is fine. That may mean reprocessing
|
|
387
|
+
// some source changes in some edge cases, which is not an issue since
|
|
388
|
+
// changes are processed in an idempotent way.
|
|
389
|
+
writeConcern: { w: 1 }
|
|
390
|
+
}
|
|
359
391
|
);
|
|
360
392
|
}
|
|
361
393
|
|
|
362
394
|
async markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void> {
|
|
363
395
|
await this.db.sync_rules.updateOne(
|
|
364
396
|
{
|
|
365
|
-
_id: this.
|
|
397
|
+
_id: this.replicationStreamId
|
|
366
398
|
},
|
|
367
399
|
{
|
|
368
400
|
$set: {
|
|
@@ -380,17 +412,24 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
380
412
|
async markSnapshotDone(no_checkpoint_before_lsn: string, options?: { throwOnConflict?: boolean }): Promise<void> {
|
|
381
413
|
await this.withTransaction(async () => {
|
|
382
414
|
// Protect against race conditions
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
415
|
+
const blockingTables = await this.db
|
|
416
|
+
.sourceTablesV1(this.replicationStreamId)
|
|
417
|
+
.find(
|
|
418
|
+
{
|
|
419
|
+
group_id: this.replicationStreamId,
|
|
420
|
+
snapshot_done: false
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
session: this.session,
|
|
424
|
+
projection: { schema_name: 1, table_name: 1 }
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
.toArray();
|
|
428
|
+
|
|
429
|
+
if (blockingTables.length > 0) {
|
|
391
430
|
if (options?.throwOnConflict ?? true) {
|
|
392
431
|
throw new ReplicationAssertionError(
|
|
393
|
-
`Cannot mark snapshot done while
|
|
432
|
+
`Cannot mark snapshot done while source tables still require snapshotting. Tables: ${blockingTables.map((t) => `${t.schema_name}.${t.table_name}`).join(', ')}`
|
|
394
433
|
);
|
|
395
434
|
} else {
|
|
396
435
|
return;
|
|
@@ -404,7 +443,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
404
443
|
async markTableSnapshotRequired(_table: storage.SourceTable): Promise<void> {
|
|
405
444
|
await this.db.sync_rules.updateOne(
|
|
406
445
|
{
|
|
407
|
-
_id: this.
|
|
446
|
+
_id: this.replicationStreamId
|
|
408
447
|
},
|
|
409
448
|
{
|
|
410
449
|
$set: {
|
|
@@ -423,7 +462,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
423
462
|
const ids = tables.map((table) => mongoTableId(table.id));
|
|
424
463
|
|
|
425
464
|
await this.withTransaction(async () => {
|
|
426
|
-
await this.db.
|
|
465
|
+
await this.db.sourceTablesV1(this.replicationStreamId).updateMany(
|
|
427
466
|
{ _id: { $in: ids } },
|
|
428
467
|
{
|
|
429
468
|
$set: {
|
|
@@ -439,7 +478,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
439
478
|
if (no_checkpoint_before_lsn != null) {
|
|
440
479
|
await this.db.sync_rules.updateOne(
|
|
441
480
|
{
|
|
442
|
-
_id: this.
|
|
481
|
+
_id: this.replicationStreamId
|
|
443
482
|
},
|
|
444
483
|
{
|
|
445
484
|
$set: {
|
|
@@ -467,12 +506,20 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
467
506
|
|
|
468
507
|
const session = this.session;
|
|
469
508
|
let activated = false;
|
|
509
|
+
let needsFutureActivationCheck = true;
|
|
470
510
|
await session.withTransaction(async () => {
|
|
471
|
-
|
|
511
|
+
// Reset on transaction retries.
|
|
512
|
+
activated = false;
|
|
513
|
+
needsFutureActivationCheck = true;
|
|
514
|
+
|
|
515
|
+
const doc = (await this.db.sync_rules.findOne(
|
|
516
|
+
{ _id: this.replicationStreamId },
|
|
517
|
+
{ session }
|
|
518
|
+
)) as SyncRuleDocumentV1;
|
|
472
519
|
if (doc && doc.state == storage.SyncRuleState.PROCESSING && doc.snapshot_done && doc.last_checkpoint != null) {
|
|
473
520
|
await this.db.sync_rules.updateOne(
|
|
474
521
|
{
|
|
475
|
-
_id: this.
|
|
522
|
+
_id: this.replicationStreamId
|
|
476
523
|
},
|
|
477
524
|
{
|
|
478
525
|
$set: {
|
|
@@ -484,7 +531,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
484
531
|
|
|
485
532
|
await this.db.sync_rules.updateMany(
|
|
486
533
|
{
|
|
487
|
-
_id: { $ne: this.
|
|
534
|
+
_id: { $ne: this.replicationStreamId },
|
|
488
535
|
state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
|
|
489
536
|
},
|
|
490
537
|
{
|
|
@@ -496,13 +543,15 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
496
543
|
);
|
|
497
544
|
activated = true;
|
|
498
545
|
} else if (doc?.state != storage.SyncRuleState.PROCESSING) {
|
|
499
|
-
|
|
546
|
+
needsFutureActivationCheck = false;
|
|
500
547
|
}
|
|
501
548
|
});
|
|
502
549
|
if (activated) {
|
|
503
550
|
this.logger.info(`Activated new replication stream at ${lsn}`);
|
|
504
551
|
await this.db.notifyCheckpoint();
|
|
505
552
|
this.needsActivation = false;
|
|
553
|
+
} else if (!needsFutureActivationCheck) {
|
|
554
|
+
this.needsActivation = false;
|
|
506
555
|
}
|
|
507
556
|
}
|
|
508
557
|
}
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
1
2
|
import {
|
|
3
|
+
addPartialChecksums,
|
|
2
4
|
bson,
|
|
3
5
|
BucketChecksum,
|
|
4
6
|
FetchPartialBucketChecksum,
|
|
5
7
|
InternalOpId,
|
|
6
|
-
|
|
8
|
+
isPartialChecksum,
|
|
9
|
+
PartialChecksumMap,
|
|
10
|
+
PartialOrFullChecksum
|
|
7
11
|
} from '@powersync/service-core';
|
|
8
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
checksumFromAggregate,
|
|
14
|
+
DEFAULT_OPERATION_BATCH_LIMIT,
|
|
15
|
+
FetchPartialBucketChecksumByBucket,
|
|
16
|
+
MongoChecksums,
|
|
17
|
+
MongoChecksumSessionContext
|
|
18
|
+
} from '../MongoChecksums.js';
|
|
9
19
|
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
10
|
-
|
|
11
20
|
export class MongoChecksumsV1 extends MongoChecksums {
|
|
12
21
|
declare protected readonly db: VersionedPowerSyncMongoV1;
|
|
13
22
|
|
|
14
23
|
async computePartialChecksumsDirectByBucket(
|
|
15
|
-
batch: FetchPartialBucketChecksumByBucket[]
|
|
24
|
+
batch: FetchPartialBucketChecksumByBucket[],
|
|
25
|
+
context: MongoChecksumSessionContext
|
|
16
26
|
): Promise<PartialChecksumMap> {
|
|
17
|
-
|
|
27
|
+
const collection = this.db.bucketDataV1;
|
|
28
|
+
const createFilter = (request: FetchPartialBucketChecksumByBucket) => ({
|
|
18
29
|
_id: {
|
|
19
30
|
$gt: {
|
|
20
31
|
g: this.group_id,
|
|
@@ -27,11 +38,134 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
27
38
|
o: request.end
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
|
-
})
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const batchLimit = this.options?.operationBatchLimit ?? DEFAULT_OPERATION_BATCH_LIMIT;
|
|
44
|
+
|
|
45
|
+
// Map requests by bucket. We adjust this as we get partial results.
|
|
46
|
+
let requests = new Map<string, FetchPartialBucketChecksumByBucket>();
|
|
47
|
+
for (let request of batch) {
|
|
48
|
+
requests.set(request.bucket, request);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const partialChecksums = new Map<string, PartialOrFullChecksum>();
|
|
52
|
+
|
|
53
|
+
while (requests.size > 0) {
|
|
54
|
+
const filters = Array.from(requests.values(), createFilter);
|
|
55
|
+
|
|
56
|
+
// Historically, checksum may be stored as 'int' or 'double'.
|
|
57
|
+
// More recently, this should be a 'long'.
|
|
58
|
+
// $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
|
|
59
|
+
const checksumLong = this.storageConfig.longChecksums ? '$checksum' : { $toLong: '$checksum' };
|
|
60
|
+
|
|
61
|
+
// Aggregate over a max of `batchLimit` operations at a time.
|
|
62
|
+
// Let's say we have 3 buckets (A, B, C), each with 10 operations, and our batch limit is 12.
|
|
63
|
+
// Then we'll do three batches:
|
|
64
|
+
// 1. Query: A[1-end], B[1-end], C[1-end]
|
|
65
|
+
// Returns: A[1-10], B[1-2]
|
|
66
|
+
// 2. Query: B[3-end], C[1-end]
|
|
67
|
+
// Returns: B[3-10], C[1-4]
|
|
68
|
+
// 3. Query: C[5-end]
|
|
69
|
+
// Returns: C[5-10]
|
|
70
|
+
const aggregate = await collection
|
|
71
|
+
.aggregate(
|
|
72
|
+
[
|
|
73
|
+
{
|
|
74
|
+
$match: {
|
|
75
|
+
$or: filters
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
// sort and limit _before_ grouping
|
|
79
|
+
{ $sort: { _id: 1 } },
|
|
80
|
+
{ $limit: batchLimit },
|
|
81
|
+
{
|
|
82
|
+
$group: {
|
|
83
|
+
_id: '$_id.b',
|
|
84
|
+
checksum_total: { $sum: checksumLong },
|
|
85
|
+
count: { $sum: 1 },
|
|
86
|
+
has_clear_op: {
|
|
87
|
+
$max: {
|
|
88
|
+
$cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
last_op: { $max: '$_id.o' }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
// Sort the aggregated results (100 max, so should be fast).
|
|
95
|
+
// This is important to identify which buckets we have partial data for.
|
|
96
|
+
{ $sort: { _id: 1 } }
|
|
97
|
+
],
|
|
98
|
+
{
|
|
99
|
+
...context.readOptions,
|
|
100
|
+
maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
.toArray()
|
|
104
|
+
.catch((e) => {
|
|
105
|
+
throw lib_mongo.mapQueryError(e, 'while reading checksums');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
let batchCount = 0;
|
|
109
|
+
let limitReached = false;
|
|
110
|
+
for (let doc of aggregate) {
|
|
111
|
+
const bucket = doc._id;
|
|
112
|
+
const checksum = checksumFromAggregate(doc);
|
|
113
|
+
|
|
114
|
+
const existing = partialChecksums.get(bucket);
|
|
115
|
+
if (existing != null) {
|
|
116
|
+
partialChecksums.set(bucket, addPartialChecksums(bucket, existing, checksum));
|
|
117
|
+
} else {
|
|
118
|
+
partialChecksums.set(bucket, checksum);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
batchCount += doc.count;
|
|
122
|
+
if (batchCount == batchLimit) {
|
|
123
|
+
// Limit reached. Request more in the next batch.
|
|
124
|
+
// Note that this only affects the _last_ bucket in a batch.
|
|
125
|
+
limitReached = true;
|
|
126
|
+
const req = requests.get(bucket);
|
|
127
|
+
requests.set(bucket, {
|
|
128
|
+
...req!,
|
|
129
|
+
start: doc.last_op
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
// All done for this bucket
|
|
133
|
+
requests.delete(bucket);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!limitReached) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return new Map<string, PartialOrFullChecksum>(
|
|
142
|
+
batch.map((request) => {
|
|
143
|
+
const bucket = request.bucket;
|
|
144
|
+
let partialChecksum = partialChecksums.get(bucket);
|
|
145
|
+
// Could be null if we got no data
|
|
146
|
+
if (partialChecksum == null) {
|
|
147
|
+
partialChecksum = {
|
|
148
|
+
bucket,
|
|
149
|
+
partialCount: 0,
|
|
150
|
+
partialChecksum: 0
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (request.start == null && isPartialChecksum(partialChecksum)) {
|
|
154
|
+
partialChecksum = {
|
|
155
|
+
bucket,
|
|
156
|
+
count: partialChecksum.partialCount,
|
|
157
|
+
checksum: partialChecksum.partialChecksum
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return [bucket, partialChecksum];
|
|
162
|
+
})
|
|
163
|
+
);
|
|
31
164
|
}
|
|
32
165
|
|
|
33
166
|
protected async fetchPreStates(
|
|
34
|
-
batch: FetchPartialBucketChecksum[]
|
|
167
|
+
batch: FetchPartialBucketChecksum[],
|
|
168
|
+
context: MongoChecksumSessionContext
|
|
35
169
|
): Promise<Map<string, { opId: InternalOpId; checksum: BucketChecksum }>> {
|
|
36
170
|
const preFilters = batch
|
|
37
171
|
.filter((request) => request.start == null)
|
|
@@ -49,9 +183,14 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
49
183
|
}
|
|
50
184
|
|
|
51
185
|
const states = await this.db.bucketStateV1
|
|
52
|
-
.find(
|
|
53
|
-
|
|
54
|
-
|
|
186
|
+
.find(
|
|
187
|
+
{
|
|
188
|
+
$or: preFilters
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
...context.readOptions
|
|
192
|
+
}
|
|
193
|
+
)
|
|
55
194
|
.toArray();
|
|
56
195
|
|
|
57
196
|
for (const state of states) {
|
|
@@ -69,7 +208,10 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
69
208
|
return preStates;
|
|
70
209
|
}
|
|
71
210
|
|
|
72
|
-
protected async computePartialChecksumsInternal(
|
|
73
|
-
|
|
211
|
+
protected async computePartialChecksumsInternal(
|
|
212
|
+
batch: FetchPartialBucketChecksum[],
|
|
213
|
+
context: MongoChecksumSessionContext
|
|
214
|
+
): Promise<PartialChecksumMap> {
|
|
215
|
+
return this.computePartialChecksumsDirectByBucket(batch, context);
|
|
74
216
|
}
|
|
75
217
|
}
|