@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
|
@@ -5,6 +5,8 @@ import { ErrorCode, Logger, ServiceError } from '@powersync/lib-services-framewo
|
|
|
5
5
|
import { storage } from '@powersync/service-core';
|
|
6
6
|
import { VersionedPowerSyncMongo } from './db.js';
|
|
7
7
|
|
|
8
|
+
const LOCK_DURATION_MS = 60 * 1000;
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Manages a lock on a replication stream document, so that only one process
|
|
10
12
|
* processes that replication stream at a time.
|
|
@@ -17,20 +19,25 @@ export class MongoSyncRulesLock implements storage.ReplicationLock {
|
|
|
17
19
|
*/
|
|
18
20
|
static async createLock(
|
|
19
21
|
db: VersionedPowerSyncMongo,
|
|
20
|
-
sync_rules: storage.
|
|
22
|
+
sync_rules: storage.PersistedReplicationStream,
|
|
21
23
|
session?: mongo.ClientSession
|
|
22
24
|
): Promise<MongoSyncRulesLock> {
|
|
23
25
|
const lockId = crypto.randomBytes(8).toString('hex');
|
|
24
26
|
const doc = await db.sync_rules.findOneAndUpdate(
|
|
25
|
-
{ _id: sync_rules.id, $or: [{ lock: null }, { 'lock.expires_at': { $lt: new Date() } }] },
|
|
26
27
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
_id: sync_rules.replicationStreamId,
|
|
29
|
+
$or: [{ lock: null }, { $expr: { $lt: ['$lock.expires_at', '$$NOW'] } }]
|
|
30
|
+
},
|
|
31
|
+
[
|
|
32
|
+
{
|
|
33
|
+
$set: {
|
|
34
|
+
lock: {
|
|
35
|
+
id: lockId,
|
|
36
|
+
expires_at: { $dateAdd: { startDate: '$$NOW', unit: 'millisecond', amount: LOCK_DURATION_MS } }
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
|
-
|
|
40
|
+
],
|
|
34
41
|
{
|
|
35
42
|
projection: { lock: 1 },
|
|
36
43
|
returnDocument: 'before',
|
|
@@ -40,7 +47,10 @@ export class MongoSyncRulesLock implements storage.ReplicationLock {
|
|
|
40
47
|
|
|
41
48
|
if (doc == null) {
|
|
42
49
|
// Query the existing lock to get the expiration time (best effort - it may have been released in the meantime).
|
|
43
|
-
const heldLock = await db.sync_rules.findOne(
|
|
50
|
+
const heldLock = await db.sync_rules.findOne(
|
|
51
|
+
{ _id: sync_rules.replicationStreamId },
|
|
52
|
+
{ projection: { lock: 1 }, session }
|
|
53
|
+
);
|
|
44
54
|
if (heldLock?.lock?.expires_at) {
|
|
45
55
|
throw new ServiceError(
|
|
46
56
|
ErrorCode.PSYNC_S1003,
|
|
@@ -51,7 +61,7 @@ export class MongoSyncRulesLock implements storage.ReplicationLock {
|
|
|
51
61
|
}
|
|
52
62
|
}
|
|
53
63
|
sync_rules.logger.info(`Locked replication stream for processing`);
|
|
54
|
-
return new MongoSyncRulesLock(db, sync_rules.
|
|
64
|
+
return new MongoSyncRulesLock(db, sync_rules.replicationStreamId, lockId, sync_rules.logger);
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
constructor(
|
|
@@ -93,9 +103,15 @@ export class MongoSyncRulesLock implements storage.ReplicationLock {
|
|
|
93
103
|
_id: this.sync_rules_id,
|
|
94
104
|
'lock.id': this.lock_id
|
|
95
105
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
[
|
|
107
|
+
{
|
|
108
|
+
$set: {
|
|
109
|
+
'lock.expires_at': {
|
|
110
|
+
$dateAdd: { startDate: '$$NOW', unit: 'millisecond', amount: LOCK_DURATION_MS }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
],
|
|
99
115
|
{ returnDocument: 'after' }
|
|
100
116
|
);
|
|
101
117
|
if (result == null) {
|
|
@@ -1,55 +1,223 @@
|
|
|
1
|
-
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
1
|
import * as framework from '@powersync/lib-services-framework';
|
|
3
|
-
import { GetCheckpointChangesOptions,
|
|
2
|
+
import { GetCheckpointChangesOptions, storage } from '@powersync/service-core';
|
|
4
3
|
import { VersionedPowerSyncMongo } from './db.js';
|
|
5
4
|
|
|
6
5
|
export type MongoCheckpointAPIOptions = {
|
|
7
6
|
db: VersionedPowerSyncMongo;
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
writeCheckpointMode: storage.WriteCheckpointModeConfig;
|
|
8
|
+
replicationStreamId: number;
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
export class MongoWriteCheckpointAPI implements storage.WriteCheckpointAPI {
|
|
13
12
|
readonly db: VersionedPowerSyncMongo;
|
|
13
|
+
readonly replicationStreamId: number;
|
|
14
|
+
|
|
14
15
|
private _mode: storage.WriteCheckpointMode;
|
|
15
16
|
|
|
16
17
|
constructor(options: MongoCheckpointAPIOptions) {
|
|
17
18
|
this.db = options.db;
|
|
18
|
-
this._mode = options.mode;
|
|
19
|
+
this._mode = options.writeCheckpointMode.mode;
|
|
20
|
+
this.replicationStreamId = options.replicationStreamId;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
get writeCheckpointMode() {
|
|
22
24
|
return this._mode;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
setWriteCheckpointMode(
|
|
26
|
-
this._mode = mode;
|
|
27
|
+
setWriteCheckpointMode(config: storage.WriteCheckpointModeConfig): void {
|
|
28
|
+
this._mode = config.mode;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
async
|
|
31
|
+
async createManagedWriteCheckpoints(
|
|
32
|
+
checkpoints: storage.ManagedWriteCheckpointOptions[]
|
|
33
|
+
): Promise<storage.CreateManagedWriteCheckpointsResult> {
|
|
30
34
|
if (this.writeCheckpointMode !== storage.WriteCheckpointMode.MANAGED) {
|
|
31
35
|
throw new framework.ServiceAssertionError(
|
|
32
36
|
`Attempting to create a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"`
|
|
33
37
|
);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
const uniqueCheckpoints = storage.uniqueManagedWriteCheckpoints(checkpoints);
|
|
41
|
+
if (uniqueCheckpoints.length == 0) {
|
|
42
|
+
return { writeCheckpoints: new Map(), shouldAdvance: false };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let shouldAdvance = false;
|
|
46
|
+
const writeCheckpoints = new Map<string, bigint>();
|
|
47
|
+
const generatedCheckpoints = uniqueCheckpoints.filter((checkpoint) => checkpoint.checkpoint_request_id == null);
|
|
48
|
+
const suppliedCheckpoints = uniqueCheckpoints.filter((checkpoint) => checkpoint.checkpoint_request_id != null);
|
|
49
|
+
|
|
50
|
+
if (generatedCheckpoints.length == 1) {
|
|
51
|
+
// For the common case of a single checkpoint, we can do this in a single request.
|
|
52
|
+
const { user_id, heads: lsns } = generatedCheckpoints[0];
|
|
53
|
+
const doc = await this.db.write_checkpoints.findOneAndUpdate(
|
|
54
|
+
{
|
|
55
|
+
user_id
|
|
45
56
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
{
|
|
58
|
+
$set: {
|
|
59
|
+
lsns,
|
|
60
|
+
processed_at_lsn: null
|
|
61
|
+
},
|
|
62
|
+
// Unset rather than set to null, to keep the document out of the
|
|
63
|
+
// partial checkpoint_requested_at index used for retention cleanup.
|
|
64
|
+
$unset: {
|
|
65
|
+
checkpoint_requested_at: 1
|
|
66
|
+
},
|
|
67
|
+
$inc: {
|
|
68
|
+
client_id: 1n
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{ upsert: true, returnDocument: 'after' }
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Generated checkpoints always reset processed_at_lsn, so they are pending
|
|
75
|
+
// and the source marker must be forced.
|
|
76
|
+
shouldAdvance = true;
|
|
77
|
+
writeCheckpoints.set(doc!.user_id, doc!.client_id);
|
|
78
|
+
} else if (generatedCheckpoints.length > 1) {
|
|
79
|
+
// For more than one generated checkpoint, this gives a constant 2 requests.
|
|
80
|
+
await this.db.write_checkpoints.bulkWrite(
|
|
81
|
+
generatedCheckpoints.map(({ user_id, heads: lsns }) => ({
|
|
82
|
+
updateOne: {
|
|
83
|
+
filter: { user_id },
|
|
84
|
+
update: {
|
|
85
|
+
$set: {
|
|
86
|
+
lsns,
|
|
87
|
+
processed_at_lsn: null
|
|
88
|
+
},
|
|
89
|
+
$unset: {
|
|
90
|
+
checkpoint_requested_at: 1
|
|
91
|
+
},
|
|
92
|
+
$inc: {
|
|
93
|
+
client_id: 1n
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
upsert: true
|
|
97
|
+
}
|
|
98
|
+
}))
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const userIds = generatedCheckpoints.map((checkpoint) => checkpoint.user_id);
|
|
102
|
+
const docs = await this.db.write_checkpoints
|
|
103
|
+
.find(
|
|
104
|
+
{
|
|
105
|
+
user_id: { $in: userIds }
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
projection: {
|
|
109
|
+
user_id: 1,
|
|
110
|
+
client_id: 1
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
.toArray();
|
|
115
|
+
|
|
116
|
+
shouldAdvance = true;
|
|
117
|
+
for (const doc of docs) {
|
|
118
|
+
writeCheckpoints.set(doc.user_id, doc.client_id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (suppliedCheckpoints.length > 0) {
|
|
123
|
+
const suppliedResult = await this.createSuppliedManagedWriteCheckpoints(suppliedCheckpoints);
|
|
124
|
+
shouldAdvance ||= suppliedResult.shouldAdvance;
|
|
125
|
+
for (const [userId, writeCheckpoint] of suppliedResult.writeCheckpoints) {
|
|
126
|
+
writeCheckpoints.set(userId, writeCheckpoint);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { writeCheckpoints, shouldAdvance };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async createSuppliedManagedWriteCheckpoints(checkpoints: storage.ManagedWriteCheckpointOptions[]) {
|
|
134
|
+
const checkpointRequestedAt = new Date();
|
|
135
|
+
|
|
136
|
+
await this.db.write_checkpoints.bulkWrite(
|
|
137
|
+
checkpoints.map((checkpoint) => {
|
|
138
|
+
const { user_id, heads: lsns } = checkpoint;
|
|
139
|
+
const checkpointRequestId = checkpoint.checkpoint_request_id!;
|
|
140
|
+
|
|
141
|
+
// Supplied request ids are monotonic. updateMany preserves the existing
|
|
142
|
+
// upsert behavior for first checkpoint requests, while also advancing
|
|
143
|
+
// every duplicate row for the user_id that is lower than this request id.
|
|
144
|
+
const shouldApplyRequestId = {
|
|
145
|
+
$or: [
|
|
146
|
+
{ $eq: [{ $ifNull: ['$client_id', null] }, null] },
|
|
147
|
+
{ $lt: ['$client_id', { $literal: checkpointRequestId }] }
|
|
148
|
+
]
|
|
149
|
+
};
|
|
150
|
+
// Equal retries keep their original source head but refresh retention.
|
|
151
|
+
const shouldRefreshRequest = {
|
|
152
|
+
$or: [
|
|
153
|
+
{ $eq: [{ $ifNull: ['$client_id', null] }, null] },
|
|
154
|
+
{ $lte: ['$client_id', { $literal: checkpointRequestId }] }
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
updateMany: {
|
|
160
|
+
filter: { user_id },
|
|
161
|
+
update: [
|
|
162
|
+
{
|
|
163
|
+
$set: {
|
|
164
|
+
user_id,
|
|
165
|
+
client_id: {
|
|
166
|
+
$cond: [shouldApplyRequestId, { $literal: checkpointRequestId }, '$client_id']
|
|
167
|
+
},
|
|
168
|
+
lsns: {
|
|
169
|
+
$cond: [shouldApplyRequestId, { $literal: lsns }, '$lsns']
|
|
170
|
+
},
|
|
171
|
+
processed_at_lsn: {
|
|
172
|
+
$cond: [shouldApplyRequestId, null, '$processed_at_lsn']
|
|
173
|
+
},
|
|
174
|
+
checkpoint_requested_at: {
|
|
175
|
+
$cond: [shouldRefreshRequest, { $literal: checkpointRequestedAt }, '$checkpoint_requested_at']
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
upsert: true
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
})
|
|
51
184
|
);
|
|
52
|
-
|
|
185
|
+
|
|
186
|
+
// Fetch the final ids separately so stale requests can still return the
|
|
187
|
+
// checkpoint currently stored. If duplicate rows already exist for a
|
|
188
|
+
// user_id, use the highest stored id instead of whichever document MongoDB
|
|
189
|
+
// happens to return first.
|
|
190
|
+
const userIds = checkpoints.map((checkpoint) => checkpoint.user_id);
|
|
191
|
+
const docs = await this.db.write_checkpoints
|
|
192
|
+
.find(
|
|
193
|
+
{
|
|
194
|
+
user_id: { $in: userIds }
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
projection: {
|
|
198
|
+
user_id: 1,
|
|
199
|
+
client_id: 1,
|
|
200
|
+
processed_at_lsn: 1
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
.toArray();
|
|
205
|
+
const maxDocsByUser = new Map<string, (typeof docs)[number]>();
|
|
206
|
+
for (const doc of docs) {
|
|
207
|
+
const existing = maxDocsByUser.get(doc.user_id);
|
|
208
|
+
if (
|
|
209
|
+
existing == null ||
|
|
210
|
+
doc.client_id > existing.client_id ||
|
|
211
|
+
(doc.client_id == existing.client_id && existing.processed_at_lsn != null && doc.processed_at_lsn == null)
|
|
212
|
+
) {
|
|
213
|
+
maxDocsByUser.set(doc.user_id, doc);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
writeCheckpoints: new Map([...maxDocsByUser].map(([userId, doc]) => [userId, doc.client_id])),
|
|
219
|
+
shouldAdvance: [...maxDocsByUser.values()].some((doc) => doc.processed_at_lsn == null)
|
|
220
|
+
};
|
|
53
221
|
}
|
|
54
222
|
|
|
55
223
|
async lastWriteCheckpoint(filters: storage.LastWriteCheckpointFilters): Promise<bigint | null> {
|
|
@@ -81,6 +249,7 @@ export class MongoWriteCheckpointAPI implements storage.WriteCheckpointAPI {
|
|
|
81
249
|
}
|
|
82
250
|
|
|
83
251
|
protected async lastCustomWriteCheckpoint(filters: storage.CustomWriteCheckpointFilters) {
|
|
252
|
+
// Legacy V1/V2 implementation.
|
|
84
253
|
const { user_id, sync_rules_id } = filters;
|
|
85
254
|
const lastWriteCheckpoint = await this.db.custom_write_checkpoints.findOne({
|
|
86
255
|
user_id,
|
|
@@ -135,8 +304,9 @@ export class MongoWriteCheckpointAPI implements storage.WriteCheckpointAPI {
|
|
|
135
304
|
};
|
|
136
305
|
}
|
|
137
306
|
|
|
138
|
-
|
|
307
|
+
protected async getCustomWriteCheckpointChanges(options: GetCheckpointChangesOptions) {
|
|
139
308
|
const limit = 1000;
|
|
309
|
+
// Legacy V1/V2 implementation.
|
|
140
310
|
const changes = await this.db.custom_write_checkpoints
|
|
141
311
|
.find(
|
|
142
312
|
{
|
|
@@ -166,31 +336,3 @@ export class MongoWriteCheckpointAPI implements storage.WriteCheckpointAPI {
|
|
|
166
336
|
};
|
|
167
337
|
}
|
|
168
338
|
}
|
|
169
|
-
|
|
170
|
-
export async function batchCreateCustomWriteCheckpoints(
|
|
171
|
-
db: VersionedPowerSyncMongo,
|
|
172
|
-
session: mongo.ClientSession,
|
|
173
|
-
checkpoints: storage.CustomWriteCheckpointOptions[],
|
|
174
|
-
opId: InternalOpId
|
|
175
|
-
): Promise<void> {
|
|
176
|
-
if (checkpoints.length == 0) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
await db.custom_write_checkpoints.bulkWrite(
|
|
181
|
-
checkpoints.map((checkpointOptions) => ({
|
|
182
|
-
updateOne: {
|
|
183
|
-
filter: { user_id: checkpointOptions.user_id, sync_rules_id: checkpointOptions.sync_rules_id },
|
|
184
|
-
update: {
|
|
185
|
-
$set: {
|
|
186
|
-
checkpoint: checkpointOptions.checkpoint,
|
|
187
|
-
sync_rules_id: checkpointOptions.sync_rules_id,
|
|
188
|
-
op_id: opId
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
upsert: true
|
|
192
|
-
}
|
|
193
|
-
})),
|
|
194
|
-
{ session }
|
|
195
|
-
);
|
|
196
|
-
}
|
|
@@ -2,21 +2,28 @@ import { mongo } from '@powersync/lib-service-mongodb';
|
|
|
2
2
|
import { storage } from '@powersync/service-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Update pipeline to
|
|
5
|
+
* Update pipeline to stop a deploying replication stream, covering all storage versions.
|
|
6
6
|
*
|
|
7
7
|
* Roughly equivalent to:
|
|
8
8
|
* $set: {
|
|
9
|
-
* state:
|
|
10
|
-
* 'sync_configs.$[].state':
|
|
9
|
+
* state: STOP,
|
|
10
|
+
* 'sync_configs.$[].state': STOP
|
|
11
11
|
* }
|
|
12
12
|
*
|
|
13
13
|
* The difference is that this also handles v1 storage cases, where `sync_configs` is not present.
|
|
14
|
+
*
|
|
15
|
+
* This can be used for specific use cases:
|
|
16
|
+
* 1. Stop an existing PROCESSING stream, when it is being replaced by a new PROCESSING one.
|
|
17
|
+
* 2. Stop an existing ACTIVE or ERRORED stream, when it is being replaced by a new ACTIVE one.
|
|
18
|
+
*
|
|
19
|
+
* Do not use this when an ACTIVE stream transitions to ERRORED, and is being replaced by a new PROCESSING one.
|
|
20
|
+
* In that case, we need to maintain the ERRORED state, which this pipeline does not cover.
|
|
14
21
|
*/
|
|
15
|
-
export function
|
|
22
|
+
export function stopReplicationStreamPipeline(): mongo.Document[] {
|
|
16
23
|
return [
|
|
17
24
|
{
|
|
18
25
|
$set: {
|
|
19
|
-
state,
|
|
26
|
+
state: storage.SyncRuleState.STOP,
|
|
20
27
|
sync_configs: {
|
|
21
28
|
$cond: [
|
|
22
29
|
{ $isArray: '$sync_configs' },
|
|
@@ -25,7 +32,7 @@ export function syncRuleStateUpdatePipeline(state: storage.SyncRuleState): mongo
|
|
|
25
32
|
input: '$sync_configs',
|
|
26
33
|
as: 'config',
|
|
27
34
|
in: {
|
|
28
|
-
$mergeObjects: ['$$config', { state }]
|
|
35
|
+
$mergeObjects: ['$$config', { state: storage.SyncRuleState.STOP }]
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { InternalOpId, ReplicationCheckpoint } from '@powersync/service-core';
|
|
3
|
+
import * as bson from 'bson';
|
|
4
|
+
|
|
5
|
+
export interface MongoSyncBucketStorageCheckpoint extends ReplicationCheckpoint {
|
|
6
|
+
checkpoint: InternalOpId;
|
|
7
|
+
snapshotTime: bson.Timestamp;
|
|
8
|
+
clusterTime: mongo.ClusterTime;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The stream's `parameter_compaction.checkpoint_changes_invalid_before` boundary, read in the
|
|
12
|
+
* same snapshot as {@link checkpoint} and {@link snapshotTime}.
|
|
13
|
+
*
|
|
14
|
+
* Parameter compaction may have deleted parameter entries below this boundary, so checkpoint
|
|
15
|
+
* change detection cannot enumerate individual lookup changes from a checkpoint below it.
|
|
16
|
+
*
|
|
17
|
+
* 0n for streams that have never been compacted.
|
|
18
|
+
*/
|
|
19
|
+
parameterChangesInvalidBefore: InternalOpId;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* MongoDB-specific version of GetCheckpointChangesOptions.
|
|
24
|
+
*
|
|
25
|
+
* The next checkpoint carries the snapshot and invalidation boundary that change detection must
|
|
26
|
+
* be evaluated against. Only `checkpoint` is used from the previous one.
|
|
27
|
+
*/
|
|
28
|
+
export interface MongoGetCheckpointChangesOptions {
|
|
29
|
+
lastCheckpoint: ReplicationCheckpoint;
|
|
30
|
+
nextCheckpoint: MongoSyncBucketStorageCheckpoint;
|
|
31
|
+
}
|
|
@@ -3,14 +3,14 @@ import { BucketDataSource, BucketDefinitionId, EvaluatedParameters, EvaluatedRow
|
|
|
3
3
|
import * as bson from 'bson';
|
|
4
4
|
|
|
5
5
|
import { logger as defaultLogger, Logger } from '@powersync/lib-services-framework';
|
|
6
|
-
import { InternalOpId, storage, utils } from '@powersync/service-core';
|
|
6
|
+
import { BucketDefinitionMapping, InternalOpId, storage, utils } from '@powersync/service-core';
|
|
7
7
|
import { JSONBig } from '@powersync/service-jsonbig';
|
|
8
8
|
import { mongoTableId, replicaIdToSubkey } from '../../../utils/util.js';
|
|
9
|
-
import { BucketDefinitionMapping } from '../BucketDefinitionMapping.js';
|
|
10
9
|
import { currentBucketKey, MAX_ROW_SIZE } from '../MongoBucketBatchShared.js';
|
|
11
10
|
import { MongoIdSequence } from '../MongoIdSequence.js';
|
|
12
11
|
import type { VersionedPowerSyncMongo } from '../db.js';
|
|
13
12
|
import { TaggedBucketParameterDocument } from '../models.js';
|
|
13
|
+
import { ObjectStorage } from '../v3/object-storage/ObjectStorage.js';
|
|
14
14
|
import { BucketDataDoc, BucketKey } from './BucketDataDoc.js';
|
|
15
15
|
import { SourceRecordBucketState, SourceRecordLookupState } from './SourceRecordStore.js';
|
|
16
16
|
|
|
@@ -35,6 +35,8 @@ const MAX_TRANSACTION_BATCH_SIZE = 30_000_000;
|
|
|
35
35
|
*/
|
|
36
36
|
const MAX_TRANSACTION_DOC_COUNT = 2_000;
|
|
37
37
|
|
|
38
|
+
export const DEFAULT_INLINE_THRESHOLD_BYTES = 16 * 1024;
|
|
39
|
+
|
|
38
40
|
export interface SaveBucketDataOptions {
|
|
39
41
|
op_seq: MongoIdSequence;
|
|
40
42
|
sourceKey: storage.ReplicaId;
|
|
@@ -61,6 +63,13 @@ export interface UpsertCurrentDataOptions {
|
|
|
61
63
|
|
|
62
64
|
export interface PersistedBatchOptions {
|
|
63
65
|
logger?: Logger;
|
|
66
|
+
objectStorage?: ObjectStorage;
|
|
67
|
+
inlineThresholdBytes?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Aborts in-flight object storage uploads when replication stops.
|
|
70
|
+
*/
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
objectStorageUsageWriterId?: string;
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
/**
|
|
@@ -75,6 +84,11 @@ export abstract class PersistedBatch {
|
|
|
75
84
|
bucketParameters: TaggedBucketParameterDocument[] = [];
|
|
76
85
|
bucketStates: Map<string, BucketStateUpdate> = new Map();
|
|
77
86
|
|
|
87
|
+
protected readonly objectStorage?: ObjectStorage;
|
|
88
|
+
protected readonly inlineThresholdBytes: number = DEFAULT_INLINE_THRESHOLD_BYTES;
|
|
89
|
+
protected readonly signal?: AbortSignal;
|
|
90
|
+
protected readonly objectStorageUsageWriterId?: string;
|
|
91
|
+
|
|
78
92
|
/**
|
|
79
93
|
* For debug logging only.
|
|
80
94
|
*/
|
|
@@ -94,10 +108,16 @@ export abstract class PersistedBatch {
|
|
|
94
108
|
) {
|
|
95
109
|
this.currentSize = writtenSize;
|
|
96
110
|
this.logger = options?.logger ?? defaultLogger;
|
|
111
|
+
this.objectStorage = options?.objectStorage;
|
|
112
|
+
this.signal = options?.signal;
|
|
113
|
+
this.objectStorageUsageWriterId = options?.objectStorageUsageWriterId;
|
|
114
|
+
if (options?.inlineThresholdBytes != null) {
|
|
115
|
+
this.inlineThresholdBytes = options.inlineThresholdBytes;
|
|
116
|
+
}
|
|
97
117
|
}
|
|
98
118
|
|
|
99
119
|
saveBucketData(options: SaveBucketDataOptions) {
|
|
100
|
-
const remaining_buckets = new Map<string,
|
|
120
|
+
const remaining_buckets = new Map<string, SourceRecordBucketState>();
|
|
101
121
|
for (let bucket of options.before_buckets) {
|
|
102
122
|
const mapped: SourceRecordBucketState = {
|
|
103
123
|
bucket: bucket.bucket,
|
|
@@ -105,10 +125,16 @@ export abstract class PersistedBatch {
|
|
|
105
125
|
id: bucket.id,
|
|
106
126
|
table: bucket.table
|
|
107
127
|
};
|
|
128
|
+
if (options.table.bucketDataSourceIds != null && !options.table.bucketDataSourceIds.has(mapped.definitionId!)) {
|
|
129
|
+
// The bucket definition is not active anymore.
|
|
130
|
+
// We don't cleanup these references upfront, but do need to ignore them after the definition is removed.
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
108
133
|
remaining_buckets.set(currentBucketKey(mapped), mapped);
|
|
109
134
|
}
|
|
110
135
|
|
|
111
|
-
const
|
|
136
|
+
const subkey = this.persistedSubkey(options.table.id, options.sourceKey);
|
|
137
|
+
const dchecksum = BigInt(utils.hashDelete(subkey));
|
|
112
138
|
|
|
113
139
|
for (const evaluated of options.evaluated) {
|
|
114
140
|
const definitionId = this.getBucketDefinitionId(evaluated.source);
|
|
@@ -143,6 +169,7 @@ export abstract class PersistedBatch {
|
|
|
143
169
|
bucket: evaluated.bucket,
|
|
144
170
|
sourceTableId: options.table.id,
|
|
145
171
|
sourceKey: options.sourceKey,
|
|
172
|
+
subkey,
|
|
146
173
|
table: evaluated.table,
|
|
147
174
|
rowId: evaluated.id,
|
|
148
175
|
checksum: BigInt(checksum),
|
|
@@ -165,6 +192,7 @@ export abstract class PersistedBatch {
|
|
|
165
192
|
op_id,
|
|
166
193
|
sourceTableId: options.table.id,
|
|
167
194
|
sourceKey: options.sourceKey,
|
|
195
|
+
subkey,
|
|
168
196
|
table: bucket.table,
|
|
169
197
|
rowId: bucket.id,
|
|
170
198
|
checksum: dchecksum
|
|
@@ -201,6 +229,14 @@ export abstract class PersistedBatch {
|
|
|
201
229
|
protected abstract checkDefinitionId(definitionId: BucketDefinitionId | null): BucketDefinitionId;
|
|
202
230
|
protected abstract getBucketDefinitionId(bucketSource: BucketDataSource): BucketDefinitionId;
|
|
203
231
|
|
|
232
|
+
/**
|
|
233
|
+
* New bucket operations persist protocol subkeys during replication. Older
|
|
234
|
+
* V1 documents may not have one, so sync reads retain a fallback.
|
|
235
|
+
*/
|
|
236
|
+
protected persistedSubkey(table: storage.SourceTableId, key: storage.ReplicaId): string {
|
|
237
|
+
return replicaIdToSubkey(table, key);
|
|
238
|
+
}
|
|
239
|
+
|
|
204
240
|
protected get bucketDataCount(): number {
|
|
205
241
|
return this.bucketData.length;
|
|
206
242
|
}
|
|
@@ -218,17 +254,45 @@ export abstract class PersistedBatch {
|
|
|
218
254
|
bucket,
|
|
219
255
|
lastOp: op_id,
|
|
220
256
|
incrementCount: 1,
|
|
221
|
-
incrementBytes: bytes
|
|
257
|
+
incrementBytes: bytes,
|
|
258
|
+
incrementChunks: 0
|
|
222
259
|
});
|
|
223
260
|
}
|
|
224
261
|
}
|
|
225
262
|
|
|
263
|
+
/**
|
|
264
|
+
* V3 persists operations in chunks. Keep this separate from incrementBucket:
|
|
265
|
+
* operation counts are known while evaluating rows, while chunk counts and
|
|
266
|
+
* exact persisted bytes are only known after the writer has chunked a flush.
|
|
267
|
+
*/
|
|
268
|
+
protected incrementBucketPersistedChunk(definitionId: BucketDefinitionId, bucket: string, bytes: number) {
|
|
269
|
+
const key = `${definitionId ?? ''}:${bucket}`;
|
|
270
|
+
const existingState = this.bucketStates.get(key);
|
|
271
|
+
if (existingState != null) {
|
|
272
|
+
existingState.incrementChunks += 1;
|
|
273
|
+
existingState.incrementBytes += bytes;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* V3's compactor calculates byte deltas from persisted chunk metadata.
|
|
279
|
+
* Replace the writer's per-operation estimate with those exact sizes before
|
|
280
|
+
* flushing the corresponding bucket state.
|
|
281
|
+
*/
|
|
282
|
+
protected resetBucketPersistedBytes(definitionId: BucketDefinitionId, bucket: string) {
|
|
283
|
+
const state = this.bucketStates.get(`${definitionId ?? ''}:${bucket}`);
|
|
284
|
+
if (state != null) {
|
|
285
|
+
state.incrementBytes = 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
226
289
|
protected addBucketDataPut(options: {
|
|
227
290
|
op_id: InternalOpId;
|
|
228
291
|
bucketKey: BucketKey;
|
|
229
292
|
bucket: string;
|
|
230
293
|
sourceTableId: storage.SourceTable['id'];
|
|
231
294
|
sourceKey: storage.ReplicaId;
|
|
295
|
+
subkey: string;
|
|
232
296
|
table: string;
|
|
233
297
|
rowId: string;
|
|
234
298
|
checksum: bigint;
|
|
@@ -240,6 +304,7 @@ export abstract class PersistedBatch {
|
|
|
240
304
|
op: 'PUT',
|
|
241
305
|
source_table: mongoTableId(options.sourceTableId),
|
|
242
306
|
source_key: options.sourceKey,
|
|
307
|
+
subkey: options.subkey,
|
|
243
308
|
table: options.table,
|
|
244
309
|
row_id: options.rowId,
|
|
245
310
|
checksum: options.checksum,
|
|
@@ -252,6 +317,7 @@ export abstract class PersistedBatch {
|
|
|
252
317
|
bucketKey: BucketKey;
|
|
253
318
|
sourceTableId: storage.SourceTable['id'];
|
|
254
319
|
sourceKey: storage.ReplicaId;
|
|
320
|
+
subkey: string;
|
|
255
321
|
table: string;
|
|
256
322
|
rowId: string;
|
|
257
323
|
checksum: bigint;
|
|
@@ -262,6 +328,7 @@ export abstract class PersistedBatch {
|
|
|
262
328
|
op: 'REMOVE',
|
|
263
329
|
source_table: mongoTableId(options.sourceTableId),
|
|
264
330
|
source_key: options.sourceKey,
|
|
331
|
+
subkey: options.subkey,
|
|
265
332
|
table: options.table,
|
|
266
333
|
row_id: options.rowId,
|
|
267
334
|
checksum: options.checksum,
|
|
@@ -305,32 +372,32 @@ export abstract class PersistedBatch {
|
|
|
305
372
|
const replicationLag = Math.round((Date.now() - options.oldestUncommittedChange.getTime()) / 1000);
|
|
306
373
|
|
|
307
374
|
this.logger.info(
|
|
308
|
-
`Flushed ${this.bucketDataCount}
|
|
375
|
+
`Flushed: ${this.bucketDataCount} ops, ${this.bucketParameters.length} index entries, ${
|
|
309
376
|
this.currentDataCount
|
|
310
|
-
}
|
|
377
|
+
} records. ${Math.round(this.currentSize / 1024)}kb in ${duration}ms. Last op_id: ${this.debugLastOpId}. Replication lag: ${replicationLag}s`,
|
|
311
378
|
{
|
|
312
379
|
flushed: {
|
|
313
380
|
duration: duration,
|
|
314
381
|
size: this.currentSize,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
382
|
+
bucket_ops_count: this.bucketDataCount,
|
|
383
|
+
parameter_indexes_count: this.bucketParameters.length,
|
|
384
|
+
source_records_count: this.currentDataCount,
|
|
318
385
|
replication_lag_seconds: replicationLag
|
|
319
386
|
}
|
|
320
387
|
}
|
|
321
388
|
);
|
|
322
389
|
} else {
|
|
323
390
|
this.logger.info(
|
|
324
|
-
`Flushed ${this.bucketDataCount}
|
|
391
|
+
`Flushed: ${this.bucketDataCount} ops, ${this.bucketParameters.length} index entries, ${
|
|
325
392
|
this.currentDataCount
|
|
326
|
-
}
|
|
393
|
+
} records. ${Math.round(this.currentSize / 1024)}kb in ${duration}ms. Last op_id: ${this.debugLastOpId}`,
|
|
327
394
|
{
|
|
328
395
|
flushed: {
|
|
329
396
|
duration: duration,
|
|
330
397
|
size: this.currentSize,
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
398
|
+
bucket_ops_count: this.bucketDataCount,
|
|
399
|
+
parameter_indexes_count: this.bucketParameters.length,
|
|
400
|
+
source_records_count: this.currentDataCount
|
|
334
401
|
}
|
|
335
402
|
}
|
|
336
403
|
);
|
|
@@ -361,4 +428,5 @@ export interface BucketStateUpdate {
|
|
|
361
428
|
lastOp: InternalOpId;
|
|
362
429
|
incrementCount: number;
|
|
363
430
|
incrementBytes: number;
|
|
431
|
+
incrementChunks: number;
|
|
364
432
|
}
|