@powersync/service-module-mongodb-storage 0.0.0-dev-20260225160713 → 0.0.0-dev-20260511080634

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 (213) hide show
  1. package/CHANGELOG.md +119 -6
  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 +3 -3
  5. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  6. package/dist/migrations/db/migrations/1770213298299-storage-version.js.map +1 -1
  7. package/dist/storage/MongoBucketStorage.d.ts +5 -3
  8. package/dist/storage/MongoBucketStorage.js +53 -26
  9. package/dist/storage/MongoBucketStorage.js.map +1 -1
  10. package/dist/storage/MongoReportStorage.js.map +1 -1
  11. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
  12. package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
  13. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  14. package/dist/storage/implementation/MongoBucketBatch.d.ts +25 -21
  15. package/dist/storage/implementation/MongoBucketBatch.js +256 -210
  16. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  17. package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
  18. package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
  19. package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
  20. package/dist/storage/implementation/MongoChecksums.d.ts +30 -19
  21. package/dist/storage/implementation/MongoChecksums.js +13 -71
  22. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  23. package/dist/storage/implementation/MongoCompactor.d.ts +99 -53
  24. package/dist/storage/implementation/MongoCompactor.js +265 -303
  25. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  26. package/dist/storage/implementation/MongoParameterCompactor.d.ts +12 -7
  27. package/dist/storage/implementation/MongoParameterCompactor.js +24 -9
  28. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  29. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
  30. package/dist/storage/implementation/MongoPersistedSyncRules.js +64 -0
  31. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
  32. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
  33. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +11 -7
  34. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
  35. package/dist/storage/implementation/MongoStorageProvider.js +1 -1
  36. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  37. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +54 -30
  38. package/dist/storage/implementation/MongoSyncBucketStorage.js +118 -472
  39. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  40. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +7 -5
  41. package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
  42. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  43. package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +4 -4
  44. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
  45. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  46. package/dist/storage/implementation/OperationBatch.js +3 -2
  47. package/dist/storage/implementation/OperationBatch.js.map +1 -1
  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 +36 -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 +38 -4
  70. package/dist/storage/implementation/db.js +80 -1
  71. package/dist/storage/implementation/db.js.map +1 -1
  72. package/dist/storage/implementation/models.d.ts +66 -25
  73. package/dist/storage/implementation/models.js +22 -2
  74. package/dist/storage/implementation/models.js.map +1 -1
  75. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +13 -0
  76. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -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 +41 -0
  88. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +269 -0
  89. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
  90. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -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 +34 -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 +13 -0
  106. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -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 +5 -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 +41 -0
  121. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +404 -0
  122. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
  123. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -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 +21 -0
  133. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
  134. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
  135. package/dist/storage/implementation/v3/models.d.ts +43 -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 +8 -5
  139. package/dist/storage/storage-index.js +8 -5
  140. package/dist/storage/storage-index.js.map +1 -1
  141. package/dist/utils/test-utils.d.ts +4 -1
  142. package/dist/utils/test-utils.js +15 -12
  143. package/dist/utils/test-utils.js.map +1 -1
  144. package/dist/utils/util.d.ts +13 -5
  145. package/dist/utils/util.js +40 -5
  146. package/dist/utils/util.js.map +1 -1
  147. package/package.json +9 -9
  148. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
  149. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -7
  150. package/src/migrations/db/migrations/1770213298299-storage-version.ts +1 -1
  151. package/src/storage/MongoBucketStorage.ts +111 -55
  152. package/src/storage/MongoReportStorage.ts +2 -2
  153. package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
  154. package/src/storage/implementation/MongoBucketBatch.ts +322 -270
  155. package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
  156. package/src/storage/implementation/MongoChecksums.ts +54 -75
  157. package/src/storage/implementation/MongoCompactor.ts +411 -412
  158. package/src/storage/implementation/MongoParameterCompactor.ts +52 -26
  159. package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
  160. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +19 -10
  161. package/src/storage/implementation/MongoStorageProvider.ts +1 -1
  162. package/src/storage/implementation/MongoSyncBucketStorage.ts +206 -466
  163. package/src/storage/implementation/MongoSyncRulesLock.ts +14 -16
  164. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +7 -5
  165. package/src/storage/implementation/OperationBatch.ts +3 -2
  166. package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
  167. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
  168. package/src/storage/implementation/common/PersistedBatch.ts +364 -0
  169. package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
  170. package/src/storage/implementation/common/SourceRecordStore.ts +49 -0
  171. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
  172. package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
  173. package/src/storage/implementation/db.ts +115 -8
  174. package/src/storage/implementation/models.ts +88 -28
  175. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -0
  176. package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
  177. package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
  178. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
  179. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +432 -0
  180. package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -0
  181. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
  182. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
  183. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
  184. package/src/storage/implementation/v1/models.ts +84 -0
  185. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -0
  186. package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
  187. package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
  188. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
  189. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +12 -0
  190. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +545 -0
  191. package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -0
  192. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
  193. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
  194. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +112 -0
  195. package/src/storage/implementation/v3/models.ts +96 -0
  196. package/src/storage/storage-index.ts +8 -5
  197. package/src/utils/test-utils.ts +15 -12
  198. package/src/utils/util.ts +53 -9
  199. package/test/src/__snapshots__/storage.test.ts.snap +201 -0
  200. package/test/src/__snapshots__/storage_compacting.test.ts.snap +17 -0
  201. package/test/src/__snapshots__/storage_sync.test.ts.snap +1393 -16
  202. package/test/src/connection-report-storage.test.ts +3 -3
  203. package/test/src/setup.ts +1 -1
  204. package/test/src/storage.test.ts +10 -8
  205. package/test/src/storage_compacting.test.ts +146 -46
  206. package/test/src/storage_sync.test.ts +404 -56
  207. package/test/src/util.ts +3 -3
  208. package/test/tsconfig.json +0 -1
  209. package/tsconfig.tsbuildinfo +1 -1
  210. package/dist/storage/implementation/PersistedBatch.d.ts +0 -64
  211. package/dist/storage/implementation/PersistedBatch.js +0 -323
  212. package/dist/storage/implementation/PersistedBatch.js.map +0 -1
  213. package/src/storage/implementation/PersistedBatch.ts +0 -401
