@powersync/service-module-mongodb-storage 0.15.4 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +1 -1
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -1
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +2 -2
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +8 -6
- package/dist/storage/MongoBucketStorage.js +153 -66
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +15 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
- package/dist/storage/implementation/CheckpointState.d.ts +20 -0
- package/dist/storage/implementation/CheckpointState.js +31 -0
- package/dist/storage/implementation/CheckpointState.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.d.ts +48 -35
- package/dist/storage/implementation/MongoBucketBatch.js +118 -379
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
- package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
- package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
- package/dist/storage/implementation/MongoChecksums.d.ts +29 -17
- package/dist/storage/implementation/MongoChecksums.js +13 -72
- package/dist/storage/implementation/MongoChecksums.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +98 -58
- package/dist/storage/implementation/MongoCompactor.js +229 -296
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParameterCompactor.d.ts +11 -6
- package/dist/storage/implementation/MongoParameterCompactor.js +11 -8
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
- package/dist/storage/implementation/MongoPersistedSyncRules.js +67 -0
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +22 -5
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +56 -13
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +61 -32
- package/dist/storage/implementation/MongoSyncBucketStorage.js +85 -523
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +10 -4
- package/dist/storage/implementation/MongoSyncRulesLock.js +19 -13
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/OperationBatch.js +1 -1
- package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +14 -0
- package/dist/storage/implementation/SyncRuleStateUpdate.js +36 -0
- package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -0
- package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
- package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
- package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
- package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
- package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
- package/dist/storage/implementation/common/PersistedBatch.js +237 -0
- package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
- package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
- package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
- package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
- package/dist/storage/implementation/common/SourceRecordStore.d.ts +35 -0
- package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
- package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
- package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
- package/dist/storage/implementation/db.d.ts +41 -36
- package/dist/storage/implementation/db.js +77 -99
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +79 -66
- package/dist/storage/implementation/models.js +20 -1
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +27 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +407 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
- package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +50 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +354 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +25 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
- package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
- package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
- package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
- package/dist/storage/implementation/v1/models.d.ts +45 -0
- package/dist/storage/implementation/v1/models.js +37 -0
- package/dist/storage/implementation/v1/models.js.map +1 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +30 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +463 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
- package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +4 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
- package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +63 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +508 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +28 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
- package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
- package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +22 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +74 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +101 -0
- package/dist/storage/implementation/v3/models.js +34 -0
- package/dist/storage/implementation/v3/models.js.map +1 -0
- package/dist/storage/storage-index.d.ts +6 -3
- package/dist/storage/storage-index.js +6 -3
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/utils/util.d.ts +10 -3
- package/dist/utils/util.js +24 -3
- package/dist/utils/util.js.map +1 -1
- package/package.json +9 -9
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -7
- package/src/storage/MongoBucketStorage.ts +254 -99
- package/src/storage/implementation/BucketDefinitionMapping.ts +75 -0
- package/src/storage/implementation/CheckpointState.ts +59 -0
- package/src/storage/implementation/MongoBucketBatch.ts +182 -490
- package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
- package/src/storage/implementation/MongoChecksums.ts +53 -75
- package/src/storage/implementation/MongoCompactor.ts +374 -404
- package/src/storage/implementation/MongoParameterCompactor.ts +37 -24
- package/src/storage/implementation/MongoPersistedSyncRules.ts +82 -0
- package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +78 -16
- package/src/storage/implementation/MongoSyncBucketStorage.ts +179 -628
- package/src/storage/implementation/MongoSyncRulesLock.ts +20 -16
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +3 -1
- package/src/storage/implementation/OperationBatch.ts +1 -1
- package/src/storage/implementation/SyncRuleStateUpdate.ts +38 -0
- package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
- package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
- package/src/storage/implementation/common/PersistedBatch.ts +364 -0
- package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
- package/src/storage/implementation/common/SourceRecordStore.ts +48 -0
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
- package/src/storage/implementation/db.ts +110 -131
- package/src/storage/implementation/models.ts +102 -79
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +509 -0
- package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
- package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
- package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +543 -0
- package/src/storage/implementation/v1/PersistedBatchV1.ts +229 -0
- package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
- package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
- package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
- package/src/storage/implementation/v1/models.ts +99 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +607 -0
- package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
- package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
- package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
- package/src/storage/implementation/v3/MongoParameterLookupV3.ts +11 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +678 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +317 -0
- package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
- package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +117 -0
- package/src/storage/implementation/v3/models.ts +164 -0
- package/src/storage/storage-index.ts +6 -3
- package/src/utils/util.ts +34 -5
- package/test/src/storage_compacting.test.ts +57 -29
- package/test/src/storage_sync.test.ts +767 -5
- package/test/src/storeCurrentData.test.ts +211 -0
- package/test/tsconfig.json +0 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/PersistedBatch.d.ts +0 -71
- package/dist/storage/implementation/PersistedBatch.js +0 -354
- package/dist/storage/implementation/PersistedBatch.js.map +0 -1
- package/src/storage/implementation/PersistedBatch.ts +0 -432
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { BucketDefinitionId, ParameterIndexId } from '@powersync/service-sync-rules';
|
|
3
|
+
import { BaseVersionedPowerSyncMongo } from '../common/VersionedPowerSyncMongoBase.js';
|
|
4
|
+
import { CommonSourceTableDocument } from '../models.js';
|
|
5
|
+
import {
|
|
6
|
+
BucketDataDocumentV3,
|
|
7
|
+
BucketParameterDocumentV3,
|
|
8
|
+
BucketStateDocumentV3,
|
|
9
|
+
CurrentDataDocumentV3,
|
|
10
|
+
SourceTableDocumentV3,
|
|
11
|
+
SyncConfigDefinition
|
|
12
|
+
} from './models.js';
|
|
13
|
+
|
|
14
|
+
export class VersionedPowerSyncMongoV3 extends BaseVersionedPowerSyncMongo {
|
|
15
|
+
get syncConfigDefinitions() {
|
|
16
|
+
return this.db.collection<SyncConfigDefinition>('sync_config');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
sourceRecordsV3(replicationStreamId: number, sourceTableId: mongo.ObjectId): mongo.Collection<CurrentDataDocumentV3> {
|
|
20
|
+
const collectionName = this.sourceRecordsCollectionName(replicationStreamId, sourceTableId);
|
|
21
|
+
return this.db.collection<CurrentDataDocumentV3>(collectionName);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async listSourceRecordCollectionsV3(replicationStreamId: number): Promise<mongo.Collection<CurrentDataDocumentV3>[]> {
|
|
25
|
+
return this.listCollectionsByPrefix<CurrentDataDocumentV3>(`source_records_${replicationStreamId}_`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async initializeSourceRecordsCollection(replicationStreamId: number, sourceTableId: mongo.ObjectId) {
|
|
29
|
+
await this.sourceRecordsV3(replicationStreamId, sourceTableId).createIndex(
|
|
30
|
+
{
|
|
31
|
+
pending_delete: 1
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
partialFilterExpression: { pending_delete: { $exists: true } },
|
|
35
|
+
name: 'pending_delete'
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
commonSourceTables(replicationStreamId: number): mongo.Collection<CommonSourceTableDocument> {
|
|
41
|
+
return this.sourceTablesV3(replicationStreamId) as mongo.Collection<CommonSourceTableDocument>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
bucketStateV3(replicationStreamId: number): mongo.Collection<BucketStateDocumentV3> {
|
|
45
|
+
return this.db.collection(`bucket_state_${replicationStreamId}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
parameterIndexV3(
|
|
49
|
+
replicationStreamId: number,
|
|
50
|
+
indexId: ParameterIndexId
|
|
51
|
+
): mongo.Collection<BucketParameterDocumentV3> {
|
|
52
|
+
return this.db.collection(`parameter_index_${replicationStreamId}_${indexId}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
sourceTablesV3(replicationStreamId: number): mongo.Collection<SourceTableDocumentV3> {
|
|
56
|
+
return this.db.collection<SourceTableDocumentV3>(this.sourceTableCollectionName(replicationStreamId));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async initializeStreamStorage(replicationStreamId: number) {
|
|
60
|
+
const sourceTables = this.sourceTablesV3(replicationStreamId);
|
|
61
|
+
const bucketState = this.bucketStateV3(replicationStreamId);
|
|
62
|
+
await sourceTables.createIndex(
|
|
63
|
+
{
|
|
64
|
+
connection_id: 1,
|
|
65
|
+
schema_name: 1,
|
|
66
|
+
table_name: 1,
|
|
67
|
+
relation_id: 1
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'source_lookup'
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
await sourceTables.createIndex(
|
|
74
|
+
{
|
|
75
|
+
latest_pending_delete: 1
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
partialFilterExpression: { latest_pending_delete: { $exists: true } },
|
|
79
|
+
name: 'latest_pending_delete'
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
await bucketState.createIndex(
|
|
83
|
+
{
|
|
84
|
+
last_op: 1
|
|
85
|
+
},
|
|
86
|
+
{ name: 'bucket_updates', unique: true }
|
|
87
|
+
);
|
|
88
|
+
await bucketState.createIndex(
|
|
89
|
+
{
|
|
90
|
+
'estimate_since_compact.count': -1
|
|
91
|
+
},
|
|
92
|
+
{ name: 'dirty_count' }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
bucketDataV3(replicationStreamId: number, definitionId: BucketDefinitionId) {
|
|
97
|
+
return this.db.collection<BucketDataDocumentV3>(`bucket_data_${replicationStreamId}_${definitionId}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
listBucketDataCollectionsV3(replicationStreamId: number) {
|
|
101
|
+
return this.upstream.listBucketDataCollectionsV3(replicationStreamId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async listParameterIndexCollectionsV3(
|
|
105
|
+
replicationStreamId: number
|
|
106
|
+
): Promise<{ collection: mongo.Collection<BucketParameterDocumentV3>; indexId: ParameterIndexId }[]> {
|
|
107
|
+
const prefix = `parameter_index_${replicationStreamId}_`;
|
|
108
|
+
const collections = await this.db.listCollections({ name: new RegExp(`^${prefix}`) }, { nameOnly: true }).toArray();
|
|
109
|
+
|
|
110
|
+
return collections
|
|
111
|
+
.filter((collection) => collection.name.startsWith(prefix))
|
|
112
|
+
.map((collection) => ({
|
|
113
|
+
collection: this.db.collection<BucketParameterDocumentV3>(collection.name),
|
|
114
|
+
indexId: collection.name.slice(prefix.length)
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { InternalOpId, SerializedSyncPlan, SyncRuleState } from '@powersync/service-core';
|
|
2
|
+
import { BucketDefinitionId, ParameterIndexId } from '@powersync/service-sync-rules';
|
|
3
|
+
import * as bson from 'bson';
|
|
4
|
+
import { BucketDataDoc, BucketKey } from '../common/BucketDataDoc.js';
|
|
5
|
+
import {
|
|
6
|
+
BucketDataDocumentBase,
|
|
7
|
+
BucketDataKey,
|
|
8
|
+
BucketParameterDocumentBase,
|
|
9
|
+
BucketStateDocumentBase,
|
|
10
|
+
CurrentBucket,
|
|
11
|
+
ReplicaId,
|
|
12
|
+
SourceTableDocument,
|
|
13
|
+
SourceTableKey,
|
|
14
|
+
SyncRuleCheckpointFields,
|
|
15
|
+
SyncRuleDocumentBase,
|
|
16
|
+
TaggedBucketParameterDocument
|
|
17
|
+
} from '../models.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Embedded in sync_rules.sync_configs.
|
|
21
|
+
*/
|
|
22
|
+
export interface SyncRuleConfigStateV3 extends SyncRuleCheckpointFields<bigint | null> {
|
|
23
|
+
_id: bson.ObjectId;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* If false, we cannot create any checkpoints.
|
|
27
|
+
*/
|
|
28
|
+
snapshot_done: boolean;
|
|
29
|
+
|
|
30
|
+
state: SyncRuleState;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents the state of a replication stream, in the sync_rules collection.
|
|
35
|
+
*
|
|
36
|
+
* Differences from V1:
|
|
37
|
+
* 1. The static config is moved into a separate sync_configs collection.
|
|
38
|
+
* 2. The same replication stream may be shared by multiple sync config instances.
|
|
39
|
+
*/
|
|
40
|
+
export interface ReplicationStreamDocumentV3 extends SyncRuleDocumentBase {
|
|
41
|
+
storage_version: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* These contain the checkpoint/state per sync config.
|
|
45
|
+
*
|
|
46
|
+
* In common cases we'd have one active config or one active + one processing config,
|
|
47
|
+
* but the model allows multiple configs in any state.
|
|
48
|
+
*/
|
|
49
|
+
sync_configs: SyncRuleConfigStateV3[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Static sync config definition.
|
|
54
|
+
*
|
|
55
|
+
* This should be treated as immutable - we don't update this after initial creation.
|
|
56
|
+
*/
|
|
57
|
+
export interface SyncConfigDefinition {
|
|
58
|
+
_id: bson.ObjectId;
|
|
59
|
+
created_at: Date;
|
|
60
|
+
storage_version: number;
|
|
61
|
+
/**
|
|
62
|
+
* The related SyncRuleDocumentV3.
|
|
63
|
+
*
|
|
64
|
+
* Note that a specific sync config definition never moves between replication streams. Instead, we can create a new copy for the new replication stream.
|
|
65
|
+
*
|
|
66
|
+
* When terminating a specific sync config definition, we remove the reference from replication stream -> sync config, but this reference here remains as a historical record.
|
|
67
|
+
*/
|
|
68
|
+
replication_stream_id: number;
|
|
69
|
+
|
|
70
|
+
content: string;
|
|
71
|
+
serialized_plan?: SerializedSyncPlan | null;
|
|
72
|
+
|
|
73
|
+
rule_mapping: {
|
|
74
|
+
/**
|
|
75
|
+
* Map of uniqueName -> id, unique per replication stream.
|
|
76
|
+
*/
|
|
77
|
+
definitions: Record<string, string>;
|
|
78
|
+
/**
|
|
79
|
+
* Map of (lookupName, queryId) -> id, unique per replication stream.
|
|
80
|
+
*/
|
|
81
|
+
parameter_indexes: Record<string, string>;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface CurrentBucketV3 extends CurrentBucket {
|
|
86
|
+
def: BucketDefinitionId;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RecordedLookupV3 {
|
|
90
|
+
i: ParameterIndexId;
|
|
91
|
+
l: bson.Binary;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CurrentDataDocumentV3 {
|
|
95
|
+
_id: ReplicaId;
|
|
96
|
+
data: bson.Binary | null;
|
|
97
|
+
buckets: CurrentBucketV3[];
|
|
98
|
+
lookups: RecordedLookupV3[];
|
|
99
|
+
/**
|
|
100
|
+
* If set, this can be deleted, once there is a consistent checkpoint >= pending_delete.
|
|
101
|
+
*
|
|
102
|
+
* This must only be set if buckets = [], lookups = [].
|
|
103
|
+
*/
|
|
104
|
+
pending_delete?: bigint;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface BucketParameterDocumentV3 extends BucketParameterDocumentBase<SourceTableKey> {}
|
|
108
|
+
|
|
109
|
+
export type BucketDataKeyV3 = BucketDataKey;
|
|
110
|
+
|
|
111
|
+
export interface BucketDataDocumentV3 extends BucketDataDocumentBase {
|
|
112
|
+
_id: BucketDataKeyV3;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function serializeBucketDataV3(document: BucketDataDoc): BucketDataDocumentV3 {
|
|
116
|
+
const { bucketKey, o } = document;
|
|
117
|
+
return {
|
|
118
|
+
_id: {
|
|
119
|
+
b: bucketKey.bucket,
|
|
120
|
+
o: o
|
|
121
|
+
},
|
|
122
|
+
// List fields directly, so that we don't accidentally persist any unknown fields
|
|
123
|
+
op: document.op,
|
|
124
|
+
source_table: document.source_table,
|
|
125
|
+
source_key: document.source_key,
|
|
126
|
+
table: document.table,
|
|
127
|
+
row_id: document.row_id,
|
|
128
|
+
checksum: document.checksum,
|
|
129
|
+
data: document.data,
|
|
130
|
+
target_op: document.target_op
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function loadBucketDataDocumentV3(
|
|
135
|
+
context: Pick<BucketKey, 'replicationStreamId' | 'definitionId'>,
|
|
136
|
+
doc: BucketDataDocumentV3
|
|
137
|
+
): BucketDataDoc {
|
|
138
|
+
const { _id, ...rest } = doc;
|
|
139
|
+
return {
|
|
140
|
+
bucketKey: {
|
|
141
|
+
...context,
|
|
142
|
+
bucket: _id.b
|
|
143
|
+
},
|
|
144
|
+
o: _id.o,
|
|
145
|
+
...rest
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function taggedBucketParameterDocumentToV3(document: TaggedBucketParameterDocument): BucketParameterDocumentV3 {
|
|
150
|
+
const { index: _index, ...rest } = document;
|
|
151
|
+
return rest as BucketParameterDocumentV3;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface SourceTableDocumentV3 extends SourceTableDocument {
|
|
155
|
+
bucket_data_source_ids: BucketDefinitionId[];
|
|
156
|
+
parameter_lookup_source_ids: ParameterIndexId[];
|
|
157
|
+
latest_pending_delete?: InternalOpId | undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface BucketStateDocumentV3 extends BucketStateDocumentBase {
|
|
161
|
+
_id: BucketStateDocumentBase['_id'] & {
|
|
162
|
+
d: BucketDefinitionId;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
export * as test_utils from '../utils/test-utils.js';
|
|
2
2
|
export * from '../utils/util.js';
|
|
3
|
+
export * from './implementation/BucketDefinitionMapping.js';
|
|
4
|
+
export * from './implementation/common/PersistedBatch.js';
|
|
5
|
+
export * from './implementation/createMongoSyncBucketStorage.js';
|
|
3
6
|
export * from './implementation/db.js';
|
|
4
7
|
export * from './implementation/models.js';
|
|
5
|
-
export * from './implementation/MongoBucketBatch.js';
|
|
6
8
|
export * from './implementation/MongoIdSequence.js';
|
|
9
|
+
export * from './implementation/MongoPersistedSyncRules.js';
|
|
7
10
|
export * from './implementation/MongoPersistedSyncRulesContent.js';
|
|
8
11
|
export * from './implementation/MongoStorageProvider.js';
|
|
9
|
-
export * from './implementation/MongoSyncBucketStorage.js';
|
|
10
12
|
export * from './implementation/MongoSyncRulesLock.js';
|
|
11
13
|
export * from './implementation/OperationBatch.js';
|
|
12
|
-
export * from './implementation/
|
|
14
|
+
export * from './implementation/v1/models.js';
|
|
15
|
+
export * from './implementation/v3/models.js';
|
|
13
16
|
export * from './MongoBucketStorage.js';
|
|
14
17
|
export * from './MongoReportStorage.js';
|
package/src/utils/util.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
1
2
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
-
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
3
|
+
import { ReplicationAbortedError, ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
3
4
|
import { storage, utils } from '@powersync/service-core';
|
|
4
5
|
import * as bson from 'bson';
|
|
5
6
|
import * as crypto from 'crypto';
|
|
7
|
+
import * as timers from 'node:timers/promises';
|
|
6
8
|
import * as uuid from 'uuid';
|
|
7
|
-
import {
|
|
9
|
+
import { BucketDataDoc } from '../storage/implementation/common/BucketDataDoc.js';
|
|
8
10
|
|
|
9
11
|
export function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T> {
|
|
10
12
|
let filter = {
|
|
@@ -69,10 +71,10 @@ export async function readSingleBatch<T>(cursor: mongo.AbstractCursor<T>): Promi
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
export function mapOpEntry(row:
|
|
74
|
+
export function mapOpEntry(row: BucketDataDoc): utils.OplogEntry {
|
|
73
75
|
if (row.op == 'PUT' || row.op == 'REMOVE') {
|
|
74
76
|
return {
|
|
75
|
-
op_id: utils.internalToExternalOpId(row.
|
|
77
|
+
op_id: utils.internalToExternalOpId(row.o),
|
|
76
78
|
op: row.op,
|
|
77
79
|
object_type: row.table,
|
|
78
80
|
object_id: row.row_id,
|
|
@@ -84,7 +86,7 @@ export function mapOpEntry(row: BucketDataDocument): utils.OplogEntry {
|
|
|
84
86
|
// MOVE, CLEAR
|
|
85
87
|
|
|
86
88
|
return {
|
|
87
|
-
op_id: utils.internalToExternalOpId(row.
|
|
89
|
+
op_id: utils.internalToExternalOpId(row.o),
|
|
88
90
|
op: row.op,
|
|
89
91
|
checksum: Number(row.checksum)
|
|
90
92
|
};
|
|
@@ -129,6 +131,33 @@ export function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.
|
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
export async function retryOnMongoMaxTimeMSExpired<T>(
|
|
135
|
+
operation: () => Promise<T>,
|
|
136
|
+
options: {
|
|
137
|
+
signal?: AbortSignal;
|
|
138
|
+
abortMessage?: string;
|
|
139
|
+
retryDelayMs: number;
|
|
140
|
+
onRetry?: (retryCount: number) => void;
|
|
141
|
+
}
|
|
142
|
+
): Promise<T> {
|
|
143
|
+
let retryCount = 0;
|
|
144
|
+
while (true) {
|
|
145
|
+
if (options.signal?.aborted) {
|
|
146
|
+
throw new ReplicationAbortedError(options.abortMessage ?? 'Aborted MongoDB operation', options.signal.reason);
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
return await operation();
|
|
150
|
+
} catch (e) {
|
|
151
|
+
if (!lib_mongo.isMongoServerError(e) || e.codeName !== 'MaxTimeMSExpired') {
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
154
|
+
retryCount += 1;
|
|
155
|
+
options.onRetry?.(retryCount);
|
|
156
|
+
await timers.setTimeout(options.retryDelayMs);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
132
161
|
export const createPaginatedConnectionQuery = async <T extends mongo.Document>(
|
|
133
162
|
query: mongo.Filter<T>,
|
|
134
163
|
collection: mongo.Collection<T>,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { VersionedPowerSyncMongoV3 } from '@module/storage/implementation/v3/VersionedPowerSyncMongoV3.js';
|
|
1
2
|
import { storage, SyncRulesBucketStorage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
2
3
|
import { bucketRequest, register, test_utils } from '@powersync/service-core-tests';
|
|
3
4
|
import { describe, expect, test } from 'vitest';
|
|
4
|
-
import { MongoCompactor } from '../../src/storage/implementation/MongoCompactor.js';
|
|
5
5
|
import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
|
|
6
6
|
|
|
7
7
|
describe('Mongo Sync Bucket Storage Compact', () => {
|
|
@@ -64,9 +64,16 @@ bucket_definitions:
|
|
|
64
64
|
|
|
65
65
|
test('full compact', async () => {
|
|
66
66
|
const { bucketStorage, checkpoint, factory, syncRules } = await setup();
|
|
67
|
+
const storageDb = bucketStorage.db;
|
|
67
68
|
|
|
68
69
|
// Simulate bucket_state from old version not being available
|
|
69
|
-
|
|
70
|
+
if (storageDb.storageConfig.incrementalReprocessing) {
|
|
71
|
+
// This should actually never happen on V3, but we test this anyway.
|
|
72
|
+
// Can remove this if it causes issues in the future.
|
|
73
|
+
await (storageDb as VersionedPowerSyncMongoV3).bucketStateV3(bucketStorage.group_id).deleteMany({});
|
|
74
|
+
} else {
|
|
75
|
+
await factory.db.bucket_state.deleteMany({});
|
|
76
|
+
}
|
|
70
77
|
|
|
71
78
|
await bucketStorage.compact({
|
|
72
79
|
clearBatchLimit: 200,
|
|
@@ -95,10 +102,10 @@ bucket_definitions:
|
|
|
95
102
|
});
|
|
96
103
|
|
|
97
104
|
test('populatePersistentChecksumCache', async () => {
|
|
98
|
-
// Populate old
|
|
105
|
+
// Populate old replication stream
|
|
99
106
|
const { factory } = await setup();
|
|
100
107
|
|
|
101
|
-
// Now populate another
|
|
108
|
+
// Now populate another replication stream (bucket definition name changed)
|
|
102
109
|
const syncRules = await factory.updateSyncRules(
|
|
103
110
|
updateSyncRulesFromYaml(`
|
|
104
111
|
bucket_definitions:
|
|
@@ -108,6 +115,7 @@ bucket_definitions:
|
|
|
108
115
|
`)
|
|
109
116
|
);
|
|
110
117
|
const bucketStorage = factory.getInstance(syncRules);
|
|
118
|
+
const storageDb = (bucketStorage as any).db;
|
|
111
119
|
|
|
112
120
|
await populate(bucketStorage, 2);
|
|
113
121
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
@@ -158,35 +166,54 @@ bucket_definitions:
|
|
|
158
166
|
`)
|
|
159
167
|
);
|
|
160
168
|
const bucketStorage = factory.getInstance(syncRules);
|
|
169
|
+
const storageDb = bucketStorage.db;
|
|
161
170
|
|
|
162
171
|
// This simulates bucket_state created using bigint bytes.
|
|
163
172
|
// This typically happens when buckets get very large (> 2GiB). We don't want to create that much
|
|
164
173
|
// data in the tests, so we directly insert the bucket_state here.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
174
|
+
if (storageDb.storageConfig.incrementalReprocessing) {
|
|
175
|
+
const bucketStateCollection = (storageDb as VersionedPowerSyncMongoV3).bucketStateV3(bucketStorage.group_id);
|
|
176
|
+
await bucketStateCollection.insertOne({
|
|
177
|
+
_id: {
|
|
178
|
+
d: '1',
|
|
179
|
+
b: 'global[]'
|
|
180
|
+
},
|
|
181
|
+
last_op: 5n,
|
|
182
|
+
compacted_state: {
|
|
183
|
+
op_id: 3n,
|
|
184
|
+
count: 3,
|
|
185
|
+
checksum: 0n,
|
|
186
|
+
bytes: 7n
|
|
187
|
+
},
|
|
188
|
+
estimate_since_compact: {
|
|
189
|
+
count: 2,
|
|
190
|
+
bytes: 5n
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
await factory.db.bucket_state.insertOne({
|
|
195
|
+
_id: {
|
|
196
|
+
g: bucketStorage.group_id,
|
|
197
|
+
b: 'global[]'
|
|
198
|
+
},
|
|
199
|
+
last_op: 5n,
|
|
200
|
+
compacted_state: {
|
|
201
|
+
op_id: 3n,
|
|
202
|
+
count: 3,
|
|
203
|
+
checksum: 0n,
|
|
204
|
+
bytes: 7n
|
|
205
|
+
},
|
|
206
|
+
estimate_since_compact: {
|
|
207
|
+
count: 2,
|
|
208
|
+
bytes: 5n
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// This test uses a couple of "internal" APIs of the compactor.
|
|
214
|
+
const compactor = bucketStorage.createMongoCompactor({ maxOpId: 5n });
|
|
215
|
+
|
|
216
|
+
const dirtyBuckets = compactor.dirtyBucketBatches({
|
|
190
217
|
minBucketChanges: 1,
|
|
191
218
|
minChangeRatio: 0.39
|
|
192
219
|
});
|
|
@@ -205,6 +232,7 @@ bucket_definitions:
|
|
|
205
232
|
expect(checksumBuckets).toEqual([
|
|
206
233
|
{
|
|
207
234
|
bucket: 'global[]',
|
|
235
|
+
definitionId: storageDb.storageConfig.incrementalReprocessing ? '1' : null,
|
|
208
236
|
estimatedCount: 5
|
|
209
237
|
}
|
|
210
238
|
]);
|