@powersync/service-module-mongodb-storage 0.15.4 → 0.16.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 +35 -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 +2 -2
- package/dist/storage/MongoBucketStorage.js +47 -34
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.d.ts +16 -14
- package/dist/storage/implementation/MongoBucketBatch.js +80 -115
- 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 +28 -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 +64 -0
- package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +9 -0
- package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +47 -29
- package/dist/storage/implementation/MongoSyncBucketStorage.js +94 -387
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -3
- package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
- 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/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 +36 -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 +32 -35
- package/dist/storage/implementation/db.js +77 -99
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +62 -33
- 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 +13 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -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 +41 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +283 -0
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
- package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -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 +34 -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 +13 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -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 +5 -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 +41 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +407 -0
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -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 +21 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +43 -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 +6 -6
- package/src/storage/MongoBucketStorage.ts +92 -59
- package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
- package/src/storage/implementation/MongoBucketBatch.ts +110 -144
- package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
- package/src/storage/implementation/MongoChecksums.ts +52 -75
- package/src/storage/implementation/MongoCompactor.ts +374 -404
- package/src/storage/implementation/MongoParameterCompactor.ts +37 -24
- package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
- package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +17 -0
- package/src/storage/implementation/MongoSyncBucketStorage.ts +181 -455
- package/src/storage/implementation/MongoSyncRulesLock.ts +11 -13
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +3 -1
- package/src/storage/implementation/OperationBatch.ts +1 -1
- 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 +49 -0
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
- package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
- package/src/storage/implementation/db.ts +105 -129
- package/src/storage/implementation/models.ts +82 -36
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -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 +448 -0
- package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -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 +84 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -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 +12 -0
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +550 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -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 +112 -0
- package/src/storage/implementation/v3/models.ts +96 -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 +351 -5
- 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,112 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { BucketDefinitionId, ParameterIndexId } from '../BucketDefinitionMapping.js';
|
|
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
|
+
} from './models.js';
|
|
12
|
+
|
|
13
|
+
export class VersionedPowerSyncMongoV3 extends BaseVersionedPowerSyncMongo {
|
|
14
|
+
sourceRecordsV3(replicationStreamId: number, sourceTableId: mongo.ObjectId): mongo.Collection<CurrentDataDocumentV3> {
|
|
15
|
+
const collectionName = this.sourceRecordsCollectionName(replicationStreamId, sourceTableId);
|
|
16
|
+
return this.db.collection<CurrentDataDocumentV3>(collectionName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async listSourceRecordCollectionsV3(replicationStreamId: number): Promise<mongo.Collection<CurrentDataDocumentV3>[]> {
|
|
20
|
+
return this.listCollectionsByPrefix<CurrentDataDocumentV3>(`source_records_${replicationStreamId}_`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async initializeSourceRecordsCollection(replicationStreamId: number, sourceTableId: mongo.ObjectId) {
|
|
24
|
+
await this.sourceRecordsV3(replicationStreamId, sourceTableId).createIndex(
|
|
25
|
+
{
|
|
26
|
+
pending_delete: 1
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
partialFilterExpression: { pending_delete: { $exists: true } },
|
|
30
|
+
name: 'pending_delete'
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
commonSourceTables(replicationStreamId: number): mongo.Collection<CommonSourceTableDocument> {
|
|
36
|
+
return this.sourceTablesV3(replicationStreamId) as mongo.Collection<CommonSourceTableDocument>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bucketStateV3(replicationStreamId: number): mongo.Collection<BucketStateDocumentV3> {
|
|
40
|
+
return this.db.collection(`bucket_state_${replicationStreamId}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
parameterIndexV3(
|
|
44
|
+
replicationStreamId: number,
|
|
45
|
+
indexId: ParameterIndexId
|
|
46
|
+
): mongo.Collection<BucketParameterDocumentV3> {
|
|
47
|
+
return this.db.collection(`parameter_index_${replicationStreamId}_${indexId}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sourceTablesV3(replicationStreamId: number): mongo.Collection<SourceTableDocumentV3> {
|
|
51
|
+
return this.db.collection<SourceTableDocumentV3>(this.sourceTableCollectionName(replicationStreamId));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async initializeStreamStorage(replicationStreamId: number) {
|
|
55
|
+
const sourceTables = this.sourceTablesV3(replicationStreamId);
|
|
56
|
+
const bucketState = this.bucketStateV3(replicationStreamId);
|
|
57
|
+
await sourceTables.createIndex(
|
|
58
|
+
{
|
|
59
|
+
connection_id: 1,
|
|
60
|
+
schema_name: 1,
|
|
61
|
+
table_name: 1,
|
|
62
|
+
relation_id: 1
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'source_lookup'
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
await sourceTables.createIndex(
|
|
69
|
+
{
|
|
70
|
+
latest_pending_delete: 1
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
partialFilterExpression: { latest_pending_delete: { $exists: true } },
|
|
74
|
+
name: 'latest_pending_delete'
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
await bucketState.createIndex(
|
|
78
|
+
{
|
|
79
|
+
last_op: 1
|
|
80
|
+
},
|
|
81
|
+
{ name: 'bucket_updates', unique: true }
|
|
82
|
+
);
|
|
83
|
+
await bucketState.createIndex(
|
|
84
|
+
{
|
|
85
|
+
'estimate_since_compact.count': -1
|
|
86
|
+
},
|
|
87
|
+
{ name: 'dirty_count' }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
bucketDataV3(replicationStreamId: number, definitionId: BucketDefinitionId) {
|
|
92
|
+
return this.db.collection<BucketDataDocumentV3>(`bucket_data_${replicationStreamId}_${definitionId}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
listBucketDataCollectionsV3(replicationStreamId: number) {
|
|
96
|
+
return this.upstream.listBucketDataCollectionsV3(replicationStreamId);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async listParameterIndexCollectionsV3(
|
|
100
|
+
replicationStreamId: number
|
|
101
|
+
): Promise<{ collection: mongo.Collection<BucketParameterDocumentV3>; indexId: ParameterIndexId }[]> {
|
|
102
|
+
const prefix = `parameter_index_${replicationStreamId}_`;
|
|
103
|
+
const collections = await this.db.listCollections({ name: new RegExp(`^${prefix}`) }, { nameOnly: true }).toArray();
|
|
104
|
+
|
|
105
|
+
return collections
|
|
106
|
+
.filter((collection) => collection.name.startsWith(prefix))
|
|
107
|
+
.map((collection) => ({
|
|
108
|
+
collection: this.db.collection<BucketParameterDocumentV3>(collection.name),
|
|
109
|
+
indexId: collection.name.slice(prefix.length)
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { InternalOpId } from '@powersync/service-core';
|
|
2
|
+
import * as bson from 'bson';
|
|
3
|
+
import { BucketDefinitionId, ParameterIndexId } from '../BucketDefinitionMapping.js';
|
|
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
|
+
TaggedBucketParameterDocument
|
|
15
|
+
} from '../models.js';
|
|
16
|
+
|
|
17
|
+
export interface CurrentBucketV3 extends CurrentBucket {
|
|
18
|
+
def: BucketDefinitionId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RecordedLookupV3 {
|
|
22
|
+
i: ParameterIndexId;
|
|
23
|
+
l: bson.Binary;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CurrentDataDocumentV3 {
|
|
27
|
+
_id: ReplicaId;
|
|
28
|
+
data: bson.Binary | null;
|
|
29
|
+
buckets: CurrentBucketV3[];
|
|
30
|
+
lookups: RecordedLookupV3[];
|
|
31
|
+
/**
|
|
32
|
+
* If set, this can be deleted, once there is a consistent checkpoint >= pending_delete.
|
|
33
|
+
*
|
|
34
|
+
* This must only be set if buckets = [], lookups = [].
|
|
35
|
+
*/
|
|
36
|
+
pending_delete?: bigint;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BucketParameterDocumentV3 extends BucketParameterDocumentBase<SourceTableKey> {}
|
|
40
|
+
|
|
41
|
+
export type BucketDataKeyV3 = BucketDataKey;
|
|
42
|
+
|
|
43
|
+
export interface BucketDataDocumentV3 extends BucketDataDocumentBase {
|
|
44
|
+
_id: BucketDataKeyV3;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function serializeBucketDataV3(document: BucketDataDoc): BucketDataDocumentV3 {
|
|
48
|
+
const { bucketKey, o } = document;
|
|
49
|
+
return {
|
|
50
|
+
_id: {
|
|
51
|
+
b: bucketKey.bucket,
|
|
52
|
+
o: o
|
|
53
|
+
},
|
|
54
|
+
// List fields directly, so that we don't accidentally persist any unknown fields
|
|
55
|
+
op: document.op,
|
|
56
|
+
source_table: document.source_table,
|
|
57
|
+
source_key: document.source_key,
|
|
58
|
+
table: document.table,
|
|
59
|
+
row_id: document.row_id,
|
|
60
|
+
checksum: document.checksum,
|
|
61
|
+
data: document.data,
|
|
62
|
+
target_op: document.target_op
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function loadBucketDataDocumentV3(
|
|
67
|
+
context: Pick<BucketKey, 'replicationStreamId' | 'definitionId'>,
|
|
68
|
+
doc: BucketDataDocumentV3
|
|
69
|
+
): BucketDataDoc {
|
|
70
|
+
const { _id, ...rest } = doc;
|
|
71
|
+
return {
|
|
72
|
+
bucketKey: {
|
|
73
|
+
...context,
|
|
74
|
+
bucket: _id.b
|
|
75
|
+
},
|
|
76
|
+
o: _id.o,
|
|
77
|
+
...rest
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function taggedBucketParameterDocumentToV3(document: TaggedBucketParameterDocument): BucketParameterDocumentV3 {
|
|
82
|
+
const { index: _index, ...rest } = document;
|
|
83
|
+
return rest as BucketParameterDocumentV3;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface SourceTableDocumentV3 extends SourceTableDocument {
|
|
87
|
+
bucket_data_source_ids: BucketDefinitionId[];
|
|
88
|
+
parameter_lookup_source_ids: ParameterIndexId[];
|
|
89
|
+
latest_pending_delete?: InternalOpId | undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface BucketStateDocumentV3 extends BucketStateDocumentBase {
|
|
93
|
+
_id: BucketStateDocumentBase['_id'] & {
|
|
94
|
+
d: BucketDefinitionId;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -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
|
]);
|