@@ -0,0 +1,93 @@
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 { BucketDefinitionId } from '../BucketDefinitionMapping.js';
5
+ import { SingleBucketStore } from '../common/SingleBucketStore.js';
6
+ import { BucketStateDocumentBase, LEGACY_BUCKET_DATA_DEFINITION_ID } from '../models.js';
7
+ import { DirtyBucket, MongoCompactor } from '../MongoCompactor.js';
8
+ import { BucketStateDocumentV1 } from './models.js';
9
+ import type { MongoSyncBucketStorageV1 } from './MongoSyncBucketStorageV1.js';
10
+ import { SingleBucketStoreV1 } from './SingleBucketStoreV1.js';
11
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
12
+
13
+ export class MongoCompactorV1 extends MongoCompactor {
14
+ // Override types to the more specific ones
15
+ declare protected readonly db: VersionedPowerSyncMongoV1;
16
+ declare protected readonly storage: MongoSyncBucketStorageV1;
17
+
18
+ public async *dirtyBucketBatches(options: {
19
+ minBucketChanges: number;
20
+ minChangeRatio: number;
21
+ }): AsyncGenerator<DirtyBucket[]> {
22
+ if (options.minBucketChanges <= 0) {
23
+ throw new ReplicationAssertionError('minBucketChanges must be >= 1');
24
+ }
25
+ // Previously, we used an index on {_id.g: 1, estimate_since_compact.count: 1} to only scan buckets with changes.
26
+ // That works well if there are only a small number of dirty buckets, but it causes repeated rescans while data is
27
+ // still changing. We now iterate through all V1 bucket_state rows for the group and filter after projecting.
28
+ yield* this.dirtyBucketBatchesForCollection(
29
+ this.db.bucketStateV1,
30
+ { g: this.group_id, b: new mongo.MinKey() as any },
31
+ { g: this.group_id, b: new mongo.MaxKey() as any },
32
+ options,
33
+ () => null
34
+ );
35
+ }
36
+
37
+ public async dirtyBucketBatchForChecksums(options: { minBucketChanges: number }): Promise<DirtyBucket[]> {
38
+ if (options.minBucketChanges <= 0) {
39
+ throw new ReplicationAssertionError('minBucketChanges must be >= 1');
40
+ }
41
+ // Unlike dirtyBucketBatches, this path is resumable after restart because populateChecksums resets
42
+ // estimate_since_compact as it progresses.
43
+ return this.dirtyBucketBatchForChecksumsForCollection(
44
+ this.db.bucketStateV1,
45
+ {
46
+ '_id.g': this.group_id,
47
+ 'estimate_since_compact.count': { $gte: options.minBucketChanges }
48
+ },
49
+ () => null
50
+ );
51
+ }
52
+
53
+ protected async writeBucketStateUpdates(): Promise<void> {
54
+ await this.db.bucketStateV1.bulkWrite(
55
+ this.bucketStateUpdates as mongo.AnyBulkWriteOperation<BucketStateDocumentV1>[],
56
+ { ordered: false }
57
+ );
58
+ }
59
+
60
+ protected async computeChecksumsForBuckets(
61
+ buckets: Pick<DirtyBucket, 'bucket' | 'definitionId'>[]
62
+ ): Promise<storage.PartialChecksumMap> {
63
+ return this.storage.checksums.computePartialChecksumsDirectByBucket(
64
+ buckets.map(({ bucket }) => ({
65
+ bucket,
66
+ end: this.maxOpId
67
+ }))
68
+ );
69
+ }
70
+
71
+ protected bucketStateFilter(
72
+ bucket: string,
73
+ _definitionId: BucketDefinitionId | null
74
+ ): mongo.Filter<BucketStateDocumentBase> {
75
+ return {
76
+ _id: {
77
+ g: this.group_id,
78
+ b: bucket
79
+ }
80
+ };
81
+ }
82
+
83
+ protected async getBucketDataContext(
84
+ bucket: string,
85
+ _definitionId: BucketDefinitionId | null
86
+ ): Promise<SingleBucketStore | null> {
87
+ return new SingleBucketStoreV1(this.db, {
88
+ replicationStreamId: this.group_id,
89
+ definitionId: LEGACY_BUCKET_DATA_DEFINITION_ID,
90
+ bucket
91
+ });
92
+ }
93
+ }
@@ -0,0 +1,26 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
2
+ import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
3
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
4
+
5
+ export class MongoParameterCompactorV1 extends MongoParameterCompactor {
6
+ declare protected readonly db: VersionedPowerSyncMongoV1;
7
+
8
+ protected async getCollections(): Promise<mongo.Collection<mongo.Document>[]> {
9
+ return [this.db.parameterIndexV1 as unknown as mongo.Collection<mongo.Document>];
10
+ }
11
+
12
+ protected collectionFilter(): mongo.Document {
13
+ return {
14
+ 'key.g': this.group_id
15
+ };
16
+ }
17
+
18
+ protected deleteFilter(doc: mongo.Document): mongo.Document {
19
+ return {
20
+ 'key.g': doc.key.g as number,
21
+ lookup: doc.lookup,
22
+ _id: { $lte: doc._id },
23
+ key: doc.key
24
+ };
25
+ }
26
+ }
@@ -0,0 +1,432 @@
1
+ import * as lib_mongo from '@powersync/lib-service-mongodb';
2
+ import { mongo } from '@powersync/lib-service-mongodb';
3
+ import {
4
+ CheckpointChanges,
5
+ deserializeParameterLookup,
6
+ GetCheckpointChangesOptions,
7
+ InternalOpId,
8
+ internalToExternalOpId,
9
+ ParameterSetLimitExceededError,
10
+ ProtocolOpId,
11
+ storage,
12
+ utils
13
+ } from '@powersync/service-core';
14
+ import { JSONBig } from '@powersync/service-jsonbig';
15
+ import { ScopedParameterLookup, SqliteJsonRow } from '@powersync/service-sync-rules';
16
+ import * as bson from 'bson';
17
+ import { idPrefixFilter, mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../../utils/util.js';
18
+ import { MongoBucketStorage } from '../../MongoBucketStorage.js';
19
+ import {
20
+ MongoSyncBucketStorageCheckpoint,
21
+ MongoSyncBucketStorageContext
22
+ } from '../common/MongoSyncBucketStorageContext.js';
23
+ import { CommonSourceTableDocument, SourceKey } from '../models.js';
24
+ import { MongoBucketBatchOptions } from '../MongoBucketBatch.js';
25
+ import { MongoChecksums } from '../MongoChecksums.js';
26
+ import { MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
27
+ import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
28
+ import { MongoPersistedSyncRulesContent } from '../MongoPersistedSyncRulesContent.js';
29
+ import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from '../MongoSyncBucketStorage.js';
30
+ import { BucketDataDocumentV1, BucketDataKeyV1, BucketStateDocument, loadBucketDataDocumentV1 } from './models.js';
31
+ import { MongoBucketBatchV1 } from './MongoBucketBatchV1.js';
32
+ import { MongoChecksumsV1 } from './MongoChecksumsV1.js';
33
+ import { MongoCompactorV1 } from './MongoCompactorV1.js';
34
+ import { MongoParameterCompactorV1 } from './MongoParameterCompactorV1.js';
35
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
36
+
37
+ export class MongoSyncBucketStorageV1 extends MongoSyncBucketStorage {
38
+ // Declare types to be more specific
39
+ declare readonly db: VersionedPowerSyncMongoV1;
40
+ declare readonly checksums: MongoChecksumsV1;
41
+
42
+ constructor(
43
+ factory: MongoBucketStorage,
44
+ group_id: number,
45
+ sync_rules: MongoPersistedSyncRulesContent,
46
+ slot_name: string,
47
+ writeCheckpointMode: storage.WriteCheckpointMode | undefined,
48
+ options: MongoSyncBucketStorageOptions
49
+ ) {
50
+ super(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
51
+ }
52
+
53
+ protected async initializeVersionStorage(): Promise<void> {}
54
+
55
+ protected createWriterImpl(batchOptions: MongoBucketBatchOptions): storage.BucketStorageBatch {
56
+ return new MongoBucketBatchV1(batchOptions);
57
+ }
58
+
59
+ protected createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums {
60
+ return new MongoChecksumsV1(this.db, this.group_id, {
61
+ ...options.checksumOptions,
62
+ storageConfig: options?.storageConfig,
63
+ mapping: this.sync_rules.mapping
64
+ });
65
+ }
66
+
67
+ createMongoCompactor(options: MongoCompactOptions): MongoCompactor {
68
+ return new MongoCompactorV1(this, this.db, options);
69
+ }
70
+
71
+ protected createMongoParameterCompactor(
72
+ checkpoint: InternalOpId,
73
+ options: storage.CompactOptions
74
+ ): MongoParameterCompactor {
75
+ return new MongoParameterCompactorV1(this.db, this.group_id, checkpoint, options);
76
+ }
77
+
78
+ protected sourceTableBaseId(): Partial<CommonSourceTableDocument> {
79
+ return { group_id: this.group_id };
80
+ }
81
+
82
+ protected augmentCreatedSourceTableDocument(
83
+ _createDoc: CommonSourceTableDocument,
84
+ _options: storage.ResolveTableOptions,
85
+ _candidateSourceTable: storage.SourceTable
86
+ ): void {}
87
+
88
+ protected async initializeResolvedSourceRecords(_sourceTableId: bson.ObjectId): Promise<void> {}
89
+
90
+ protected override get versionContext(): MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1> {
91
+ return {
92
+ db: this.db,
93
+ group_id: this.group_id,
94
+ mapping: this.mapping
95
+ };
96
+ }
97
+
98
+ protected getParameterSetsImpl(
99
+ checkpoint: MongoSyncBucketStorageCheckpoint,
100
+ lookups: ScopedParameterLookup[],
101
+ limit: number
102
+ ): Promise<SqliteJsonRow[]> {
103
+ return getParameterSetsV1(this.versionContext, checkpoint, lookups, limit);
104
+ }
105
+
106
+ protected getBucketDataBatchImpl(
107
+ checkpoint: utils.InternalOpId,
108
+ dataBuckets: storage.BucketDataRequest[],
109
+ options?: storage.BucketDataBatchOptions
110
+ ): AsyncIterable<storage.SyncBucketDataChunk> {
111
+ return getBucketDataBatchV1(this.versionContext, checkpoint, dataBuckets, options);
112
+ }
113
+
114
+ protected async clearBucketData(signal?: AbortSignal): Promise<void> {
115
+ await this.clearDeleteMany(
116
+ 'bucket data',
117
+ () =>
118
+ this.db.bucket_data.deleteMany(
119
+ {
120
+ _id: idPrefixFilter<BucketDataKeyV1>({ g: this.group_id }, ['b', 'o'])
121
+ },
122
+ { maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
123
+ ),
124
+ signal
125
+ );
126
+ }
127
+
128
+ protected async clearParameterIndexes(signal?: AbortSignal): Promise<void> {
129
+ await this.clearDeleteMany(
130
+ 'parameter index',
131
+ () =>
132
+ this.db.parameterIndexV1.deleteMany(
133
+ {
134
+ 'key.g': this.group_id
135
+ },
136
+ { maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
137
+ ),
138
+ signal
139
+ );
140
+ }
141
+
142
+ protected async clearSourceRecords(signal?: AbortSignal): Promise<void> {
143
+ await this.clearDeleteMany(
144
+ 'source records',
145
+ () =>
146
+ this.db.sourceRecordsV1.deleteMany(
147
+ {
148
+ _id: idPrefixFilter<SourceKey>({ g: this.group_id }, ['t', 'k'])
149
+ },
150
+ { maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
151
+ ),
152
+ signal
153
+ );
154
+ }
155
+
156
+ protected async clearBucketState(signal?: AbortSignal): Promise<void> {
157
+ await this.clearDeleteMany(
158
+ 'bucket state',
159
+ () =>
160
+ this.db.bucketStateV1.deleteMany(
161
+ {
162
+ _id: idPrefixFilter<BucketStateDocument['_id']>({ g: this.group_id }, ['b'])
163
+ },
164
+ { maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
165
+ ),
166
+ signal
167
+ );
168
+ }
169
+
170
+ protected async clearSourceTables(signal?: AbortSignal): Promise<void> {
171
+ await this.clearDeleteMany(
172
+ 'source tables',
173
+ () =>
174
+ this.db.commonSourceTables(this.group_id).deleteMany(
175
+ {
176
+ group_id: this.group_id
177
+ },
178
+ { maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
179
+ ),
180
+ signal
181
+ );
182
+ }
183
+
184
+ protected getDataBucketChangesImpl(
185
+ options: GetCheckpointChangesOptions
186
+ ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
187
+ return getDataBucketChangesV1(this.versionContext, options);
188
+ }
189
+
190
+ protected getParameterBucketChangesImpl(
191
+ options: GetCheckpointChangesOptions
192
+ ): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
193
+ return getParameterBucketChangesV1(this.versionContext, options);
194
+ }
195
+ }
196
+
197
+ export async function getParameterSetsV1(
198
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
199
+ checkpoint: MongoSyncBucketStorageCheckpoint,
200
+ lookups: ScopedParameterLookup[],
201
+ limit: number
202
+ ): Promise<SqliteJsonRow[]> {
203
+ return ctx.db.client.withSession({ snapshot: true }, async (session) => {
204
+ setSessionSnapshotTime(session, checkpoint.snapshotTime);
205
+ const lookupFilter = lookups.map((lookup) => {
206
+ return storage.serializeLookup(lookup);
207
+ });
208
+ const rows = await ctx.db.parameterIndexV1
209
+ .aggregate(
210
+ [
211
+ {
212
+ $match: {
213
+ 'key.g': ctx.group_id,
214
+ lookup: { $in: lookupFilter },
215
+ _id: { $lte: checkpoint.checkpoint }
216
+ }
217
+ },
218
+ {
219
+ $sort: {
220
+ _id: -1
221
+ }
222
+ },
223
+ {
224
+ $group: {
225
+ _id: { key: '$key', lookup: '$lookup' },
226
+ bucket_parameters: {
227
+ $first: '$bucket_parameters'
228
+ }
229
+ }
230
+ },
231
+ { $unwind: '$bucket_parameters' },
232
+ {
233
+ $limit: limit + 1
234
+ }
235
+ ],
236
+ {
237
+ session,
238
+ readConcern: 'snapshot',
239
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
240
+ }
241
+ )
242
+ .toArray()
243
+ .catch((e) => {
244
+ throw lib_mongo.mapQueryError(e, 'while evaluating parameter queries');
245
+ });
246
+
247
+ const results = rows.map((row) => row.bucket_parameters);
248
+ if (results.length > limit) {
249
+ throw new ParameterSetLimitExceededError(limit);
250
+ }
251
+ return results;
252
+ });
253
+ }
254
+
255
+ export async function* getBucketDataBatchV1(
256
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
257
+ checkpoint: utils.InternalOpId,
258
+ dataBuckets: storage.BucketDataRequest[],
259
+ options?: storage.BucketDataBatchOptions
260
+ ): AsyncIterable<storage.SyncBucketDataChunk> {
261
+ if (dataBuckets.length == 0) {
262
+ return;
263
+ }
264
+ let filters: mongo.Filter<BucketDataDocumentV1>[] = [];
265
+ const bucketMap = new Map(dataBuckets.map((request) => [request.bucket, request.start]));
266
+
267
+ if (checkpoint == null) {
268
+ throw new Error('checkpoint is null');
269
+ }
270
+ const end = checkpoint;
271
+ for (let { bucket: name, start } of dataBuckets) {
272
+ filters.push({
273
+ _id: {
274
+ $gt: {
275
+ g: ctx.group_id,
276
+ b: name,
277
+ o: start
278
+ },
279
+ $lte: {
280
+ g: ctx.group_id,
281
+ b: name,
282
+ o: end as any
283
+ }
284
+ }
285
+ });
286
+ }
287
+
288
+ const batchLimit = options?.limit ?? storage.DEFAULT_DOCUMENT_BATCH_LIMIT;
289
+ const chunkSizeLimitBytes = options?.chunkLimitBytes ?? storage.DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES;
290
+
291
+ const cursor = ctx.db.bucket_data.find(
292
+ {
293
+ $or: filters
294
+ },
295
+ {
296
+ session: undefined,
297
+ sort: { _id: 1 },
298
+ limit: batchLimit,
299
+ batchSize: batchLimit + 1,
300
+ raw: true,
301
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
302
+ }
303
+ ) as unknown as mongo.FindCursor<Buffer>;
304
+
305
+ let { data, hasMore: batchHasMore } = await readSingleBatch(cursor).catch((e) => {
306
+ throw lib_mongo.mapQueryError(e, 'while reading bucket data');
307
+ });
308
+ if (data.length == batchLimit) {
309
+ batchHasMore = true;
310
+ }
311
+
312
+ let chunkSizeBytes = 0;
313
+ let currentChunk: utils.SyncBucketData | null = null;
314
+ let targetOp: InternalOpId | null = null;
315
+
316
+ for (let rawData of data) {
317
+ const row = loadBucketDataDocumentV1(
318
+ bson.deserialize(rawData, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV1
319
+ );
320
+ const bucket = row.bucketKey.bucket;
321
+
322
+ if (currentChunk == null || currentChunk.bucket != bucket || chunkSizeBytes >= chunkSizeLimitBytes) {
323
+ let start: ProtocolOpId | undefined = undefined;
324
+ if (currentChunk != null) {
325
+ if (currentChunk.bucket == bucket) {
326
+ currentChunk.has_more = true;
327
+ start = currentChunk.next_after;
328
+ }
329
+
330
+ const yieldChunk = currentChunk;
331
+ currentChunk = null;
332
+ chunkSizeBytes = 0;
333
+ yield { chunkData: yieldChunk, targetOp: targetOp };
334
+ targetOp = null;
335
+ }
336
+
337
+ if (start == null) {
338
+ const startOpId = bucketMap.get(bucket);
339
+ if (startOpId == null) {
340
+ throw new Error(`data for unexpected bucket: ${bucket}`);
341
+ }
342
+ start = internalToExternalOpId(startOpId);
343
+ }
344
+ currentChunk = {
345
+ bucket,
346
+ after: start,
347
+ has_more: false,
348
+ data: [],
349
+ next_after: start
350
+ };
351
+ targetOp = null;
352
+ }
353
+
354
+ const entry = mapOpEntry(row);
355
+
356
+ if (row.target_op != null && (targetOp == null || row.target_op > targetOp)) {
357
+ targetOp = row.target_op;
358
+ }
359
+
360
+ currentChunk.data.push(entry);
361
+ currentChunk.next_after = entry.op_id;
362
+ chunkSizeBytes += rawData.byteLength;
363
+ }
364
+
365
+ if (currentChunk != null) {
366
+ const yieldChunk = currentChunk;
367
+ yieldChunk.has_more = batchHasMore;
368
+ yield { chunkData: yieldChunk, targetOp: targetOp };
369
+ }
370
+ }
371
+
372
+ export async function getDataBucketChangesV1(
373
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
374
+ options: GetCheckpointChangesOptions
375
+ ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
376
+ const limit = 1000;
377
+ const bucketStateUpdates = await ctx.db.bucketStateV1
378
+ .find(
379
+ {
380
+ '_id.g': ctx.group_id,
381
+ last_op: { $gt: options.lastCheckpoint.checkpoint }
382
+ },
383
+ {
384
+ projection: {
385
+ '_id.b': 1
386
+ },
387
+ limit: limit + 1,
388
+ batchSize: limit + 2,
389
+ singleBatch: true
390
+ }
391
+ )
392
+ .toArray();
393
+
394
+ const buckets = bucketStateUpdates.map((doc) => doc._id.b);
395
+ const invalidateDataBuckets = buckets.length > limit;
396
+
397
+ return {
398
+ invalidateDataBuckets,
399
+ updatedDataBuckets: invalidateDataBuckets ? new Set<string>() : new Set(buckets)
400
+ };
401
+ }
402
+
403
+ export async function getParameterBucketChangesV1(
404
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV1>,
405
+ options: GetCheckpointChangesOptions
406
+ ): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
407
+ const limit = 1000;
408
+ const parameterUpdates = await ctx.db.parameterIndexV1
409
+ .find(
410
+ {
411
+ _id: { $gt: options.lastCheckpoint.checkpoint, $lte: options.nextCheckpoint.checkpoint },
412
+ 'key.g': ctx.group_id
413
+ },
414
+ {
415
+ projection: {
416
+ lookup: 1
417
+ },
418
+ limit: limit + 1,
419
+ batchSize: limit + 2,
420
+ singleBatch: true
421
+ }
422
+ )
423
+ .toArray();
424
+ const invalidateParameterUpdates = parameterUpdates.length > limit;
425
+
426
+ return {
427
+ invalidateParameterBuckets: invalidateParameterUpdates,
428
+ updatedParameterLookups: invalidateParameterUpdates
429
+ ? new Set<string>()
430
+ : new Set<string>(parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookup(p.lookup))))
431
+ };
432
+ }