@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,229 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { ReplicationAssertionError } from '@powersync/lib-services-framework';
3
+ import { storage } from '@powersync/service-core';
4
+ import * as bson from 'bson';
5
+
6
+ import { BucketDataSource, BucketDefinitionId } from '@powersync/service-sync-rules';
7
+ import { mongoTableId } from '../../../utils/util.js';
8
+ import { EMPTY_DATA } from '../MongoBucketBatchShared.js';
9
+ import {
10
+ BucketStateUpdate,
11
+ PersistedBatch,
12
+ SaveParameterDataOptions,
13
+ UpsertCurrentDataOptions
14
+ } from '../common/PersistedBatch.js';
15
+ import { LEGACY_BUCKET_DATA_DEFINITION_ID, LEGACY_BUCKET_PARAMETER_INDEX_ID, SourceKey } from '../models.js';
16
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
17
+ import {
18
+ BucketParameterDocument,
19
+ BucketStateDocumentV1,
20
+ CurrentDataDocument,
21
+ serializeBucketDataV1,
22
+ taggedBucketParameterDocumentToV1
23
+ } from './models.js';
24
+
25
+ export class PersistedBatchV1 extends PersistedBatch {
26
+ declare protected readonly db: VersionedPowerSyncMongoV1;
27
+
28
+ currentData: mongo.AnyBulkWriteOperation<CurrentDataDocument>[] = [];
29
+
30
+ protected checkDefinitionId(_definitionId: BucketDefinitionId | null): BucketDefinitionId {
31
+ // V1 storage doesn't persist the id, and we don't use it.
32
+ return LEGACY_BUCKET_DATA_DEFINITION_ID;
33
+ }
34
+
35
+ protected getBucketDefinitionId(_bucketSource: BucketDataSource): BucketDefinitionId {
36
+ return LEGACY_BUCKET_DATA_DEFINITION_ID;
37
+ }
38
+
39
+ saveParameterData(data: SaveParameterDataOptions) {
40
+ const { sourceTable, sourceKey, evaluated } = data;
41
+ const remaining_lookups = new Map<string, bson.Binary>();
42
+
43
+ for (let lookup of data.existing_lookups) {
44
+ if (lookup.indexId != null) {
45
+ throw new ReplicationAssertionError('Unexpected v3 lookup when incrementalReprocessing is disabled');
46
+ }
47
+ remaining_lookups.set(lookup.lookup.toString('base64'), lookup.lookup);
48
+ }
49
+
50
+ for (let result of evaluated) {
51
+ const binLookup = storage.serializeLookup(result.lookup);
52
+ remaining_lookups.delete(binLookup.toString('base64'));
53
+
54
+ const op_id = data.op_seq.next();
55
+ this.debugLastOpId = op_id;
56
+ const values: BucketParameterDocument = {
57
+ _id: op_id,
58
+ key: {
59
+ g: this.group_id,
60
+ t: mongoTableId(sourceTable.id),
61
+ k: sourceKey
62
+ },
63
+ lookup: binLookup,
64
+ bucket_parameters: result.bucketParameters
65
+ };
66
+ this.bucketParameters.push({
67
+ ...values,
68
+ index: LEGACY_BUCKET_PARAMETER_INDEX_ID
69
+ });
70
+
71
+ this.currentSize += 200;
72
+ }
73
+
74
+ for (let lookup of remaining_lookups.values()) {
75
+ const op_id = data.op_seq.next();
76
+ this.debugLastOpId = op_id;
77
+ const values: BucketParameterDocument = {
78
+ _id: op_id,
79
+ key: {
80
+ g: this.group_id,
81
+ t: mongoTableId(sourceTable.id),
82
+ k: sourceKey
83
+ },
84
+ lookup,
85
+ bucket_parameters: []
86
+ };
87
+ this.bucketParameters.push({
88
+ ...values,
89
+ index: LEGACY_BUCKET_PARAMETER_INDEX_ID
90
+ });
91
+
92
+ this.currentSize += 200;
93
+ }
94
+ }
95
+
96
+ hardDeleteCurrentData(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId) {
97
+ this.currentData.push({
98
+ deleteOne: {
99
+ filter: { _id: this.currentDataId(sourceTableId, replicaId) }
100
+ }
101
+ });
102
+ this.currentSize += 50;
103
+ }
104
+
105
+ softDeleteCurrentData(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId, _checkpointGreaterThan: bigint) {
106
+ this.hardDeleteCurrentData(sourceTableId, replicaId);
107
+ }
108
+
109
+ upsertCurrentData(values: UpsertCurrentDataOptions) {
110
+ const buckets = values.buckets.map((bucket) => {
111
+ if (bucket.definitionId != null) {
112
+ throw new ReplicationAssertionError('Unexpected v3 bucket when incrementalReprocessing is disabled');
113
+ }
114
+ return {
115
+ bucket: bucket.bucket,
116
+ table: bucket.table,
117
+ id: bucket.id
118
+ };
119
+ });
120
+ const lookups = values.lookups.map((lookup) => {
121
+ if (lookup.indexId != null) {
122
+ throw new ReplicationAssertionError('Unexpected v3 lookup when incrementalReprocessing is disabled');
123
+ }
124
+ return lookup.lookup;
125
+ });
126
+
127
+ this.currentData.push({
128
+ updateOne: {
129
+ filter: { _id: this.currentDataId(values.sourceTableId, values.replicaId) },
130
+ update: {
131
+ $set: {
132
+ data: values.data ?? EMPTY_DATA,
133
+ buckets,
134
+ lookups
135
+ }
136
+ },
137
+ upsert: true
138
+ }
139
+ });
140
+ this.currentSize += (values.data?.length() ?? 0) + 100;
141
+ }
142
+
143
+ protected get currentDataCount() {
144
+ return this.currentData.length;
145
+ }
146
+
147
+ protected async flushBucketData(session: mongo.ClientSession) {
148
+ await this.db.bucketDataV1.bulkWrite(
149
+ this.bucketData.map((document) => ({
150
+ insertOne: {
151
+ document: serializeBucketDataV1(document)
152
+ }
153
+ })),
154
+ {
155
+ session,
156
+ ordered: false
157
+ }
158
+ );
159
+ }
160
+
161
+ protected async flushBucketParameters(session: mongo.ClientSession) {
162
+ await this.db.parameterIndexV1.bulkWrite(
163
+ this.bucketParameters.map((document) => ({
164
+ insertOne: {
165
+ document: taggedBucketParameterDocumentToV1(document)
166
+ }
167
+ })),
168
+ {
169
+ session,
170
+ ordered: false
171
+ }
172
+ );
173
+ }
174
+
175
+ protected async flushCurrentData(session: mongo.ClientSession) {
176
+ if (this.currentData.length == 0) {
177
+ return;
178
+ }
179
+
180
+ await this.db.sourceRecordsV1.bulkWrite(this.currentData, {
181
+ session,
182
+ ordered: true
183
+ });
184
+ }
185
+
186
+ protected async flushBucketStates(session: mongo.ClientSession) {
187
+ await this.db.bucketStateV1.bulkWrite(this.getBucketStateUpdates(), {
188
+ session,
189
+ ordered: false
190
+ });
191
+ }
192
+
193
+ protected resetCurrentData() {
194
+ this.currentData = [];
195
+ }
196
+
197
+ private getBucketStateUpdates(): mongo.AnyBulkWriteOperation<BucketStateDocumentV1>[] {
198
+ return Array.from(this.bucketStates.values()).map((state: BucketStateUpdate) => {
199
+ return {
200
+ updateOne: {
201
+ filter: {
202
+ _id: {
203
+ g: this.group_id,
204
+ b: state.bucket
205
+ }
206
+ },
207
+ update: {
208
+ $set: {
209
+ last_op: state.lastOp
210
+ },
211
+ $inc: {
212
+ 'estimate_since_compact.count': state.incrementCount,
213
+ 'estimate_since_compact.bytes': state.incrementBytes
214
+ }
215
+ },
216
+ upsert: true
217
+ }
218
+ } satisfies mongo.AnyBulkWriteOperation<BucketStateDocumentV1>;
219
+ });
220
+ }
221
+
222
+ private currentDataId(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId): SourceKey {
223
+ return {
224
+ g: this.group_id,
225
+ t: sourceTableId,
226
+ k: replicaId
227
+ };
228
+ }
229
+ }
@@ -0,0 +1,74 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { InternalOpId } from '@powersync/service-core';
3
+ import { BucketDataDoc, BucketKey } from '../common/BucketDataDoc.js';
4
+ import {
5
+ BucketDataDocumentGeneric,
6
+ BucketDataDocumentGenericId,
7
+ SingleBucketStore
8
+ } from '../common/SingleBucketStore.js';
9
+ import { BucketDataProperties } from '../models.js';
10
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
11
+ import { BucketDataDocumentV1, BucketDataKeyV1, serializeBucketDataV1 } from './models.js';
12
+
13
+ export class SingleBucketStoreV1 implements SingleBucketStore {
14
+ public readonly collection: mongo.Collection<BucketDataDocumentGeneric>;
15
+
16
+ constructor(
17
+ private db: VersionedPowerSyncMongoV1,
18
+ public readonly key: BucketKey
19
+ ) {
20
+ this.collection = db.bucketDataV1 as unknown as mongo.Collection<BucketDataDocumentGeneric>;
21
+ }
22
+
23
+ docId(o: InternalOpId): BucketDataDocumentGenericId {
24
+ // `satisfies BucketDataKeyV1` checks that we use the correct type for V1 storage
25
+ // `as anyt` is to allow casting to the interface virtual type
26
+ return {
27
+ g: this.key.replicationStreamId,
28
+ b: this.key.bucket,
29
+ o
30
+ } satisfies BucketDataKeyV1 as any;
31
+ }
32
+
33
+ get minId(): BucketDataDocumentGenericId {
34
+ return {
35
+ g: this.key.replicationStreamId,
36
+ b: this.key.bucket,
37
+ o: new mongo.MinKey()
38
+ } as any;
39
+ }
40
+
41
+ get maxId(): BucketDataDocumentGenericId {
42
+ return {
43
+ g: this.key.replicationStreamId,
44
+ b: this.key.bucket,
45
+ o: new mongo.MaxKey()
46
+ } as any;
47
+ }
48
+
49
+ toPersistedDocument(source: Omit<BucketDataDoc, 'bucketKey'>): BucketDataDocumentGeneric {
50
+ return serializeBucketDataV1({ bucketKey: this.key, ...source }) as unknown as BucketDataDocumentGeneric;
51
+ }
52
+
53
+ fromPersistedDocument(doc: BucketDataDocumentGeneric): BucketDataDoc {
54
+ const document = doc as unknown as BucketDataDocumentV1;
55
+ const { _id, ...rest } = document;
56
+ return {
57
+ bucketKey: this.key,
58
+ o: _id.o,
59
+ ...rest
60
+ };
61
+ }
62
+
63
+ fromPartialPersistedDocument<T extends keyof BucketDataProperties>(
64
+ doc: Pick<BucketDataDocumentGeneric, '_id' | T>
65
+ ): Pick<BucketDataDoc, 'bucketKey' | 'o' | T> {
66
+ const document = doc as Pick<BucketDataDocumentV1, '_id' | T>;
67
+ const { _id, ...rest } = document;
68
+ return {
69
+ bucketKey: this.key,
70
+ o: _id.o,
71
+ ...rest
72
+ } as Pick<BucketDataDoc, 'bucketKey' | 'o' | T>;
73
+ }
74
+ }
@@ -0,0 +1,156 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { Logger } from '@powersync/lib-services-framework';
3
+ import { storage } from '@powersync/service-core';
4
+ import { EvaluatedParameters, EvaluatedRow } from '@powersync/service-sync-rules';
5
+ import * as bson from 'bson';
6
+ import { idPrefixFilter } from '../../../utils/util.js';
7
+ import { cacheKey } from '../OperationBatch.js';
8
+ import {
9
+ LoadedSourceRecord,
10
+ SourceRecordLookupEntry,
11
+ SourceRecordLookupState,
12
+ SourceRecordStore
13
+ } from '../common/SourceRecordStore.js';
14
+ import { SourceKey } from '../models.js';
15
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
16
+ import { CurrentDataDocument } from './models.js';
17
+
18
+ export class SourceRecordStoreV1 implements SourceRecordStore {
19
+ constructor(
20
+ private readonly db: VersionedPowerSyncMongoV1,
21
+ private readonly groupId: number
22
+ ) {}
23
+
24
+ mapEvaluatedBuckets(evaluated: EvaluatedRow[]): LoadedSourceRecord['buckets'] {
25
+ return evaluated.map((entry) => ({
26
+ definitionId: null,
27
+ bucket: entry.bucket,
28
+ table: entry.table,
29
+ id: entry.id
30
+ }));
31
+ }
32
+
33
+ mapParameterLookups(paramEvaluated: EvaluatedParameters[]): SourceRecordLookupState[] {
34
+ return paramEvaluated.map((entry) => ({
35
+ indexId: null,
36
+ lookup: storage.serializeLookup(entry.lookup)
37
+ }));
38
+ }
39
+
40
+ private createId(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId): SourceKey {
41
+ return {
42
+ g: this.groupId,
43
+ t: sourceTableId,
44
+ k: replicaId
45
+ } satisfies SourceKey;
46
+ }
47
+
48
+ private createLoadedDocument(
49
+ sourceTableId: bson.ObjectId,
50
+ id: SourceKey,
51
+ data: bson.Binary | null,
52
+ buckets: CurrentDataDocument['buckets'],
53
+ lookups: CurrentDataDocument['lookups']
54
+ ): LoadedSourceRecord {
55
+ return {
56
+ sourceTableId,
57
+ replicaId: id.k,
58
+ data,
59
+ buckets: buckets.map((bucket) => ({
60
+ definitionId: null,
61
+ bucket: bucket.bucket,
62
+ table: bucket.table,
63
+ id: bucket.id
64
+ })),
65
+ lookups: lookups.map((lookup) => ({
66
+ indexId: null,
67
+ lookup
68
+ })),
69
+ cacheKey: cacheKey(sourceTableId, id.k)
70
+ };
71
+ }
72
+
73
+ async loadSizes(session: mongo.ClientSession, entries: SourceRecordLookupEntry[]): Promise<Map<string, number>> {
74
+ const sizes = new Map<string, number>();
75
+ const sizeCursor: mongo.AggregationCursor<CurrentDataDocument & { size: number }> =
76
+ this.db.sourceRecordsV1.aggregate(
77
+ [
78
+ {
79
+ $match: {
80
+ _id: {
81
+ $in: entries.map((entry) => this.createId(entry.sourceTableId, entry.replicaId) as SourceKey)
82
+ }
83
+ }
84
+ },
85
+ {
86
+ $project: {
87
+ _id: 1,
88
+ size: { $bsonSize: '$$ROOT' }
89
+ }
90
+ }
91
+ ],
92
+ { session }
93
+ );
94
+ for await (const doc of sizeCursor.stream()) {
95
+ sizes.set(cacheKey(doc._id.t, doc._id.k), doc.size);
96
+ }
97
+ return sizes;
98
+ }
99
+
100
+ async loadDocuments(
101
+ session: mongo.ClientSession,
102
+ entries: SourceRecordLookupEntry[],
103
+ idsOnly: boolean
104
+ ): Promise<Map<string, LoadedSourceRecord>> {
105
+ const documents = new Map<string, LoadedSourceRecord>();
106
+ const projection = idsOnly ? { _id: 1 } : undefined;
107
+ const cursor = this.db.sourceRecordsV1.find(
108
+ {
109
+ _id: {
110
+ $in: entries.map((entry) => this.createId(entry.sourceTableId, entry.replicaId) as SourceKey)
111
+ }
112
+ },
113
+ { session, projection }
114
+ );
115
+ for await (const doc of cursor.stream()) {
116
+ const loaded = this.createLoadedDocument(
117
+ doc._id.t,
118
+ doc._id,
119
+ idsOnly ? null : doc.data,
120
+ idsOnly ? [] : doc.buckets,
121
+ idsOnly ? [] : doc.lookups
122
+ );
123
+ documents.set(loaded.cacheKey, loaded);
124
+ }
125
+ return documents;
126
+ }
127
+
128
+ async loadTruncateBatch(
129
+ session: mongo.ClientSession,
130
+ sourceTableId: bson.ObjectId,
131
+ limit: number
132
+ ): Promise<LoadedSourceRecord[]> {
133
+ const cursor = this.db.sourceRecordsV1.find(
134
+ {
135
+ _id: idPrefixFilter<SourceKey>({ g: this.groupId, t: sourceTableId }, ['k']),
136
+ pending_delete: { $exists: false }
137
+ },
138
+ {
139
+ projection: {
140
+ _id: 1,
141
+ buckets: 1,
142
+ lookups: 1
143
+ },
144
+ limit,
145
+ session
146
+ }
147
+ );
148
+ return (await cursor.toArray()).map((doc) =>
149
+ this.createLoadedDocument(sourceTableId, doc._id, null, doc.buckets, doc.lookups)
150
+ );
151
+ }
152
+
153
+ async postCommitCleanup(_lastCheckpoint: bigint, _logger: Logger): Promise<void> {
154
+ // No-op for V1.
155
+ }
156
+ }
@@ -0,0 +1,28 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { BaseVersionedPowerSyncMongo } from '../common/VersionedPowerSyncMongoBase.js';
3
+ import { CommonSourceTableDocument } from '../models.js';
4
+ import { BucketDataDocumentV1, BucketParameterDocument, BucketStateDocumentV1, CurrentDataDocument } from './models.js';
5
+
6
+ export class VersionedPowerSyncMongoV1 extends BaseVersionedPowerSyncMongo {
7
+ get sourceRecordsV1(): mongo.Collection<CurrentDataDocument> {
8
+ return this.upstream.current_data;
9
+ }
10
+
11
+ get bucketStateV1(): mongo.Collection<BucketStateDocumentV1> {
12
+ return this.upstream.bucket_state;
13
+ }
14
+
15
+ commonSourceTables(_replicationStreamId: number): mongo.Collection<CommonSourceTableDocument> {
16
+ return this.upstream.source_tables as any as mongo.Collection<CommonSourceTableDocument>;
17
+ }
18
+
19
+ async initializeStreamStorage(_replicationStreamId: number): Promise<void> {}
20
+
21
+ get bucketDataV1(): mongo.Collection<BucketDataDocumentV1> {
22
+ return this.upstream.bucket_data;
23
+ }
24
+
25
+ get parameterIndexV1(): mongo.Collection<BucketParameterDocument> {
26
+ return this.upstream.bucket_parameters;
27
+ }
28
+ }
@@ -0,0 +1,99 @@
1
+ import { SerializedSyncPlan } from '@powersync/service-core';
2
+ import * as bson from 'bson';
3
+ import { BucketDataDoc } from '../common/BucketDataDoc.js';
4
+ import {
5
+ BucketDataDocumentBase,
6
+ BucketParameterDocumentBase,
7
+ BucketStateDocumentBase,
8
+ CurrentBucket,
9
+ LEGACY_BUCKET_DATA_DEFINITION_ID,
10
+ SourceKey,
11
+ SourceTableDocument,
12
+ SyncRuleCheckpointFields,
13
+ SyncRuleDocumentBase,
14
+ TaggedBucketParameterDocument
15
+ } from '../models.js';
16
+
17
+ export interface BucketDataKeyV1 {
18
+ /** group_id */
19
+ g: number;
20
+ /** bucket name */
21
+ b: string;
22
+ /** op_id */
23
+ o: bigint;
24
+ }
25
+
26
+ export interface CurrentDataDocument {
27
+ _id: SourceKey;
28
+ data: bson.Binary;
29
+ buckets: CurrentBucket[];
30
+ lookups: bson.Binary[];
31
+ }
32
+
33
+ export interface BucketParameterDocument extends BucketParameterDocumentBase<SourceKey> {}
34
+
35
+ export interface BucketDataDocumentV1 extends BucketDataDocumentBase {
36
+ _id: BucketDataKeyV1;
37
+ }
38
+
39
+ export function serializeBucketDataV1(document: BucketDataDoc): BucketDataDocumentV1 {
40
+ const { bucketKey, o } = document;
41
+ return {
42
+ _id: {
43
+ g: bucketKey.replicationStreamId,
44
+ b: bucketKey.bucket,
45
+ o: o
46
+ },
47
+ // List fields directly, so that we don't accidentally persist any unknown fields
48
+ op: document.op,
49
+ source_table: document.source_table,
50
+ source_key: document.source_key,
51
+ table: document.table,
52
+ row_id: document.row_id,
53
+ checksum: document.checksum,
54
+ data: document.data,
55
+ target_op: document.target_op
56
+ };
57
+ }
58
+
59
+ export function loadBucketDataDocumentV1(doc: BucketDataDocumentV1): BucketDataDoc {
60
+ const { _id, ...rest } = doc;
61
+ return {
62
+ bucketKey: {
63
+ replicationStreamId: _id.g,
64
+ definitionId: LEGACY_BUCKET_DATA_DEFINITION_ID,
65
+ bucket: _id.b
66
+ },
67
+ o: _id.o,
68
+ ...rest
69
+ };
70
+ }
71
+
72
+ export function taggedBucketParameterDocumentToV1(document: TaggedBucketParameterDocument): BucketParameterDocument {
73
+ const { index: _index, ...rest } = document;
74
+ return rest as BucketParameterDocument;
75
+ }
76
+
77
+ export interface SourceTableDocumentV1 extends SourceTableDocument {
78
+ group_id: number;
79
+ }
80
+
81
+ export interface SyncRuleDocumentV1 extends SyncRuleDocumentBase, SyncRuleCheckpointFields<string | null> {
82
+ content: string;
83
+ serialized_plan?: SerializedSyncPlan | null;
84
+
85
+ /**
86
+ * True if initial snapshot has been replicated.
87
+ *
88
+ * Can only be false if state == PROCESSING.
89
+ */
90
+ snapshot_done: boolean;
91
+ }
92
+
93
+ export interface BucketStateDocumentV1 extends BucketStateDocumentBase {
94
+ _id: BucketStateDocumentBase['_id'] & {
95
+ g: number;
96
+ };
97
+ }
98
+
99
+ export type BucketStateDocument = BucketStateDocumentV1;