@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.
Files changed (202) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +1 -1
  3. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -1
  4. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +2 -2
  5. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  6. package/dist/storage/MongoBucketStorage.d.ts +8 -6
  7. package/dist/storage/MongoBucketStorage.js +153 -66
  8. package/dist/storage/MongoBucketStorage.js.map +1 -1
  9. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +15 -0
  10. package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
  11. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  12. package/dist/storage/implementation/CheckpointState.d.ts +20 -0
  13. package/dist/storage/implementation/CheckpointState.js +31 -0
  14. package/dist/storage/implementation/CheckpointState.js.map +1 -0
  15. package/dist/storage/implementation/MongoBucketBatch.d.ts +48 -35
  16. package/dist/storage/implementation/MongoBucketBatch.js +118 -379
  17. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  18. package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
  19. package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
  20. package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
  21. package/dist/storage/implementation/MongoChecksums.d.ts +29 -17
  22. package/dist/storage/implementation/MongoChecksums.js +13 -72
  23. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  24. package/dist/storage/implementation/MongoCompactor.d.ts +98 -58
  25. package/dist/storage/implementation/MongoCompactor.js +229 -296
  26. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  27. package/dist/storage/implementation/MongoParameterCompactor.d.ts +11 -6
  28. package/dist/storage/implementation/MongoParameterCompactor.js +11 -8
  29. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  30. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
  31. package/dist/storage/implementation/MongoPersistedSyncRules.js +67 -0
  32. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
  33. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +22 -5
  34. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +56 -13
  35. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
  36. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +61 -32
  37. package/dist/storage/implementation/MongoSyncBucketStorage.js +85 -523
  38. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  39. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +10 -4
  40. package/dist/storage/implementation/MongoSyncRulesLock.js +19 -13
  41. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  42. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
  43. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  44. package/dist/storage/implementation/OperationBatch.js +1 -1
  45. package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +14 -0
  46. package/dist/storage/implementation/SyncRuleStateUpdate.js +36 -0
  47. package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -0
  48. package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
  49. package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
  50. package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
  51. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
  52. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
  53. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
  54. package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
  55. package/dist/storage/implementation/common/PersistedBatch.js +237 -0
  56. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
  57. package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
  58. package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
  59. package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
  60. package/dist/storage/implementation/common/SourceRecordStore.d.ts +35 -0
  61. package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
  62. package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
  63. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
  64. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
  65. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
  66. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
  67. package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
  68. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
  69. package/dist/storage/implementation/db.d.ts +41 -36
  70. package/dist/storage/implementation/db.js +77 -99
  71. package/dist/storage/implementation/db.js.map +1 -1
  72. package/dist/storage/implementation/models.d.ts +79 -66
  73. package/dist/storage/implementation/models.js +20 -1
  74. package/dist/storage/implementation/models.js.map +1 -1
  75. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +27 -0
  76. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +407 -0
  77. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
  78. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
  79. package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
  80. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
  81. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
  82. package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
  83. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
  84. package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
  85. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
  86. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
  87. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +50 -0
  88. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +354 -0
  89. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
  90. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +25 -0
  91. package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
  92. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
  93. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
  94. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
  95. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
  96. package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
  97. package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
  98. package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
  99. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
  100. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
  101. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
  102. package/dist/storage/implementation/v1/models.d.ts +45 -0
  103. package/dist/storage/implementation/v1/models.js +37 -0
  104. package/dist/storage/implementation/v1/models.js.map +1 -0
  105. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +30 -0
  106. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +463 -0
  107. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
  108. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
  109. package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
  110. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
  111. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
  112. package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
  113. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
  114. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
  115. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
  116. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
  117. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +4 -0
  118. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
  119. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
  120. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +63 -0
  121. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +508 -0
  122. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
  123. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +28 -0
  124. package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
  125. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
  126. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
  127. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
  128. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
  129. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
  130. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
  131. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
  132. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +22 -0
  133. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +74 -0
  134. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
  135. package/dist/storage/implementation/v3/models.d.ts +101 -0
  136. package/dist/storage/implementation/v3/models.js +34 -0
  137. package/dist/storage/implementation/v3/models.js.map +1 -0
  138. package/dist/storage/storage-index.d.ts +6 -3
  139. package/dist/storage/storage-index.js +6 -3
  140. package/dist/storage/storage-index.js.map +1 -1
  141. package/dist/utils/util.d.ts +10 -3
  142. package/dist/utils/util.js +24 -3
  143. package/dist/utils/util.js.map +1 -1
  144. package/package.json +9 -9
  145. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
  146. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -7
  147. package/src/storage/MongoBucketStorage.ts +254 -99
  148. package/src/storage/implementation/BucketDefinitionMapping.ts +75 -0
  149. package/src/storage/implementation/CheckpointState.ts +59 -0
  150. package/src/storage/implementation/MongoBucketBatch.ts +182 -490
  151. package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
  152. package/src/storage/implementation/MongoChecksums.ts +53 -75
  153. package/src/storage/implementation/MongoCompactor.ts +374 -404
  154. package/src/storage/implementation/MongoParameterCompactor.ts +37 -24
  155. package/src/storage/implementation/MongoPersistedSyncRules.ts +82 -0
  156. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +78 -16
  157. package/src/storage/implementation/MongoSyncBucketStorage.ts +179 -628
  158. package/src/storage/implementation/MongoSyncRulesLock.ts +20 -16
  159. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +3 -1
  160. package/src/storage/implementation/OperationBatch.ts +1 -1
  161. package/src/storage/implementation/SyncRuleStateUpdate.ts +38 -0
  162. package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
  163. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
  164. package/src/storage/implementation/common/PersistedBatch.ts +364 -0
  165. package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
  166. package/src/storage/implementation/common/SourceRecordStore.ts +48 -0
  167. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
  168. package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
  169. package/src/storage/implementation/db.ts +110 -131
  170. package/src/storage/implementation/models.ts +102 -79
  171. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +509 -0
  172. package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
  173. package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
  174. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
  175. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +543 -0
  176. package/src/storage/implementation/v1/PersistedBatchV1.ts +229 -0
  177. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
  178. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
  179. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
  180. package/src/storage/implementation/v1/models.ts +99 -0
  181. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +607 -0
  182. package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
  183. package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
  184. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
  185. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +11 -0
  186. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +678 -0
  187. package/src/storage/implementation/v3/PersistedBatchV3.ts +317 -0
  188. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
  189. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
  190. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +117 -0
  191. package/src/storage/implementation/v3/models.ts +164 -0
  192. package/src/storage/storage-index.ts +6 -3
  193. package/src/utils/util.ts +34 -5
  194. package/test/src/storage_compacting.test.ts +57 -29
  195. package/test/src/storage_sync.test.ts +767 -5
  196. package/test/src/storeCurrentData.test.ts +211 -0
  197. package/test/tsconfig.json +0 -1
  198. package/tsconfig.tsbuildinfo +1 -1
  199. package/dist/storage/implementation/PersistedBatch.d.ts +0 -71
  200. package/dist/storage/implementation/PersistedBatch.js +0 -354
  201. package/dist/storage/implementation/PersistedBatch.js.map +0 -1
  202. 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/PersistedBatch.js';
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 { BucketDataDocument } from '../storage/implementation/models.js';
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: BucketDataDocument): utils.OplogEntry {
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._id.o),
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._id.o),
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
- await factory.db.bucket_state.deleteMany({});
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 sync rules version
105
+ // Populate old replication stream
99
106
  const { factory } = await setup();
100
107
 
101
- // Now populate another version (bucket definition name changed)
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
- await factory.db.bucket_state.insertOne({
166
- _id: {
167
- g: bucketStorage.group_id,
168
- b: 'global[]'
169
- },
170
- last_op: 5n,
171
- compacted_state: {
172
- op_id: 3n,
173
- count: 3,
174
- checksum: 0n,
175
- bytes: 7n
176
- },
177
- estimate_since_compact: {
178
- count: 2,
179
- bytes: 5n
180
- }
181
- });
182
-
183
- // This test uses a couple of internal APIs of the compactor - there is no simple way
184
- // to test this using the current public APIs.
185
- const compactor = new MongoCompactor(bucketStorage, (bucketStorage as any).db, {
186
- maxOpId: 5n
187
- });
188
-
189
- const dirtyBuckets = (compactor as any).dirtyBucketBatches({
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
  ]);