@powersync/service-module-mongodb-storage 0.0.0-dev-20260602073133 → 0.0.0-dev-20260803144812
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 +101 -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 +41 -16
- package/dist/storage/MongoBucketStorage.js +226 -71
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +56 -25
- package/dist/storage/implementation/MongoBucketBatch.js +110 -62
- 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 +17 -19
- package/dist/storage/implementation/MongoCompactor.js +80 -263
- 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 +7 -6
- package/dist/storage/implementation/MongoParameterCompactor.js +25 -2
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +13 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js +64 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +36 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.js +90 -0
- package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +25 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +123 -0
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
- package/dist/storage/implementation/MongoStorageProvider.js +39 -4
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +57 -29
- package/dist/storage/implementation/MongoSyncBucketStorage.js +87 -61
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.js +3 -3
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +2 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +171 -24
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +8 -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 +7 -2
- package/dist/storage/implementation/common/PersistedBatch.js +12 -0
- package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
- package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
- package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
- package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/db.js +17 -15
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +20 -13
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +9 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +61 -29
- 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 +11 -2
- package/dist/storage/implementation/v1/MongoCompactorV1.js +251 -2
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +18 -19
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +82 -58
- 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/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/MongoBucketBatchV3.d.ts +8 -4
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +309 -259
- 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 +40 -2
- package/dist/storage/implementation/v3/MongoCompactorV3.js +647 -23
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +67 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +351 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +45 -30
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +272 -140
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +6 -3
- package/dist/storage/implementation/v3/PersistedBatchV3.js +84 -21
- 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 +15 -13
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +32 -27
- 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 +77 -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/models.d.ts +101 -21
- package/dist/storage/implementation/v3/models.js +11 -28
- package/dist/storage/implementation/v3/models.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +21 -0
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +44 -0
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorage.d.ts +30 -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 +70 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +146 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.d.ts +42 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +285 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -0
- package/dist/storage/implementation/v3/source-table-utils.d.ts +55 -0
- package/dist/storage/implementation/v3/source-table-utils.js +214 -0
- package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
- package/dist/storage/storage-index.d.ts +4 -4
- package/dist/storage/storage-index.js +4 -4
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/types/types.d.ts +49 -3
- package/dist/types/types.js +48 -1
- 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 +128 -4
- package/dist/utils/util.js.map +1 -1
- package/package.json +12 -19
- package/src/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.ts +55 -0
- package/src/storage/MongoBucketStorage.ts +347 -88
- package/src/storage/implementation/MongoBucketBatch.ts +147 -82
- package/src/storage/implementation/MongoChecksums.ts +93 -153
- package/src/storage/implementation/MongoCompactor.ts +102 -316
- package/src/storage/implementation/MongoHydrationState.ts +61 -0
- package/src/storage/implementation/MongoParameterCompactor.ts +25 -6
- package/src/storage/implementation/MongoParsedSyncConfigSet.ts +88 -0
- package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
- package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +154 -0
- package/src/storage/implementation/MongoStorageProvider.ts +46 -4
- package/src/storage/implementation/MongoSyncBucketStorage.ts +121 -87
- package/src/storage/implementation/MongoSyncRulesLock.ts +7 -4
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +202 -28
- package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +9 -0
- package/src/storage/implementation/common/PersistedBatch.ts +19 -3
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
- package/src/storage/implementation/db.ts +19 -20
- package/src/storage/implementation/models.ts +20 -14
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +75 -37
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +154 -12
- package/src/storage/implementation/v1/MongoCompactorV1.ts +294 -9
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +142 -105
- 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 +11 -1
- package/src/storage/implementation/v3/BucketDataContextV3.ts +32 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +382 -321
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +250 -32
- package/src/storage/implementation/v3/MongoCompactorV3.ts +847 -32
- package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +482 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +359 -187
- package/src/storage/implementation/v3/PersistedBatchV3.ts +118 -37
- package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +46 -36
- package/src/storage/implementation/v3/bucket-format.ts +97 -0
- package/src/storage/implementation/v3/chunking.ts +40 -0
- package/src/storage/implementation/v3/models.ts +132 -55
- package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +54 -0
- package/src/storage/implementation/v3/object-storage/ObjectStorage.ts +29 -0
- package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +171 -0
- package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +343 -0
- package/src/storage/implementation/v3/source-table-utils.ts +392 -0
- package/src/storage/storage-index.ts +12 -4
- package/src/types/types.ts +58 -1
- package/src/utils/test-utils.ts +8 -3
- package/src/utils/util.ts +185 -6
- package/test/src/__snapshots__/storage.test.ts.snap +0 -75
- package/test/src/__snapshots__/storage_sync.test.ts.snap +35 -617
- package/test/src/cleanup-stopped-sync-configs.test.ts +671 -0
- package/test/src/helpers/MemoryObjectStorage.ts +53 -0
- package/test/src/helpers/s3TestFactory.ts +69 -0
- package/test/src/object_storage_lifecycle.test.ts +59 -0
- package/test/src/setup.ts +6 -1
- package/test/src/storage.test.ts +227 -13
- package/test/src/storage_compacting.test.ts +1951 -12
- package/test/src/storage_object_storage_inline_threshold.test.ts +116 -0
- package/test/src/storage_s3_checksums.test.ts +191 -0
- package/test/src/storage_s3_compaction_lifecycle.test.ts +417 -0
- package/test/src/storage_s3_reading.test.ts +526 -0
- package/test/src/storage_s3_writing.test.ts +135 -0
- package/test/src/storage_sync.test.ts +1180 -73
- package/test/src/storeCurrentData.test.ts +227 -0
- package/test/src/util.ts +2 -1
- package/tsconfig.json +1 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -15
- package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
- package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
- package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -67
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -29
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -76
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
- package/dist/storage/implementation/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/MongoParameterCompactorV3.d.ts +0 -9
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +0 -18
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +0 -1
- package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -4
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
- package/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/MongoParameterCompactorV3.ts +0 -24
- package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -11
- package/src/storage/implementation/v3/SingleBucketStoreV3.ts +0 -68
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReplicationAssertionError } from '@powersync/lib-services-framework';
|
|
2
|
-
import { ColumnDescriptor, SourceTable, storage } from '@powersync/service-core';
|
|
2
|
+
import { ColumnDescriptor, InternalOpId, SourceTable, storage } from '@powersync/service-core';
|
|
3
3
|
import * as bson from 'bson';
|
|
4
4
|
import { mongoTableId } from '../../../utils/util.js';
|
|
5
5
|
import { calculateCheckpointState } from '../CheckpointState.js';
|
|
@@ -9,22 +9,35 @@ import { SourceRecordStore } from '../common/SourceRecordStore.js';
|
|
|
9
9
|
import { PersistedBatchV1 } from './PersistedBatchV1.js';
|
|
10
10
|
import { SourceRecordStoreV1 } from './SourceRecordStoreV1.js';
|
|
11
11
|
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
12
|
-
import {
|
|
12
|
+
import { SyncRuleDocumentV1 } from './models.js';
|
|
13
13
|
|
|
14
14
|
export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
15
|
-
declare
|
|
15
|
+
declare readonly db: VersionedPowerSyncMongoV1;
|
|
16
16
|
|
|
17
17
|
private readonly store: SourceRecordStore;
|
|
18
18
|
private needsActivation = true;
|
|
19
19
|
private lastWaitingLogThrottled = 0;
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The last persisted op that is not yet covered by a checkpoint, if any.
|
|
23
|
+
*
|
|
24
|
+
* Seeded from the persisted keepalive_op, so that ops persisted before a restart can still be
|
|
25
|
+
* included in the next checkpoint.
|
|
26
|
+
*/
|
|
27
|
+
private persisted_op: InternalOpId | null = null;
|
|
28
|
+
|
|
21
29
|
constructor(options: MongoBucketBatchOptions) {
|
|
22
30
|
super(options);
|
|
23
|
-
this.
|
|
31
|
+
this.persisted_op = options.keepaliveOp ?? null;
|
|
32
|
+
this.store = new SourceRecordStoreV1(this.db, this.replicationStreamId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected override recordPersistedOp(lastOp: InternalOpId): void {
|
|
36
|
+
this.persisted_op = lastOp;
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
protected createPersistedBatch(writtenSize: number): PersistedBatch {
|
|
27
|
-
return new PersistedBatchV1(this.db, this.
|
|
40
|
+
return new PersistedBatchV1(this.db, this.replicationStreamId, this.mapping, writtenSize, {
|
|
28
41
|
logger: this.logger
|
|
29
42
|
});
|
|
30
43
|
}
|
|
@@ -32,15 +45,14 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
32
45
|
protected get sourceRecordStore(): SourceRecordStore {
|
|
33
46
|
return this.store;
|
|
34
47
|
}
|
|
35
|
-
|
|
36
48
|
protected async cleanupDroppedSourceTables(_tables: SourceTable[]) {
|
|
37
49
|
// No-op for V1: source records live in a shared collection.
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
async resolveTables(options: storage.ResolveTablesOptions): Promise<storage.ResolveTablesResult> {
|
|
41
|
-
const syncRules = options.
|
|
53
|
+
const syncRules = options.parsedSyncConfig?.hydratedSyncConfig ?? this.sync_rules;
|
|
42
54
|
const { connection_id, source } = options;
|
|
43
|
-
const { schema, name, objectId, replicaIdColumns, connectionTag } = source;
|
|
55
|
+
const { schema, name, objectId, replicaIdColumns, connectionTag, sendsCompleteRows } = source;
|
|
44
56
|
|
|
45
57
|
const normalizedReplicaIdColumns = replicaIdColumns.map((column) => ({
|
|
46
58
|
name: column.name,
|
|
@@ -50,9 +62,9 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
50
62
|
|
|
51
63
|
let result: storage.ResolveTablesResult | null = null;
|
|
52
64
|
await this.db.client.withSession(async (session) => {
|
|
53
|
-
const col = this.db.
|
|
65
|
+
const col = this.db.sourceTablesV1(this.replicationStreamId);
|
|
54
66
|
const filter: any = {
|
|
55
|
-
group_id: this.
|
|
67
|
+
group_id: this.replicationStreamId,
|
|
56
68
|
connection_id,
|
|
57
69
|
schema_name: schema,
|
|
58
70
|
table_name: name,
|
|
@@ -66,7 +78,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
66
78
|
if (doc == null) {
|
|
67
79
|
doc = {
|
|
68
80
|
_id: options.idGenerator ? (options.idGenerator() as bson.ObjectId) : new bson.ObjectId(),
|
|
69
|
-
group_id: this.
|
|
81
|
+
group_id: this.replicationStreamId,
|
|
70
82
|
connection_id,
|
|
71
83
|
relation_id: objectId,
|
|
72
84
|
schema_name: schema,
|
|
@@ -90,6 +102,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
90
102
|
sourceTable.syncEvent = syncRules.tableTriggersEvent(source);
|
|
91
103
|
sourceTable.syncData = sourceTable.bucketDataSources.length > 0;
|
|
92
104
|
sourceTable.syncParameters = sourceTable.parameterLookupSources.length > 0;
|
|
105
|
+
sourceTable.storeCurrentData = sendsCompleteRows !== true;
|
|
93
106
|
sourceTable.snapshotStatus =
|
|
94
107
|
doc.snapshot_status == null
|
|
95
108
|
? undefined
|
|
@@ -106,7 +119,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
106
119
|
const truncate = await col
|
|
107
120
|
.find(
|
|
108
121
|
{
|
|
109
|
-
group_id: this.
|
|
122
|
+
group_id: this.replicationStreamId,
|
|
110
123
|
connection_id,
|
|
111
124
|
_id: { $ne: doc._id },
|
|
112
125
|
$or: truncateFilter
|
|
@@ -144,13 +157,13 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
async getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null> {
|
|
147
|
-
const doc =
|
|
160
|
+
const doc = await this.db.sourceTablesV1(this.replicationStreamId).findOne(
|
|
148
161
|
{
|
|
149
|
-
group_id: this.
|
|
162
|
+
group_id: this.replicationStreamId,
|
|
150
163
|
_id: mongoTableId(table.id)
|
|
151
164
|
},
|
|
152
165
|
{ session: this.session }
|
|
153
|
-
)
|
|
166
|
+
);
|
|
154
167
|
if (doc == null) {
|
|
155
168
|
return null;
|
|
156
169
|
}
|
|
@@ -190,6 +203,8 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
190
203
|
|
|
191
204
|
await this.flush(options);
|
|
192
205
|
|
|
206
|
+
using _ = this.tracer.span('storage', 'commit');
|
|
207
|
+
|
|
193
208
|
const now = new Date();
|
|
194
209
|
|
|
195
210
|
await this.db.write_checkpoints.updateMany(
|
|
@@ -242,7 +257,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
242
257
|
};
|
|
243
258
|
|
|
244
259
|
const preUpdateDocument = (await this.db.sync_rules.findOneAndUpdate(
|
|
245
|
-
{ _id: this.
|
|
260
|
+
{ _id: this.replicationStreamId },
|
|
246
261
|
[
|
|
247
262
|
{
|
|
248
263
|
$set: {
|
|
@@ -295,7 +310,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
295
310
|
|
|
296
311
|
if (preUpdateDocument == null) {
|
|
297
312
|
throw new ReplicationAssertionError(
|
|
298
|
-
'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.
|
|
313
|
+
'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.replicationStreamId
|
|
299
314
|
);
|
|
300
315
|
}
|
|
301
316
|
|
|
@@ -329,7 +344,6 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
329
344
|
await this.autoActivate(lsn);
|
|
330
345
|
await this.db.notifyCheckpoint();
|
|
331
346
|
this.persisted_op = null;
|
|
332
|
-
this.last_checkpoint_lsn = lsn;
|
|
333
347
|
if (checkpointState.newLastCheckpoint != null) {
|
|
334
348
|
await this.sourceRecordStore.postCommitCleanup(checkpointState.newLastCheckpoint, this.logger);
|
|
335
349
|
}
|
|
@@ -346,23 +360,30 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
346
360
|
}
|
|
347
361
|
|
|
348
362
|
async setResumeLsn(lsn: string): Promise<void> {
|
|
363
|
+
using _ = this.tracer.span('storage', 'set_resume_lsn');
|
|
349
364
|
await this.db.sync_rules.updateOne(
|
|
350
365
|
{
|
|
351
|
-
_id: this.
|
|
366
|
+
_id: this.replicationStreamId
|
|
352
367
|
},
|
|
353
368
|
{
|
|
354
369
|
$set: {
|
|
355
370
|
snapshot_lsn: lsn
|
|
356
371
|
}
|
|
357
372
|
},
|
|
358
|
-
{
|
|
373
|
+
{
|
|
374
|
+
session: this.session,
|
|
375
|
+
// Losing occasional resume LSN is fine. That may mean reprocessing
|
|
376
|
+
// some source changes in some edge cases, which is not an issue since
|
|
377
|
+
// changes are processed in an idempotent way.
|
|
378
|
+
writeConcern: { w: 1 }
|
|
379
|
+
}
|
|
359
380
|
);
|
|
360
381
|
}
|
|
361
382
|
|
|
362
383
|
async markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void> {
|
|
363
384
|
await this.db.sync_rules.updateOne(
|
|
364
385
|
{
|
|
365
|
-
_id: this.
|
|
386
|
+
_id: this.replicationStreamId
|
|
366
387
|
},
|
|
367
388
|
{
|
|
368
389
|
$set: {
|
|
@@ -380,17 +401,24 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
380
401
|
async markSnapshotDone(no_checkpoint_before_lsn: string, options?: { throwOnConflict?: boolean }): Promise<void> {
|
|
381
402
|
await this.withTransaction(async () => {
|
|
382
403
|
// Protect against race conditions
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
404
|
+
const blockingTables = await this.db
|
|
405
|
+
.sourceTablesV1(this.replicationStreamId)
|
|
406
|
+
.find(
|
|
407
|
+
{
|
|
408
|
+
group_id: this.replicationStreamId,
|
|
409
|
+
snapshot_done: false
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
session: this.session,
|
|
413
|
+
projection: { schema_name: 1, table_name: 1 }
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
.toArray();
|
|
417
|
+
|
|
418
|
+
if (blockingTables.length > 0) {
|
|
391
419
|
if (options?.throwOnConflict ?? true) {
|
|
392
420
|
throw new ReplicationAssertionError(
|
|
393
|
-
`Cannot mark snapshot done while
|
|
421
|
+
`Cannot mark snapshot done while source tables still require snapshotting. Tables: ${blockingTables.map((t) => `${t.schema_name}.${t.table_name}`).join(', ')}`
|
|
394
422
|
);
|
|
395
423
|
} else {
|
|
396
424
|
return;
|
|
@@ -404,7 +432,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
404
432
|
async markTableSnapshotRequired(_table: storage.SourceTable): Promise<void> {
|
|
405
433
|
await this.db.sync_rules.updateOne(
|
|
406
434
|
{
|
|
407
|
-
_id: this.
|
|
435
|
+
_id: this.replicationStreamId
|
|
408
436
|
},
|
|
409
437
|
{
|
|
410
438
|
$set: {
|
|
@@ -423,7 +451,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
423
451
|
const ids = tables.map((table) => mongoTableId(table.id));
|
|
424
452
|
|
|
425
453
|
await this.withTransaction(async () => {
|
|
426
|
-
await this.db.
|
|
454
|
+
await this.db.sourceTablesV1(this.replicationStreamId).updateMany(
|
|
427
455
|
{ _id: { $in: ids } },
|
|
428
456
|
{
|
|
429
457
|
$set: {
|
|
@@ -439,7 +467,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
439
467
|
if (no_checkpoint_before_lsn != null) {
|
|
440
468
|
await this.db.sync_rules.updateOne(
|
|
441
469
|
{
|
|
442
|
-
_id: this.
|
|
470
|
+
_id: this.replicationStreamId
|
|
443
471
|
},
|
|
444
472
|
{
|
|
445
473
|
$set: {
|
|
@@ -467,12 +495,20 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
467
495
|
|
|
468
496
|
const session = this.session;
|
|
469
497
|
let activated = false;
|
|
498
|
+
let needsFutureActivationCheck = true;
|
|
470
499
|
await session.withTransaction(async () => {
|
|
471
|
-
|
|
500
|
+
// Reset on transaction retries.
|
|
501
|
+
activated = false;
|
|
502
|
+
needsFutureActivationCheck = true;
|
|
503
|
+
|
|
504
|
+
const doc = (await this.db.sync_rules.findOne(
|
|
505
|
+
{ _id: this.replicationStreamId },
|
|
506
|
+
{ session }
|
|
507
|
+
)) as SyncRuleDocumentV1;
|
|
472
508
|
if (doc && doc.state == storage.SyncRuleState.PROCESSING && doc.snapshot_done && doc.last_checkpoint != null) {
|
|
473
509
|
await this.db.sync_rules.updateOne(
|
|
474
510
|
{
|
|
475
|
-
_id: this.
|
|
511
|
+
_id: this.replicationStreamId
|
|
476
512
|
},
|
|
477
513
|
{
|
|
478
514
|
$set: {
|
|
@@ -484,7 +520,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
484
520
|
|
|
485
521
|
await this.db.sync_rules.updateMany(
|
|
486
522
|
{
|
|
487
|
-
_id: { $ne: this.
|
|
523
|
+
_id: { $ne: this.replicationStreamId },
|
|
488
524
|
state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
|
|
489
525
|
},
|
|
490
526
|
{
|
|
@@ -496,13 +532,15 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
|
|
|
496
532
|
);
|
|
497
533
|
activated = true;
|
|
498
534
|
} else if (doc?.state != storage.SyncRuleState.PROCESSING) {
|
|
499
|
-
|
|
535
|
+
needsFutureActivationCheck = false;
|
|
500
536
|
}
|
|
501
537
|
});
|
|
502
538
|
if (activated) {
|
|
503
539
|
this.logger.info(`Activated new replication stream at ${lsn}`);
|
|
504
540
|
await this.db.notifyCheckpoint();
|
|
505
541
|
this.needsActivation = false;
|
|
542
|
+
} else if (!needsFutureActivationCheck) {
|
|
543
|
+
this.needsActivation = false;
|
|
506
544
|
}
|
|
507
545
|
}
|
|
508
546
|
}
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
1
2
|
import {
|
|
3
|
+
addPartialChecksums,
|
|
2
4
|
bson,
|
|
3
5
|
BucketChecksum,
|
|
4
6
|
FetchPartialBucketChecksum,
|
|
5
7
|
InternalOpId,
|
|
6
|
-
|
|
8
|
+
isPartialChecksum,
|
|
9
|
+
PartialChecksumMap,
|
|
10
|
+
PartialOrFullChecksum
|
|
7
11
|
} from '@powersync/service-core';
|
|
8
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
checksumFromAggregate,
|
|
14
|
+
DEFAULT_OPERATION_BATCH_LIMIT,
|
|
15
|
+
FetchPartialBucketChecksumByBucket,
|
|
16
|
+
MongoChecksums,
|
|
17
|
+
MongoChecksumSessionContext
|
|
18
|
+
} from '../MongoChecksums.js';
|
|
9
19
|
import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
|
|
10
|
-
|
|
11
20
|
export class MongoChecksumsV1 extends MongoChecksums {
|
|
12
21
|
declare protected readonly db: VersionedPowerSyncMongoV1;
|
|
13
22
|
|
|
14
23
|
async computePartialChecksumsDirectByBucket(
|
|
15
|
-
batch: FetchPartialBucketChecksumByBucket[]
|
|
24
|
+
batch: FetchPartialBucketChecksumByBucket[],
|
|
25
|
+
context: MongoChecksumSessionContext
|
|
16
26
|
): Promise<PartialChecksumMap> {
|
|
17
|
-
|
|
27
|
+
const collection = this.db.bucketDataV1;
|
|
28
|
+
const createFilter = (request: FetchPartialBucketChecksumByBucket) => ({
|
|
18
29
|
_id: {
|
|
19
30
|
$gt: {
|
|
20
31
|
g: this.group_id,
|
|
@@ -27,11 +38,134 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
27
38
|
o: request.end
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
|
-
})
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const batchLimit = this.options?.operationBatchLimit ?? DEFAULT_OPERATION_BATCH_LIMIT;
|
|
44
|
+
|
|
45
|
+
// Map requests by bucket. We adjust this as we get partial results.
|
|
46
|
+
let requests = new Map<string, FetchPartialBucketChecksumByBucket>();
|
|
47
|
+
for (let request of batch) {
|
|
48
|
+
requests.set(request.bucket, request);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const partialChecksums = new Map<string, PartialOrFullChecksum>();
|
|
52
|
+
|
|
53
|
+
while (requests.size > 0) {
|
|
54
|
+
const filters = Array.from(requests.values(), createFilter);
|
|
55
|
+
|
|
56
|
+
// Historically, checksum may be stored as 'int' or 'double'.
|
|
57
|
+
// More recently, this should be a 'long'.
|
|
58
|
+
// $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
|
|
59
|
+
const checksumLong = this.storageConfig.longChecksums ? '$checksum' : { $toLong: '$checksum' };
|
|
60
|
+
|
|
61
|
+
// Aggregate over a max of `batchLimit` operations at a time.
|
|
62
|
+
// Let's say we have 3 buckets (A, B, C), each with 10 operations, and our batch limit is 12.
|
|
63
|
+
// Then we'll do three batches:
|
|
64
|
+
// 1. Query: A[1-end], B[1-end], C[1-end]
|
|
65
|
+
// Returns: A[1-10], B[1-2]
|
|
66
|
+
// 2. Query: B[3-end], C[1-end]
|
|
67
|
+
// Returns: B[3-10], C[1-4]
|
|
68
|
+
// 3. Query: C[5-end]
|
|
69
|
+
// Returns: C[5-10]
|
|
70
|
+
const aggregate = await collection
|
|
71
|
+
.aggregate(
|
|
72
|
+
[
|
|
73
|
+
{
|
|
74
|
+
$match: {
|
|
75
|
+
$or: filters
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
// sort and limit _before_ grouping
|
|
79
|
+
{ $sort: { _id: 1 } },
|
|
80
|
+
{ $limit: batchLimit },
|
|
81
|
+
{
|
|
82
|
+
$group: {
|
|
83
|
+
_id: '$_id.b',
|
|
84
|
+
checksum_total: { $sum: checksumLong },
|
|
85
|
+
count: { $sum: 1 },
|
|
86
|
+
has_clear_op: {
|
|
87
|
+
$max: {
|
|
88
|
+
$cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
last_op: { $max: '$_id.o' }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
// Sort the aggregated results (100 max, so should be fast).
|
|
95
|
+
// This is important to identify which buckets we have partial data for.
|
|
96
|
+
{ $sort: { _id: 1 } }
|
|
97
|
+
],
|
|
98
|
+
{
|
|
99
|
+
...context.readOptions,
|
|
100
|
+
maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
.toArray()
|
|
104
|
+
.catch((e) => {
|
|
105
|
+
throw lib_mongo.mapQueryError(e, 'while reading checksums');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
let batchCount = 0;
|
|
109
|
+
let limitReached = false;
|
|
110
|
+
for (let doc of aggregate) {
|
|
111
|
+
const bucket = doc._id;
|
|
112
|
+
const checksum = checksumFromAggregate(doc);
|
|
113
|
+
|
|
114
|
+
const existing = partialChecksums.get(bucket);
|
|
115
|
+
if (existing != null) {
|
|
116
|
+
partialChecksums.set(bucket, addPartialChecksums(bucket, existing, checksum));
|
|
117
|
+
} else {
|
|
118
|
+
partialChecksums.set(bucket, checksum);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
batchCount += doc.count;
|
|
122
|
+
if (batchCount == batchLimit) {
|
|
123
|
+
// Limit reached. Request more in the next batch.
|
|
124
|
+
// Note that this only affects the _last_ bucket in a batch.
|
|
125
|
+
limitReached = true;
|
|
126
|
+
const req = requests.get(bucket);
|
|
127
|
+
requests.set(bucket, {
|
|
128
|
+
...req!,
|
|
129
|
+
start: doc.last_op
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
// All done for this bucket
|
|
133
|
+
requests.delete(bucket);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!limitReached) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return new Map<string, PartialOrFullChecksum>(
|
|
142
|
+
batch.map((request) => {
|
|
143
|
+
const bucket = request.bucket;
|
|
144
|
+
let partialChecksum = partialChecksums.get(bucket);
|
|
145
|
+
// Could be null if we got no data
|
|
146
|
+
if (partialChecksum == null) {
|
|
147
|
+
partialChecksum = {
|
|
148
|
+
bucket,
|
|
149
|
+
partialCount: 0,
|
|
150
|
+
partialChecksum: 0
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (request.start == null && isPartialChecksum(partialChecksum)) {
|
|
154
|
+
partialChecksum = {
|
|
155
|
+
bucket,
|
|
156
|
+
count: partialChecksum.partialCount,
|
|
157
|
+
checksum: partialChecksum.partialChecksum
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return [bucket, partialChecksum];
|
|
162
|
+
})
|
|
163
|
+
);
|
|
31
164
|
}
|
|
32
165
|
|
|
33
166
|
protected async fetchPreStates(
|
|
34
|
-
batch: FetchPartialBucketChecksum[]
|
|
167
|
+
batch: FetchPartialBucketChecksum[],
|
|
168
|
+
context: MongoChecksumSessionContext
|
|
35
169
|
): Promise<Map<string, { opId: InternalOpId; checksum: BucketChecksum }>> {
|
|
36
170
|
const preFilters = batch
|
|
37
171
|
.filter((request) => request.start == null)
|
|
@@ -49,9 +183,14 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
49
183
|
}
|
|
50
184
|
|
|
51
185
|
const states = await this.db.bucketStateV1
|
|
52
|
-
.find(
|
|
53
|
-
|
|
54
|
-
|
|
186
|
+
.find(
|
|
187
|
+
{
|
|
188
|
+
$or: preFilters
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
...context.readOptions
|
|
192
|
+
}
|
|
193
|
+
)
|
|
55
194
|
.toArray();
|
|
56
195
|
|
|
57
196
|
for (const state of states) {
|
|
@@ -69,7 +208,10 @@ export class MongoChecksumsV1 extends MongoChecksums {
|
|
|
69
208
|
return preStates;
|
|
70
209
|
}
|
|
71
210
|
|
|
72
|
-
protected async computePartialChecksumsInternal(
|
|
73
|
-
|
|
211
|
+
protected async computePartialChecksumsInternal(
|
|
212
|
+
batch: FetchPartialBucketChecksum[],
|
|
213
|
+
context: MongoChecksumSessionContext
|
|
214
|
+
): Promise<PartialChecksumMap> {
|
|
215
|
+
return this.computePartialChecksumsDirectByBucket(batch, context);
|
|
74
216
|
}
|
|
75
217
|
}
|