@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
|
@@ -3,12 +3,14 @@ import { mongo } from '@powersync/lib-service-mongodb';
|
|
|
3
3
|
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
4
4
|
import {
|
|
5
5
|
CheckpointChanges,
|
|
6
|
+
CompactInitialReplicationOptions,
|
|
7
|
+
CompactInitialReplicationResults,
|
|
6
8
|
GetCheckpointChangesOptions,
|
|
7
9
|
InternalOpId,
|
|
8
10
|
internalToExternalOpId,
|
|
9
|
-
maxLsn,
|
|
10
11
|
ParameterSetLimitExceededError,
|
|
11
12
|
ProtocolOpId,
|
|
13
|
+
SingleSyncConfigBucketDefinitionMapping,
|
|
12
14
|
storage,
|
|
13
15
|
utils
|
|
14
16
|
} from '@powersync/service-core';
|
|
@@ -17,57 +19,125 @@ import { ParameterLookupRows, ScopedParameterLookup, SqliteJsonRow } from '@powe
|
|
|
17
19
|
import * as bson from 'bson';
|
|
18
20
|
import { mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../../utils/util.js';
|
|
19
21
|
import { MongoBucketStorage } from '../../MongoBucketStorage.js';
|
|
22
|
+
import { BucketDataDoc } from '../common/BucketDataDoc.js';
|
|
20
23
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from '../common/
|
|
24
|
-
import { MongoBucketBatchOptions } from '../MongoBucketBatch.js';
|
|
24
|
+
MongoGetCheckpointChangesOptions,
|
|
25
|
+
MongoSyncBucketStorageCheckpoint
|
|
26
|
+
} from '../common/MongoSyncBucketStorageCheckpoint.js';
|
|
25
27
|
import { MongoChecksums } from '../MongoChecksums.js';
|
|
26
28
|
import { MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
|
|
27
29
|
import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
|
|
30
|
+
import { MongoPersistedReplicationStream } from '../MongoPersistedReplicationStream.js';
|
|
28
31
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
MongoCheckpointState,
|
|
33
|
+
MongoSyncBucketStorage,
|
|
34
|
+
MongoSyncBucketStorageOptions
|
|
35
|
+
} from '../MongoSyncBucketStorage.js';
|
|
36
|
+
import { MongoCheckpointAPIOptions } from '../MongoWriteCheckpointAPI.js';
|
|
37
|
+
import { loadBucketDataDocument, maxOpId } from './bucket-format.js';
|
|
33
38
|
import {
|
|
34
39
|
BucketDataDocumentV3,
|
|
35
40
|
BucketParameterDocumentV3,
|
|
36
|
-
|
|
41
|
+
deserializeParameterLookup,
|
|
37
42
|
ReplicationStreamDocumentV3,
|
|
43
|
+
serializeParameterLookup,
|
|
38
44
|
SyncRuleConfigStateV3
|
|
39
45
|
} from './models.js';
|
|
40
46
|
import { MongoBucketBatchV3 } from './MongoBucketBatchV3.js';
|
|
41
47
|
import { MongoChecksumsV3 } from './MongoChecksumsV3.js';
|
|
42
48
|
import { MongoCompactorV3 } from './MongoCompactorV3.js';
|
|
43
49
|
import { MongoParameterCompactorV3 } from './MongoParameterCompactorV3.js';
|
|
44
|
-
import {
|
|
50
|
+
import { MongoStoppedSyncConfigCleanup } from './MongoStoppedSyncConfigCleanup.js';
|
|
51
|
+
import { MongoWriteCheckpointAPIV3 } from './MongoWriteCheckpointAPIV3.js';
|
|
52
|
+
import { hydrateBucketDataDocuments } from './object-storage/BucketDataObjectStorage.js';
|
|
53
|
+
import { ObjectStorage } from './object-storage/ObjectStorage.js';
|
|
54
|
+
import { ObjectStorageLifecycle } from './object-storage/ObjectStorageLifecycle.js';
|
|
55
|
+
import { ObjectStorageUsage } from './object-storage/ObjectStorageUsage.js';
|
|
45
56
|
import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
|
|
46
57
|
|
|
58
|
+
export interface MongoSyncBucketStorageContextV3 {
|
|
59
|
+
db: VersionedPowerSyncMongoV3;
|
|
60
|
+
replicationStreamId: number;
|
|
61
|
+
readPreference?: mongo.ReadPreference;
|
|
62
|
+
objectStorage: ObjectStorage | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Persisted mapping of the single sync config that read operations are served from.
|
|
65
|
+
*
|
|
66
|
+
* Implemented as a lazy getter: accessing it on a storage instance with multiple sync
|
|
67
|
+
* configs throws, but operations that don't use it remain unaffected.
|
|
68
|
+
*/
|
|
69
|
+
readonly mapping: SingleSyncConfigBucketDefinitionMapping;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const BUCKET_DATA_FETCH_BATCH_LIMIT_BYTES = 16 * 1024 * 1024;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Keep the documents hydrated for one sync response within a bounded payload or slightly higher.
|
|
76
|
+
*
|
|
77
|
+
* This deserializes on-demand, so no deserialization is performed for discarded data.
|
|
78
|
+
*/
|
|
79
|
+
function cutBucketDataBatch(rawDocuments: Buffer[]): {
|
|
80
|
+
documents: BucketDataDocumentV3[];
|
|
81
|
+
wasCut: boolean;
|
|
82
|
+
} {
|
|
83
|
+
let cumulativeBytes = 0;
|
|
84
|
+
let documents: BucketDataDocumentV3[] = [];
|
|
85
|
+
for (const raw of rawDocuments) {
|
|
86
|
+
const doc = bson.deserialize(raw, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV3;
|
|
87
|
+
documents.push(doc);
|
|
88
|
+
cumulativeBytes += doc.size;
|
|
89
|
+
if (cumulativeBytes > BUCKET_DATA_FETCH_BATCH_LIMIT_BYTES) {
|
|
90
|
+
return {
|
|
91
|
+
documents,
|
|
92
|
+
wasCut: documents.length < rawDocuments.length
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { documents, wasCut: false };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function extractRowsFromDocument(
|
|
100
|
+
doc: BucketDataDocumentV3,
|
|
101
|
+
context: { replicationStreamId: number; definitionId: string },
|
|
102
|
+
bucketStart: InternalOpId,
|
|
103
|
+
endOpId: InternalOpId
|
|
104
|
+
): BucketDataDoc[] {
|
|
105
|
+
const rows: BucketDataDoc[] = [];
|
|
106
|
+
for (const row of loadBucketDataDocument(context, doc)) {
|
|
107
|
+
// In theory a binary search could be faster than a linear scan to find the start.
|
|
108
|
+
// In practice, most cases should not filter out anything here.
|
|
109
|
+
if (row.o <= bucketStart) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (row.o > endOpId) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
rows.push(row);
|
|
117
|
+
}
|
|
118
|
+
return rows;
|
|
119
|
+
}
|
|
120
|
+
|
|
47
121
|
export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
48
|
-
// Declare types to be more specific
|
|
49
122
|
declare readonly db: VersionedPowerSyncMongoV3;
|
|
50
123
|
declare readonly checksums: MongoChecksumsV3;
|
|
51
124
|
|
|
52
|
-
private readonly syncRulesV3: MongoPersistedSyncRulesContentV3;
|
|
53
|
-
|
|
54
125
|
constructor(
|
|
55
126
|
factory: MongoBucketStorage,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
127
|
+
replicationStreamId: number,
|
|
128
|
+
replicationStream: MongoPersistedReplicationStream,
|
|
129
|
+
replicationStreamName: string,
|
|
59
130
|
writeCheckpointMode: storage.WriteCheckpointMode | undefined,
|
|
60
131
|
options: MongoSyncBucketStorageOptions
|
|
61
132
|
) {
|
|
62
|
-
super(factory,
|
|
63
|
-
if (
|
|
133
|
+
super(factory, replicationStreamId, replicationStream, replicationStreamName, writeCheckpointMode, options);
|
|
134
|
+
if (replicationStream.syncConfigIds.length == 0) {
|
|
64
135
|
throw new ServiceAssertionError('Missing sync config id for storage v3');
|
|
65
136
|
}
|
|
66
|
-
this.syncRulesV3 = sync_rules;
|
|
67
137
|
}
|
|
68
138
|
|
|
69
|
-
private get
|
|
70
|
-
return this.
|
|
139
|
+
private get syncConfigIds(): bson.ObjectId[] {
|
|
140
|
+
return this.replicationStream.syncConfigIds;
|
|
71
141
|
}
|
|
72
142
|
|
|
73
143
|
private get syncRulesCollection(): mongo.Collection<ReplicationStreamDocumentV3> {
|
|
@@ -76,10 +146,10 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
76
146
|
|
|
77
147
|
private syncConfigMatch(extra: mongo.Document = {}): mongo.Filter<ReplicationStreamDocumentV3> {
|
|
78
148
|
return {
|
|
79
|
-
_id: this.
|
|
149
|
+
_id: this.replicationStreamId,
|
|
80
150
|
sync_configs: {
|
|
81
151
|
$elemMatch: {
|
|
82
|
-
_id: this.
|
|
152
|
+
_id: { $in: this.syncConfigIds },
|
|
83
153
|
...extra
|
|
84
154
|
}
|
|
85
155
|
}
|
|
@@ -89,44 +159,27 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
89
159
|
private syncConfigProjection(extra: mongo.Document = {}): mongo.Document {
|
|
90
160
|
return {
|
|
91
161
|
...extra,
|
|
92
|
-
sync_configs:
|
|
93
|
-
$elemMatch: {
|
|
94
|
-
_id: this.syncConfigId
|
|
95
|
-
}
|
|
96
|
-
}
|
|
162
|
+
sync_configs: 1
|
|
97
163
|
};
|
|
98
164
|
}
|
|
99
165
|
|
|
100
|
-
private
|
|
101
|
-
return
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* For now, we only support a single sync config per replication stream.
|
|
106
|
-
*
|
|
107
|
-
* In the future we'll add support for multiple.
|
|
108
|
-
*/
|
|
109
|
-
private selectedSyncConfig(
|
|
110
|
-
doc: Pick<ReplicationStreamDocumentV3, 'sync_configs'> | null
|
|
111
|
-
): SyncRuleConfigStateV3 | null {
|
|
112
|
-
return doc?.sync_configs?.[0] ?? null;
|
|
166
|
+
private selectedSyncConfigs(doc: Pick<ReplicationStreamDocumentV3, 'sync_configs'> | null): SyncRuleConfigStateV3[] {
|
|
167
|
+
return doc?.sync_configs?.filter((config) => this.syncConfigIds.some((id) => id.equals(config._id))) ?? [];
|
|
113
168
|
}
|
|
114
169
|
|
|
115
170
|
protected async initializeVersionStorage(): Promise<void> {
|
|
116
|
-
const
|
|
117
|
-
for (
|
|
118
|
-
const collection = this.db.
|
|
119
|
-
await this.db.db
|
|
120
|
-
.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
throw error;
|
|
126
|
-
});
|
|
171
|
+
const storageIds = this.storageIds;
|
|
172
|
+
for (const source of storageIds.bucketDefinitionIds) {
|
|
173
|
+
const collection = this.db.bucketData(this.replicationStreamId, source).collectionName;
|
|
174
|
+
await this.db.db.createCollection(collection, {}).catch((error) => {
|
|
175
|
+
if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceExists') {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
});
|
|
127
180
|
}
|
|
128
|
-
for (
|
|
129
|
-
await this.db.
|
|
181
|
+
for (const indexId of storageIds.parameterIndexIds) {
|
|
182
|
+
await this.db.parameterIndex(this.replicationStreamId, indexId).createIndex(
|
|
130
183
|
{
|
|
131
184
|
lookup: 1,
|
|
132
185
|
key: 1,
|
|
@@ -140,10 +193,19 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
140
193
|
}
|
|
141
194
|
|
|
142
195
|
protected createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums {
|
|
143
|
-
return new MongoChecksumsV3(this.db, this.
|
|
196
|
+
return new MongoChecksumsV3(this.db, this.replicationStreamId, {
|
|
144
197
|
...options.checksumOptions,
|
|
198
|
+
checksumCacheTtlMs: options.checksumCacheTtlMs,
|
|
145
199
|
storageConfig: options?.storageConfig,
|
|
146
|
-
|
|
200
|
+
syncConfigMapping: () => this.singleSyncConfigMapping()
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
protected override createWriteCheckpointAPI(options: MongoCheckpointAPIOptions): MongoWriteCheckpointAPIV3 {
|
|
205
|
+
return new MongoWriteCheckpointAPIV3({
|
|
206
|
+
...options,
|
|
207
|
+
db: this.db,
|
|
208
|
+
syncConfigMapping: () => this.singleSyncConfigMapping()
|
|
147
209
|
});
|
|
148
210
|
}
|
|
149
211
|
|
|
@@ -151,57 +213,93 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
151
213
|
return new MongoCompactorV3(this, this.db, options);
|
|
152
214
|
}
|
|
153
215
|
|
|
216
|
+
override async compactInitialReplication(
|
|
217
|
+
options: CompactInitialReplicationOptions
|
|
218
|
+
): Promise<CompactInitialReplicationResults> {
|
|
219
|
+
this.logger.info(`Compacting chunks after initial replication...`);
|
|
220
|
+
const start = Date.now();
|
|
221
|
+
const maxOpId = options.maxOpId ?? (await this.fetchPersistedOpHead()) ?? undefined;
|
|
222
|
+
const compactedBuckets = await this.createMongoCompactor({
|
|
223
|
+
...options,
|
|
224
|
+
maxOpId,
|
|
225
|
+
compactChunksOnly: true,
|
|
226
|
+
logger: this.logger
|
|
227
|
+
}).compact();
|
|
228
|
+
this.logger.info(`Compacted chunks after initial replication in ${(Date.now() - start) / 1000}s`);
|
|
229
|
+
return { buckets: compactedBuckets };
|
|
230
|
+
}
|
|
231
|
+
|
|
154
232
|
protected createMongoParameterCompactor(
|
|
155
233
|
checkpoint: InternalOpId,
|
|
156
234
|
options: storage.CompactOptions
|
|
157
235
|
): MongoParameterCompactor {
|
|
158
|
-
return new MongoParameterCompactorV3(this.db, this.
|
|
236
|
+
return new MongoParameterCompactorV3(this.db, this.replicationStreamId, checkpoint, options);
|
|
159
237
|
}
|
|
160
238
|
|
|
161
|
-
protected
|
|
162
|
-
|
|
239
|
+
protected async fetchPersistedOpHead(): Promise<InternalOpId | null> {
|
|
240
|
+
const doc = await this.syncRulesCollection.findOne(
|
|
241
|
+
{ _id: this.replicationStreamId },
|
|
242
|
+
{ projection: { last_persisted_op: 1 } }
|
|
243
|
+
);
|
|
244
|
+
return doc?.last_persisted_op == null ? null : BigInt(doc.last_persisted_op);
|
|
163
245
|
}
|
|
164
246
|
|
|
165
|
-
protected async
|
|
166
|
-
|
|
167
|
-
|
|
247
|
+
protected async createWriterImpl(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch> {
|
|
248
|
+
const batchOptions = this.writerBatchOptions(options);
|
|
249
|
+
const doc = await this.syncRulesCollection.findOne(
|
|
250
|
+
{ _id: this.replicationStreamId },
|
|
251
|
+
{ projection: { resume_lsn: 1 } }
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return new MongoBucketBatchV3({
|
|
255
|
+
...batchOptions,
|
|
256
|
+
// The stream-level replication position - per-config checkpoint LSNs are consistency
|
|
257
|
+
// markers and do not affect where replication resumes.
|
|
258
|
+
resumeFromLsn: doc?.resume_lsn ?? null,
|
|
259
|
+
syncConfigIds: this.syncConfigIds
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
protected async fetchCheckpointState(session: mongo.ClientSession): Promise<MongoCheckpointState | null> {
|
|
168
264
|
const doc = await this.syncRulesCollection.findOne(
|
|
169
265
|
this.syncConfigMatch({
|
|
170
266
|
state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
|
|
171
267
|
}),
|
|
172
268
|
{
|
|
173
269
|
session,
|
|
174
|
-
|
|
270
|
+
// The invalidation fence must be read in the same snapshot as the checkpoint.
|
|
271
|
+
projection: this.syncConfigProjection({ 'parameter_compaction.checkpoint_changes_invalid_before': 1 })
|
|
175
272
|
}
|
|
176
273
|
);
|
|
177
|
-
|
|
178
|
-
|
|
274
|
+
// Checkpoints are served from the single active config. A PROCESSING config in the same
|
|
275
|
+
// stream (incremental reprocessing) does not affect checkpoints until it is activated.
|
|
276
|
+
const syncConfigs = this.selectedSyncConfigs(doc).filter((config) =>
|
|
277
|
+
[storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED].includes(config.state)
|
|
278
|
+
);
|
|
279
|
+
if (syncConfigs.length > 1) {
|
|
280
|
+
// Activation atomically replaces the previous active config, so this cannot happen unless
|
|
281
|
+
// the stored state is corrupt.
|
|
282
|
+
throw new ServiceAssertionError(
|
|
283
|
+
`Expected a single active sync config, got ${syncConfigs.map((config) => config._id.toHexString()).join(', ')}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const syncConfig = syncConfigs[0];
|
|
287
|
+
if (syncConfig == null || !syncConfig.snapshot_done) {
|
|
179
288
|
return null;
|
|
180
289
|
}
|
|
181
290
|
return {
|
|
182
291
|
checkpoint: syncConfig.last_checkpoint ?? 0n,
|
|
183
|
-
lsn: syncConfig.last_checkpoint_lsn ?? null
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
protected async getWriterSyncState() {
|
|
188
|
-
const doc = await this.syncRulesCollection.findOne(this.syncConfigMatch(), {
|
|
189
|
-
projection: this.syncConfigProjection({ snapshot_lsn: 1 })
|
|
190
|
-
});
|
|
191
|
-
const syncConfig = this.selectedSyncConfig(doc);
|
|
192
|
-
const checkpointLsn = syncConfig?.last_checkpoint_lsn ?? null;
|
|
193
|
-
return {
|
|
194
|
-
lastCheckpointLsn: checkpointLsn,
|
|
195
|
-
resumeFromLsn: maxLsn(checkpointLsn, doc?.snapshot_lsn),
|
|
196
|
-
keepaliveOp: syncConfig?.keepalive_op ?? null,
|
|
197
|
-
syncConfigId: this.syncConfigId
|
|
292
|
+
lsn: syncConfig.last_checkpoint_lsn ?? null,
|
|
293
|
+
// Stream-level state: shared by all sync configs. Defaults to 0n for streams that have
|
|
294
|
+
// never been compacted.
|
|
295
|
+
parameterChangesInvalidBefore: doc?.parameter_compaction?.checkpoint_changes_invalid_before ?? 0n
|
|
198
296
|
};
|
|
199
297
|
}
|
|
200
298
|
|
|
201
299
|
protected async terminateSyncRuleState(): Promise<void> {
|
|
202
300
|
await this.db.sync_rules.updateOne(
|
|
203
301
|
{
|
|
204
|
-
_id: this.
|
|
302
|
+
_id: this.replicationStreamId
|
|
205
303
|
},
|
|
206
304
|
{
|
|
207
305
|
$set: {
|
|
@@ -213,52 +311,83 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
213
311
|
);
|
|
214
312
|
}
|
|
215
313
|
|
|
216
|
-
protected async getStatusImpl(): Promise<storage.
|
|
314
|
+
protected async getStatusImpl(): Promise<storage.ReplicationStreamStatus> {
|
|
217
315
|
const doc = await this.syncRulesCollection.findOne(this.syncConfigMatch(), {
|
|
218
|
-
projection: this.syncConfigProjection({
|
|
316
|
+
projection: this.syncConfigProjection({ resume_lsn: 1 })
|
|
219
317
|
});
|
|
220
|
-
const
|
|
221
|
-
if (doc == null ||
|
|
318
|
+
const syncConfigs = this.selectedSyncConfigs(doc);
|
|
319
|
+
if (doc == null || syncConfigs.length == 0) {
|
|
222
320
|
throw new ServiceAssertionError('Cannot find replication stream status');
|
|
223
321
|
}
|
|
224
322
|
|
|
225
323
|
return {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
keepalive_op: syncConfig.keepalive_op ?? null
|
|
324
|
+
snapshotDone:
|
|
325
|
+
syncConfigs.every((config) => config.snapshot_done ?? false) &&
|
|
326
|
+
syncConfigs.every((config) => config.last_checkpoint_lsn != null),
|
|
327
|
+
resumeLsn: doc.resume_lsn ?? null
|
|
231
328
|
};
|
|
232
329
|
}
|
|
233
330
|
|
|
234
331
|
protected async clearSyncRuleState(): Promise<void> {
|
|
332
|
+
// Clearing resets the entire replication stream (bucket data and the op sequence), so reset
|
|
333
|
+
// the checkpoint state for _all_ embedded sync configs, not only the ones selected for this
|
|
334
|
+
// storage instance. This maintains the invariant that no config has a last_checkpoint past
|
|
335
|
+
// the stream-level last_persisted_op.
|
|
235
336
|
await this.syncRulesCollection.updateOne(
|
|
236
|
-
this.
|
|
337
|
+
{ _id: this.replicationStreamId },
|
|
237
338
|
{
|
|
238
339
|
$set: {
|
|
239
340
|
persisted_lsn: null,
|
|
240
|
-
'sync_configs.$[
|
|
241
|
-
'sync_configs.$[
|
|
242
|
-
'sync_configs.$[
|
|
243
|
-
'sync_configs.$[
|
|
244
|
-
'sync_configs.$[config].keepalive_op': null
|
|
341
|
+
'sync_configs.$[].snapshot_done': false,
|
|
342
|
+
'sync_configs.$[].last_checkpoint_lsn': null,
|
|
343
|
+
'sync_configs.$[].last_checkpoint': null,
|
|
344
|
+
'sync_configs.$[].no_checkpoint_before': null
|
|
245
345
|
},
|
|
246
346
|
$unset: {
|
|
247
|
-
|
|
347
|
+
resume_lsn: 1,
|
|
348
|
+
last_persisted_op: 1,
|
|
349
|
+
parameter_compaction: 1
|
|
248
350
|
}
|
|
249
351
|
},
|
|
250
352
|
{
|
|
251
|
-
maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS
|
|
252
|
-
arrayFilters: this.syncConfigArrayFilters()
|
|
353
|
+
maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS
|
|
253
354
|
}
|
|
254
355
|
);
|
|
255
356
|
}
|
|
256
357
|
|
|
257
|
-
|
|
358
|
+
/**
|
|
359
|
+
* The persisted mapping of the single sync config that read operations are served from.
|
|
360
|
+
*
|
|
361
|
+
* Reads always operate on a single sync config: read-path storage instances are
|
|
362
|
+
* constructed for the active sync config only (see MongoBucketStorage.getActiveSyncConfig),
|
|
363
|
+
* and checkpoints are served from the single active config (see fetchCheckpointState).
|
|
364
|
+
* Within a single sync config, unique names are the persistence key of its rule mapping,
|
|
365
|
+
* so its name-keyed {@link SingleSyncConfigBucketDefinitionMapping} resolves sources from
|
|
366
|
+
* any parse of that config unambiguously - no parsed-set identity is required.
|
|
367
|
+
*
|
|
368
|
+
* Throws on storage instances with multiple sync configs (replication-side instances),
|
|
369
|
+
* which must not serve reads.
|
|
370
|
+
*/
|
|
371
|
+
private singleSyncConfigMapping(): SingleSyncConfigBucketDefinitionMapping {
|
|
372
|
+
const content = this.replicationStream.syncConfigContent;
|
|
373
|
+
if (content.length != 1) {
|
|
374
|
+
throw new ServiceAssertionError(
|
|
375
|
+
`Read operations require a storage instance with a single sync config, got ${content.length}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
return content[0].mapping;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
protected get versionContext(): MongoSyncBucketStorageContextV3 {
|
|
382
|
+
const self = this;
|
|
258
383
|
return {
|
|
259
384
|
db: this.db,
|
|
260
|
-
|
|
261
|
-
|
|
385
|
+
objectStorage: this.objectStorage,
|
|
386
|
+
replicationStreamId: this.replicationStreamId,
|
|
387
|
+
readPreference: this.readPreference,
|
|
388
|
+
get mapping() {
|
|
389
|
+
return self.singleSyncConfigMapping();
|
|
390
|
+
}
|
|
262
391
|
};
|
|
263
392
|
}
|
|
264
393
|
|
|
@@ -271,37 +400,52 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
271
400
|
}
|
|
272
401
|
|
|
273
402
|
protected getBucketDataBatchImpl(
|
|
274
|
-
checkpoint:
|
|
403
|
+
checkpoint: MongoSyncBucketStorageCheckpoint,
|
|
275
404
|
dataBuckets: storage.BucketDataRequest[],
|
|
276
405
|
options?: storage.BucketDataBatchOptions
|
|
277
|
-
): AsyncIterable<storage.SyncBucketDataChunk> {
|
|
406
|
+
): AsyncIterable<storage.SyncBucketDataChunk | storage.SyncBucketDataBatchEnd> {
|
|
278
407
|
return getBucketDataBatchV3(this.versionContext, checkpoint, dataBuckets, options);
|
|
279
408
|
}
|
|
280
409
|
|
|
281
|
-
protected async clearBucketData(
|
|
282
|
-
for (const collection of await this.db.
|
|
410
|
+
protected async clearBucketData(signal?: AbortSignal): Promise<void> {
|
|
411
|
+
for (const collection of await this.db.listBucketDataCollections(this.replicationStreamId)) {
|
|
283
412
|
await collection.drop();
|
|
284
413
|
}
|
|
414
|
+
const usage = new ObjectStorageUsage(this.db, this.replicationStreamId);
|
|
415
|
+
await this.db.client.withSession((session) => session.withTransaction(() => usage.removeStream(session)));
|
|
416
|
+
if (this.objectStorage) {
|
|
417
|
+
const lifecycle = new ObjectStorageLifecycle(this.db, this.replicationStreamId, this.objectStorage);
|
|
418
|
+
await lifecycle.deletePrefix(lifecycle.streamPrefix(), { signal });
|
|
419
|
+
}
|
|
420
|
+
await this.db
|
|
421
|
+
.pendingObjectStorageDeletes(this.replicationStreamId)
|
|
422
|
+
.drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
|
|
423
|
+
.catch((error) => {
|
|
424
|
+
if (lib_mongo.isMongoNamespaceNotFoundError(error)) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
throw error;
|
|
428
|
+
});
|
|
285
429
|
}
|
|
286
430
|
|
|
287
431
|
protected async clearParameterIndexes(_signal?: AbortSignal): Promise<void> {
|
|
288
|
-
for (const collection of await this.db.
|
|
432
|
+
for (const collection of await this.db.listParameterIndexCollections(this.replicationStreamId)) {
|
|
289
433
|
await collection.collection.drop();
|
|
290
434
|
}
|
|
291
435
|
}
|
|
292
436
|
|
|
293
437
|
protected async clearSourceRecords(_signal?: AbortSignal): Promise<void> {
|
|
294
|
-
for (const collection of await this.db.
|
|
438
|
+
for (const collection of await this.db.listSourceRecordCollections(this.replicationStreamId)) {
|
|
295
439
|
await collection.drop();
|
|
296
440
|
}
|
|
297
441
|
}
|
|
298
442
|
|
|
299
443
|
protected async clearBucketState(_signal?: AbortSignal): Promise<void> {
|
|
300
444
|
await this.db
|
|
301
|
-
.
|
|
445
|
+
.bucketState(this.replicationStreamId)
|
|
302
446
|
.drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
|
|
303
447
|
.catch((error) => {
|
|
304
|
-
if (lib_mongo.
|
|
448
|
+
if (lib_mongo.isMongoNamespaceNotFoundError(error)) {
|
|
305
449
|
return;
|
|
306
450
|
}
|
|
307
451
|
throw error;
|
|
@@ -310,14 +454,41 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
310
454
|
|
|
311
455
|
protected async clearSourceTables(_signal?: AbortSignal): Promise<void> {
|
|
312
456
|
await this.db
|
|
313
|
-
.
|
|
457
|
+
.sourceTables(this.replicationStreamId)
|
|
314
458
|
.drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
|
|
315
459
|
.catch((error) => {
|
|
460
|
+
if (lib_mongo.isMongoNamespaceNotFoundError(error)) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
throw error;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
protected override async clearCustomCheckpointRequests(signal?: AbortSignal): Promise<void> {
|
|
468
|
+
for (const collection of await this.db.listCustomCheckpointRequestCollections(this.replicationStreamId)) {
|
|
469
|
+
signal?.throwIfAborted();
|
|
470
|
+
await collection.drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }).catch((error) => {
|
|
316
471
|
if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceNotFound') {
|
|
317
472
|
return;
|
|
318
473
|
}
|
|
319
474
|
throw error;
|
|
320
475
|
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async cleanupStoppedSyncConfigs(
|
|
480
|
+
options: storage.CleanupStoppedSyncConfigsOptions
|
|
481
|
+
): Promise<storage.CleanupStoppedSyncConfigsResult> {
|
|
482
|
+
return new MongoStoppedSyncConfigCleanup({
|
|
483
|
+
db: this.db,
|
|
484
|
+
replicationStreamId: this.replicationStreamId,
|
|
485
|
+
signal: options.signal,
|
|
486
|
+
logger: options.logger ?? this.logger,
|
|
487
|
+
defaultSchema: options.defaultSchema,
|
|
488
|
+
sourceConnectionTag: options.sourceConnectionTag,
|
|
489
|
+
objectStorage: this.objectStorage,
|
|
490
|
+
clearBatchThrottleRate: this.clearBatchThrottleRate
|
|
491
|
+
}).run();
|
|
321
492
|
}
|
|
322
493
|
|
|
323
494
|
protected getDataBucketChangesImpl(
|
|
@@ -327,14 +498,14 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
|
|
|
327
498
|
}
|
|
328
499
|
|
|
329
500
|
protected getParameterBucketChangesImpl(
|
|
330
|
-
options:
|
|
501
|
+
options: MongoGetCheckpointChangesOptions
|
|
331
502
|
): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
|
|
332
503
|
return getParameterBucketChangesV3(this.versionContext, options);
|
|
333
504
|
}
|
|
334
505
|
}
|
|
335
506
|
|
|
336
507
|
export async function getParameterSetsV3(
|
|
337
|
-
ctx:
|
|
508
|
+
ctx: MongoSyncBucketStorageContextV3,
|
|
338
509
|
checkpoint: MongoSyncBucketStorageCheckpoint,
|
|
339
510
|
lookups: ScopedParameterLookup[],
|
|
340
511
|
limit: number
|
|
@@ -350,8 +521,8 @@ export async function getParameterSetsV3(
|
|
|
350
521
|
pipeline: mongo.Document[];
|
|
351
522
|
} => {
|
|
352
523
|
const indexId = lookup.indexId;
|
|
353
|
-
const collection = ctx.db.
|
|
354
|
-
const lookupFilter =
|
|
524
|
+
const collection = ctx.db.parameterIndex(ctx.replicationStreamId, indexId);
|
|
525
|
+
const lookupFilter = serializeParameterLookup(lookup);
|
|
355
526
|
|
|
356
527
|
return {
|
|
357
528
|
collection,
|
|
@@ -434,24 +605,38 @@ export async function getParameterSetsV3(
|
|
|
434
605
|
}
|
|
435
606
|
|
|
436
607
|
export async function* getBucketDataBatchV3(
|
|
437
|
-
ctx:
|
|
438
|
-
checkpoint:
|
|
608
|
+
ctx: MongoSyncBucketStorageContextV3,
|
|
609
|
+
checkpoint: MongoSyncBucketStorageCheckpoint,
|
|
439
610
|
dataBuckets: storage.BucketDataRequest[],
|
|
440
611
|
options?: storage.BucketDataBatchOptions
|
|
441
|
-
): AsyncIterable<storage.SyncBucketDataChunk> {
|
|
612
|
+
): AsyncIterable<storage.SyncBucketDataChunk | storage.SyncBucketDataBatchEnd> {
|
|
442
613
|
if (dataBuckets.length == 0) {
|
|
443
614
|
return;
|
|
444
615
|
}
|
|
445
616
|
|
|
446
|
-
if (checkpoint == null) {
|
|
617
|
+
if (checkpoint.checkpoint == null) {
|
|
447
618
|
throw new Error('checkpoint is null');
|
|
448
619
|
}
|
|
449
620
|
|
|
621
|
+
const readPreference = options?.requestHint == 'bulk' ? ctx.readPreference : undefined;
|
|
622
|
+
const readConcern = ctx.readPreference == null ? undefined : 'majority';
|
|
623
|
+
const session =
|
|
624
|
+
readPreference == null || checkpoint.snapshotTime == null
|
|
625
|
+
? undefined
|
|
626
|
+
: ctx.db.client.startSession({ causalConsistency: true });
|
|
627
|
+
await using _ = { [Symbol.asyncDispose]: async () => session?.endSession() };
|
|
628
|
+
|
|
629
|
+
if (session != null) {
|
|
630
|
+
session.advanceOperationTime(checkpoint.snapshotTime);
|
|
631
|
+
session.advanceClusterTime(checkpoint.clusterTime);
|
|
632
|
+
}
|
|
633
|
+
|
|
450
634
|
const batchLimit = options?.limit ?? storage.DEFAULT_DOCUMENT_BATCH_LIMIT;
|
|
451
635
|
const chunkSizeLimitBytes = options?.chunkLimitBytes ?? storage.DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES;
|
|
452
|
-
const end = checkpoint;
|
|
453
|
-
let remainingLimit = batchLimit;
|
|
636
|
+
const end = checkpoint.checkpoint;
|
|
454
637
|
|
|
638
|
+
// Group requests by definition, so that we can query each definition's bucket data in a single query.
|
|
639
|
+
// We only return the results of a single query per batch.
|
|
455
640
|
const requestsByDefinition = new Map<string, storage.BucketDataRequest[]>();
|
|
456
641
|
for (const request of dataBuckets) {
|
|
457
642
|
const definitionId = ctx.mapping.bucketSourceId(request.source);
|
|
@@ -461,61 +646,79 @@ export async function* getBucketDataBatchV3(
|
|
|
461
646
|
}
|
|
462
647
|
|
|
463
648
|
const definitionGroups = Array.from(requestsByDefinition.entries());
|
|
464
|
-
for (
|
|
465
|
-
const [definitionId, requests] = definitionGroups[groupIndex];
|
|
649
|
+
for (const [groupIndex, [definitionId, requests]] of definitionGroups.entries()) {
|
|
466
650
|
const hasLaterDefinitionGroups = groupIndex < definitionGroups.length - 1;
|
|
467
651
|
const bucketMap = new Map(requests.map((request) => [request.bucket, request.start]));
|
|
468
|
-
const filters
|
|
652
|
+
const filters = Array.from(bucketMap.entries()).map(([bucket, start]) => ({
|
|
469
653
|
_id: {
|
|
470
|
-
$gt: {
|
|
471
|
-
|
|
472
|
-
o: start
|
|
473
|
-
},
|
|
474
|
-
$lte: {
|
|
475
|
-
b: bucket,
|
|
476
|
-
o: end as any
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}));
|
|
480
|
-
|
|
481
|
-
const cursor = ctx.db.bucketDataV3(ctx.group_id, definitionId).find(
|
|
482
|
-
{
|
|
483
|
-
$or: filters
|
|
654
|
+
$gt: { b: bucket, o: start },
|
|
655
|
+
$lte: { b: bucket, o: new bson.MaxKey() }
|
|
484
656
|
},
|
|
485
|
-
{
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
657
|
+
min_op: { $lte: end }
|
|
658
|
+
// MongoDB Filter<T> doesn't accept compound _id ranges or dotted field paths in its type.
|
|
659
|
+
})) as unknown as mongo.Filter<BucketDataDocumentV3>[];
|
|
660
|
+
|
|
661
|
+
const collection = ctx.db.bucketData(ctx.replicationStreamId, definitionId);
|
|
662
|
+
// MongoDB Filter<T> doesn't accept the $or operator in its type.
|
|
663
|
+
const filter = { $or: filters } as unknown as mongo.Filter<BucketDataDocumentV3>;
|
|
664
|
+
const context = { replicationStreamId: ctx.replicationStreamId, definitionId };
|
|
665
|
+
|
|
666
|
+
const cursorOptions = { limit: batchLimit, batchSize: batchLimit + 1 };
|
|
667
|
+
|
|
668
|
+
// raw: true returns Buffers, but the driver typing doesn't reflect that
|
|
669
|
+
// without an explicit cast to FindCursor<Buffer>.
|
|
670
|
+
const cursor = collection.find(filter, {
|
|
671
|
+
session,
|
|
672
|
+
readPreference,
|
|
673
|
+
readConcern,
|
|
674
|
+
sort: { _id: 1 },
|
|
675
|
+
raw: true,
|
|
676
|
+
maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS,
|
|
677
|
+
...cursorOptions
|
|
678
|
+
}) as unknown as mongo.FindCursor<Buffer>;
|
|
679
|
+
|
|
680
|
+
let { data: rawData, hasMore } = await readSingleBatch(cursor).catch((e: unknown) => {
|
|
496
681
|
throw lib_mongo.mapQueryError(e, 'while reading bucket data');
|
|
497
682
|
});
|
|
498
|
-
|
|
499
|
-
|
|
683
|
+
|
|
684
|
+
if (rawData.length >= cursorOptions.limit) {
|
|
685
|
+
hasMore = true;
|
|
500
686
|
}
|
|
501
|
-
|
|
502
|
-
|
|
687
|
+
|
|
688
|
+
// Deserialize the raw documents and cut the batch to a bounded size. Any data not
|
|
689
|
+
// making the cut will be read in the next round, using a fresh query.
|
|
690
|
+
const cutBatch = cutBucketDataBatch(rawData);
|
|
691
|
+
const docs = cutBatch.documents;
|
|
692
|
+
if (cutBatch.wasCut) {
|
|
693
|
+
hasMore = true;
|
|
503
694
|
}
|
|
504
695
|
|
|
505
|
-
|
|
696
|
+
// Hydrate any operations from object storage.
|
|
697
|
+
// In the future we can do this in a more pipelined fashion, but for now we hydrate
|
|
698
|
+
// the entire batch at once.
|
|
699
|
+
await hydrateBucketDataDocuments(docs, ctx.objectStorage, { signal: options?.signal, tracer: options?.tracer });
|
|
506
700
|
|
|
507
|
-
let
|
|
701
|
+
let currentChunkSizeBytes = 0;
|
|
508
702
|
let currentChunk: utils.SyncBucketData | null = null;
|
|
509
703
|
let targetOp: InternalOpId | null = null;
|
|
704
|
+
let seenBuckets = new Set<string>();
|
|
705
|
+
const batchHasMore = hasMore;
|
|
706
|
+
|
|
707
|
+
for (const doc of docs) {
|
|
708
|
+
const bucket = doc._id.b;
|
|
709
|
+
seenBuckets.add(bucket);
|
|
710
|
+
const bucketStart = bucketMap.get(bucket);
|
|
711
|
+
if (bucketStart == null) {
|
|
712
|
+
throw new ServiceAssertionError(`data for unexpected bucket: ${bucket}`);
|
|
713
|
+
}
|
|
510
714
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (currentChunk == null || currentChunk.bucket != bucket || chunkSizeBytes >= chunkSizeLimitBytes) {
|
|
715
|
+
// Reached a new bucket or size limit: yield the current chunk and start a new one.
|
|
716
|
+
if (
|
|
717
|
+
currentChunk == null ||
|
|
718
|
+
currentChunk.bucket != bucket ||
|
|
719
|
+
currentChunkSizeBytes >= chunkSizeLimitBytes ||
|
|
720
|
+
currentChunk.data.length >= batchLimit
|
|
721
|
+
) {
|
|
519
722
|
let start: ProtocolOpId | undefined = undefined;
|
|
520
723
|
if (currentChunk != null) {
|
|
521
724
|
if (currentChunk.bucket == bucket) {
|
|
@@ -525,18 +728,15 @@ export async function* getBucketDataBatchV3(
|
|
|
525
728
|
|
|
526
729
|
const yieldChunk = currentChunk;
|
|
527
730
|
currentChunk = null;
|
|
528
|
-
|
|
529
|
-
yield { chunkData: yieldChunk, targetOp
|
|
731
|
+
currentChunkSizeBytes = 0;
|
|
732
|
+
yield { chunkData: yieldChunk, targetOp };
|
|
530
733
|
targetOp = null;
|
|
531
734
|
}
|
|
532
735
|
|
|
533
736
|
if (start == null) {
|
|
534
|
-
|
|
535
|
-
if (startOpId == null) {
|
|
536
|
-
throw new Error(`data for unexpected bucket: ${bucket}`);
|
|
537
|
-
}
|
|
538
|
-
start = internalToExternalOpId(startOpId);
|
|
737
|
+
start = internalToExternalOpId(bucketStart);
|
|
539
738
|
}
|
|
739
|
+
|
|
540
740
|
currentChunk = {
|
|
541
741
|
bucket,
|
|
542
742
|
after: start,
|
|
@@ -546,35 +746,58 @@ export async function* getBucketDataBatchV3(
|
|
|
546
746
|
};
|
|
547
747
|
}
|
|
548
748
|
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
currentChunk.data.push(entry);
|
|
555
|
-
currentChunk.next_after = entry.op_id;
|
|
556
|
-
chunkSizeBytes += rawData.byteLength;
|
|
749
|
+
const rows = extractRowsFromDocument(doc, context, bucketStart, end);
|
|
750
|
+
currentChunk.data.push(...rows.map(mapOpEntry));
|
|
751
|
+
currentChunk.next_after = currentChunk.data.at(-1)?.op_id ?? internalToExternalOpId(end);
|
|
752
|
+
targetOp = maxOpId(targetOp, doc.target_op);
|
|
753
|
+
currentChunkSizeBytes += doc.size;
|
|
557
754
|
}
|
|
558
755
|
|
|
559
756
|
if (currentChunk != null) {
|
|
560
757
|
const yieldChunk = currentChunk;
|
|
561
|
-
|
|
562
|
-
|
|
758
|
+
// The last chunk may contain more data that was cut in this batch.
|
|
759
|
+
yieldChunk.has_more = batchHasMore;
|
|
760
|
+
yield { chunkData: yieldChunk, targetOp };
|
|
563
761
|
}
|
|
564
762
|
|
|
565
|
-
if (batchHasMore
|
|
566
|
-
|
|
763
|
+
if (!batchHasMore) {
|
|
764
|
+
for (const bucket of bucketMap.keys()) {
|
|
765
|
+
if (!seenBuckets.has(bucket)) {
|
|
766
|
+
// We processed everything for this definition group, but this bucket had no data in the batch.
|
|
767
|
+
// Yield an empty chunk to indicate that it is complete.
|
|
768
|
+
// This prevents re-querying the same bucket in the next batch.
|
|
769
|
+
yield {
|
|
770
|
+
chunkData: {
|
|
771
|
+
bucket,
|
|
772
|
+
after: internalToExternalOpId(bucketMap.get(bucket)!),
|
|
773
|
+
has_more: false,
|
|
774
|
+
data: [],
|
|
775
|
+
next_after: internalToExternalOpId(end)
|
|
776
|
+
},
|
|
777
|
+
targetOp
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (currentChunk != null) {
|
|
784
|
+
// We yielded data in this group (aside from empty buckets).
|
|
785
|
+
// Return to the caller to allow them to process it before continuing to the next group.
|
|
786
|
+
yield { hasMore: batchHasMore || hasLaterDefinitionGroups };
|
|
787
|
+
break;
|
|
788
|
+
} else {
|
|
789
|
+
// No data in this definition group - continue in the next group.
|
|
567
790
|
}
|
|
568
791
|
}
|
|
569
792
|
}
|
|
570
793
|
|
|
571
794
|
export async function getDataBucketChangesV3(
|
|
572
|
-
ctx:
|
|
795
|
+
ctx: MongoSyncBucketStorageContextV3,
|
|
573
796
|
options: GetCheckpointChangesOptions
|
|
574
797
|
): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
|
|
575
798
|
const limit = 1000;
|
|
576
799
|
const bucketStateUpdates = await ctx.db
|
|
577
|
-
.
|
|
800
|
+
.bucketState(ctx.replicationStreamId)
|
|
578
801
|
.aggregate<{ _id: string; last_op: bigint }>(
|
|
579
802
|
[
|
|
580
803
|
{
|
|
@@ -610,15 +833,23 @@ export async function getDataBucketChangesV3(
|
|
|
610
833
|
};
|
|
611
834
|
}
|
|
612
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Query the parameter entries changed between the two checkpoints, to determine which parameter
|
|
838
|
+
* lookups need to be re-evaluated.
|
|
839
|
+
*
|
|
840
|
+
* This runs at the next checkpoint's snapshot, so it still sees entries that parameter compaction
|
|
841
|
+
* deleted after that snapshot. Compaction that deleted entries before the snapshot is covered by
|
|
842
|
+
* the invalidation fence, checked before we get here.
|
|
843
|
+
*/
|
|
613
844
|
export async function getParameterBucketChangesV3(
|
|
614
|
-
ctx:
|
|
615
|
-
options:
|
|
845
|
+
ctx: MongoSyncBucketStorageContextV3,
|
|
846
|
+
options: MongoGetCheckpointChangesOptions
|
|
616
847
|
): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
|
|
617
848
|
const limit = 1000;
|
|
618
849
|
const indexIds = ctx.mapping.allParameterIndexIds();
|
|
619
850
|
const collections = indexIds.map((indexId) => ({
|
|
620
851
|
indexId,
|
|
621
|
-
collection: ctx.db.
|
|
852
|
+
collection: ctx.db.parameterIndex(ctx.replicationStreamId, indexId)
|
|
622
853
|
}));
|
|
623
854
|
if (collections.length == 0) {
|
|
624
855
|
return {
|
|
@@ -642,28 +873,41 @@ export async function getParameterBucketChangesV3(
|
|
|
642
873
|
}
|
|
643
874
|
];
|
|
644
875
|
const [firstCollection, ...remainingCollections] = collections;
|
|
645
|
-
const parameterUpdates = await
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
876
|
+
const parameterUpdates = await ctx.db.client.withSession({ snapshot: true }, async (session) => {
|
|
877
|
+
setSessionSnapshotTime(session, options.nextCheckpoint.snapshotTime);
|
|
878
|
+
return await firstCollection.collection
|
|
879
|
+
.aggregate<{ lookup: bson.Binary; indexId: string }>(
|
|
880
|
+
[
|
|
881
|
+
...pipelineForCollection(firstCollection.indexId),
|
|
882
|
+
...remainingCollections.map((collection) => {
|
|
883
|
+
return {
|
|
884
|
+
$unionWith: {
|
|
885
|
+
coll: collection.collection.collectionName,
|
|
886
|
+
pipeline: pipelineForCollection(collection.indexId)
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
}),
|
|
890
|
+
{
|
|
891
|
+
$limit: limit + 1
|
|
892
|
+
}
|
|
893
|
+
],
|
|
657
894
|
{
|
|
658
|
-
|
|
895
|
+
session,
|
|
896
|
+
readConcern: 'snapshot',
|
|
897
|
+
batchSize: limit + 2,
|
|
898
|
+
maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
|
|
659
899
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
900
|
+
)
|
|
901
|
+
.toArray()
|
|
902
|
+
.catch((e) => {
|
|
903
|
+
// Includes the case where the checkpoint snapshot has expired. Degrading to
|
|
904
|
+
// invalidateParameterBuckets would be safe in itself - it reads nothing - but it gains
|
|
905
|
+
// nothing: the caller responds to that by re-evaluating the parameter queries at this
|
|
906
|
+
// same snapshot, which fails too. The checkpoint has to be refetched instead, which is
|
|
907
|
+
// what the existing sync retry behavior does.
|
|
908
|
+
throw lib_mongo.mapQueryError(e, 'while querying parameter changes');
|
|
909
|
+
});
|
|
910
|
+
});
|
|
667
911
|
|
|
668
912
|
const invalidateParameterUpdates = parameterUpdates.length > limit;
|
|
669
913
|
|
|
@@ -671,8 +915,6 @@ export async function getParameterBucketChangesV3(
|
|
|
671
915
|
invalidateParameterBuckets: invalidateParameterUpdates,
|
|
672
916
|
updatedParameterLookups: invalidateParameterUpdates
|
|
673
917
|
? new Set<string>()
|
|
674
|
-
: new Set<string>(
|
|
675
|
-
parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookupV3(p.lookup, p.indexId)))
|
|
676
|
-
)
|
|
918
|
+
: new Set<string>(parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookup(p.lookup, p.indexId))))
|
|
677
919
|
};
|
|
678
920
|
}
|