@powersync/service-module-mongodb-storage 0.0.0-dev-20260602073133 → 0.0.0-dev-20260909133214

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 (275) hide show
  1. package/CHANGELOG.md +177 -5
  2. package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.d.ts +3 -0
  3. package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.js +41 -0
  4. package/dist/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.js.map +1 -0
  5. package/dist/storage/MongoBucketStorage.d.ts +71 -18
  6. package/dist/storage/MongoBucketStorage.js +516 -202
  7. package/dist/storage/MongoBucketStorage.js.map +1 -1
  8. package/dist/storage/implementation/MongoBucketBatch.d.ts +71 -29
  9. package/dist/storage/implementation/MongoBucketBatch.js +140 -66
  10. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  11. package/dist/storage/implementation/MongoChecksums.d.ts +41 -13
  12. package/dist/storage/implementation/MongoChecksums.js +36 -121
  13. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  14. package/dist/storage/implementation/MongoCompactor.d.ts +28 -90
  15. package/dist/storage/implementation/MongoCompactor.js +87 -481
  16. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  17. package/dist/storage/implementation/MongoHydrationState.d.ts +11 -0
  18. package/dist/storage/implementation/MongoHydrationState.js +49 -0
  19. package/dist/storage/implementation/MongoHydrationState.js.map +1 -0
  20. package/dist/storage/implementation/MongoParameterCompactor.d.ts +82 -8
  21. package/dist/storage/implementation/MongoParameterCompactor.js +312 -76
  22. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  23. package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +21 -0
  24. package/dist/storage/implementation/MongoParsedSyncConfigSet.js +93 -0
  25. package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -0
  26. package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +36 -0
  27. package/dist/storage/implementation/MongoPersistedReplicationStream.js +90 -0
  28. package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -0
  29. package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +25 -0
  30. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +125 -0
  31. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
  32. package/dist/storage/implementation/MongoStorageProvider.js +41 -4
  33. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  34. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +81 -37
  35. package/dist/storage/implementation/MongoSyncBucketStorage.js +159 -86
  36. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  37. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +1 -1
  38. package/dist/storage/implementation/MongoSyncRulesLock.js +23 -11
  39. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  40. package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +11 -8
  41. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +159 -33
  42. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  43. package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +11 -5
  44. package/dist/storage/implementation/SyncRuleStateUpdate.js +14 -6
  45. package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -1
  46. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +28 -0
  47. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js +2 -0
  48. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js.map +1 -0
  49. package/dist/storage/implementation/common/PersistedBatch.d.ts +34 -2
  50. package/dist/storage/implementation/common/PersistedBatch.js +63 -10
  51. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
  52. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
  53. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
  54. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
  55. package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
  56. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
  57. package/dist/storage/implementation/db.d.ts +4 -1
  58. package/dist/storage/implementation/db.js +31 -16
  59. package/dist/storage/implementation/db.js.map +1 -1
  60. package/dist/storage/implementation/models.d.ts +55 -13
  61. package/dist/storage/implementation/models.js.map +1 -1
  62. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +15 -1
  63. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +135 -107
  64. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
  65. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +4 -4
  66. package/dist/storage/implementation/v1/MongoChecksumsV1.js +119 -7
  67. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
  68. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +47 -7
  69. package/dist/storage/implementation/v1/MongoCompactorV1.js +478 -8
  70. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
  71. package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +16 -2
  72. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -9
  73. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -1
  74. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +33 -27
  75. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +146 -73
  76. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
  77. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +9 -10
  78. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +2 -8
  79. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -1
  80. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +2 -1
  81. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +3 -0
  82. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -1
  83. package/dist/storage/implementation/v1/models.d.ts +10 -1
  84. package/dist/storage/implementation/v1/models.js +1 -0
  85. package/dist/storage/implementation/v1/models.js.map +1 -1
  86. package/dist/storage/implementation/v3/BucketDataContextV3.d.ts +13 -0
  87. package/dist/storage/implementation/v3/BucketDataContextV3.js +23 -0
  88. package/dist/storage/implementation/v3/BucketDataContextV3.js.map +1 -0
  89. package/dist/storage/implementation/v3/CompactionLease.d.ts +50 -0
  90. package/dist/storage/implementation/v3/CompactionLease.js +131 -0
  91. package/dist/storage/implementation/v3/CompactionLease.js.map +1 -0
  92. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +16 -5
  93. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +393 -262
  94. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
  95. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +30 -9
  96. package/dist/storage/implementation/v3/MongoChecksumsV3.js +161 -20
  97. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
  98. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +108 -18
  99. package/dist/storage/implementation/v3/MongoCompactorV3.js +1115 -47
  100. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
  101. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +12 -3
  102. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +13 -8
  103. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -1
  104. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +54 -0
  105. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +319 -0
  106. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
  107. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +61 -36
  108. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +353 -161
  109. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
  110. package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.d.ts +26 -0
  111. package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js +82 -0
  112. package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js.map +1 -0
  113. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +8 -3
  114. package/dist/storage/implementation/v3/PersistedBatchV3.js +131 -29
  115. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
  116. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +3 -3
  117. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +13 -11
  118. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
  119. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +26 -14
  120. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +73 -29
  121. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
  122. package/dist/storage/implementation/v3/bucket-format.d.ts +13 -0
  123. package/dist/storage/implementation/v3/bucket-format.js +79 -0
  124. package/dist/storage/implementation/v3/bucket-format.js.map +1 -0
  125. package/dist/storage/implementation/v3/chunking.d.ts +10 -0
  126. package/dist/storage/implementation/v3/chunking.js +30 -0
  127. package/dist/storage/implementation/v3/chunking.js.map +1 -0
  128. package/dist/storage/implementation/v3/compact-utils.d.ts +107 -0
  129. package/dist/storage/implementation/v3/compact-utils.js +216 -0
  130. package/dist/storage/implementation/v3/compact-utils.js.map +1 -0
  131. package/dist/storage/implementation/v3/compaction-constants.d.ts +5 -0
  132. package/dist/storage/implementation/v3/compaction-constants.js +6 -0
  133. package/dist/storage/implementation/v3/compaction-constants.js.map +1 -0
  134. package/dist/storage/implementation/v3/models.d.ts +187 -23
  135. package/dist/storage/implementation/v3/models.js +11 -28
  136. package/dist/storage/implementation/v3/models.js.map +1 -1
  137. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +20 -0
  138. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +48 -0
  139. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -0
  140. package/dist/storage/implementation/v3/object-storage/ObjectStorage.d.ts +33 -0
  141. package/dist/storage/implementation/v3/object-storage/ObjectStorage.js +12 -0
  142. package/dist/storage/implementation/v3/object-storage/ObjectStorage.js.map +1 -0
  143. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +68 -0
  144. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +154 -0
  145. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -0
  146. package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.d.ts +37 -0
  147. package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js +167 -0
  148. package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js.map +1 -0
  149. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.d.ts +67 -0
  150. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +439 -0
  151. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -0
  152. package/dist/storage/implementation/v3/source-table-utils.d.ts +63 -0
  153. package/dist/storage/implementation/v3/source-table-utils.js +206 -0
  154. package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
  155. package/dist/storage/storage-index.d.ts +5 -4
  156. package/dist/storage/storage-index.js +5 -4
  157. package/dist/storage/storage-index.js.map +1 -1
  158. package/dist/types/types.d.ts +71 -3
  159. package/dist/types/types.js +58 -2
  160. package/dist/types/types.js.map +1 -1
  161. package/dist/utils/test-utils.d.ts +1 -2
  162. package/dist/utils/test-utils.js +7 -1
  163. package/dist/utils/test-utils.js.map +1 -1
  164. package/dist/utils/util.d.ts +9 -2
  165. package/dist/utils/util.js +131 -5
  166. package/dist/utils/util.js.map +1 -1
  167. package/package.json +13 -19
  168. package/src/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.ts +55 -0
  169. package/src/storage/MongoBucketStorage.ts +728 -242
  170. package/src/storage/implementation/MongoBucketBatch.ts +196 -91
  171. package/src/storage/implementation/MongoChecksums.ts +93 -153
  172. package/src/storage/implementation/MongoCompactor.ts +106 -631
  173. package/src/storage/implementation/MongoHydrationState.ts +61 -0
  174. package/src/storage/implementation/MongoParameterCompactor.ts +392 -87
  175. package/src/storage/implementation/MongoParsedSyncConfigSet.ts +122 -0
  176. package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
  177. package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +156 -0
  178. package/src/storage/implementation/MongoStorageProvider.ts +48 -4
  179. package/src/storage/implementation/MongoSyncBucketStorage.ts +226 -118
  180. package/src/storage/implementation/MongoSyncRulesLock.ts +28 -12
  181. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +194 -52
  182. package/src/storage/implementation/SyncRuleStateUpdate.ts +13 -6
  183. package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +31 -0
  184. package/src/storage/implementation/common/PersistedBatch.ts +83 -15
  185. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
  186. package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
  187. package/src/storage/implementation/db.ts +40 -22
  188. package/src/storage/implementation/models.ts +58 -14
  189. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +174 -125
  190. package/src/storage/implementation/v1/MongoChecksumsV1.ts +154 -12
  191. package/src/storage/implementation/v1/MongoCompactorV1.ts +594 -25
  192. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +25 -9
  193. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +226 -129
  194. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +18 -27
  195. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
  196. package/src/storage/implementation/v1/models.ts +12 -1
  197. package/src/storage/implementation/v3/BucketDataContextV3.ts +32 -0
  198. package/src/storage/implementation/v3/CompactionLease.ts +152 -0
  199. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +502 -324
  200. package/src/storage/implementation/v3/MongoChecksumsV3.ts +250 -32
  201. package/src/storage/implementation/v3/MongoCompactorV3.ts +1439 -70
  202. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +19 -9
  203. package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +473 -0
  204. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +453 -211
  205. package/src/storage/implementation/v3/MongoWriteCheckpointAPIV3.ts +111 -0
  206. package/src/storage/implementation/v3/PersistedBatchV3.ts +169 -45
  207. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
  208. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +99 -38
  209. package/src/storage/implementation/v3/bucket-format.ts +99 -0
  210. package/src/storage/implementation/v3/chunking.ts +40 -0
  211. package/src/storage/implementation/v3/compact-utils.ts +320 -0
  212. package/src/storage/implementation/v3/compaction-constants.ts +5 -0
  213. package/src/storage/implementation/v3/models.ts +228 -58
  214. package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +62 -0
  215. package/src/storage/implementation/v3/object-storage/ObjectStorage.ts +44 -0
  216. package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +182 -0
  217. package/src/storage/implementation/v3/object-storage/ObjectStorageUsage.ts +230 -0
  218. package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +573 -0
  219. package/src/storage/implementation/v3/source-table-utils.ts +398 -0
  220. package/src/storage/storage-index.ts +14 -4
  221. package/src/types/types.ts +76 -2
  222. package/src/utils/test-utils.ts +8 -3
  223. package/src/utils/util.ts +188 -7
  224. package/test/src/__snapshots__/storage.test.ts.snap +4 -54
  225. package/test/src/__snapshots__/storage_sync.test.ts.snap +652 -645
  226. package/test/src/cleanup-stopped-sync-configs.test.ts +698 -0
  227. package/test/src/compact-utils.test.ts +226 -0
  228. package/test/src/helpers/MemoryObjectStorage.ts +61 -0
  229. package/test/src/helpers/s3TestFactory.ts +69 -0
  230. package/test/src/object_storage_lifecycle.test.ts +59 -0
  231. package/test/src/object_storage_usage.test.ts +255 -0
  232. package/test/src/parameter_compacting_v1.test.ts +120 -0
  233. package/test/src/parameter_compacting_v3.test.ts +192 -0
  234. package/test/src/parameter_compaction_fence.test.ts +276 -0
  235. package/test/src/setup.ts +6 -1
  236. package/test/src/storage.test.ts +478 -13
  237. package/test/src/storage_compacting.test.ts +2516 -107
  238. package/test/src/storage_object_storage_inline_threshold.test.ts +116 -0
  239. package/test/src/storage_s3_checksums.test.ts +190 -0
  240. package/test/src/storage_s3_compaction_lifecycle.test.ts +543 -0
  241. package/test/src/storage_s3_reading.test.ts +768 -0
  242. package/test/src/storage_s3_writing.test.ts +136 -0
  243. package/test/src/storage_sync.test.ts +1507 -76
  244. package/test/src/storeCurrentData.test.ts +227 -0
  245. package/test/src/util.ts +3 -2
  246. package/tsconfig.json +1 -4
  247. package/tsconfig.tsbuildinfo +1 -1
  248. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -15
  249. package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
  250. package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
  251. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
  252. package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -67
  253. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
  254. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -29
  255. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -76
  256. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
  257. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
  258. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
  259. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
  260. package/dist/storage/implementation/common/SingleBucketStore.d.ts +0 -54
  261. package/dist/storage/implementation/common/SingleBucketStore.js +0 -3
  262. package/dist/storage/implementation/common/SingleBucketStore.js.map +0 -1
  263. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -4
  264. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
  265. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
  266. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +0 -18
  267. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +0 -48
  268. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +0 -1
  269. package/src/storage/implementation/BucketDefinitionMapping.ts +0 -75
  270. package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -82
  271. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -101
  272. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +0 -15
  273. package/src/storage/implementation/common/SingleBucketStore.ts +0 -63
  274. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -11
  275. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +0 -68
@@ -1,5 +1,7 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
1
2
  import { ReplicationAssertionError } from '@powersync/lib-services-framework';
2
- import { ColumnDescriptor, SourceTable, storage } from '@powersync/service-core';
3
+ import { ColumnDescriptor, InternalOpId, SourceTable, storage } from '@powersync/service-core';
4
+ import { HydratedSyncConfig } from '@powersync/service-sync-rules';
3
5
  import * as bson from 'bson';
4
6
  import { mongoTableId } from '../../../utils/util.js';
5
7
  import { calculateCheckpointState } from '../CheckpointState.js';
@@ -12,19 +14,32 @@ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
12
14
  import { SourceTableDocumentV1, SyncRuleDocumentV1 } from './models.js';
13
15
 
14
16
  export class MongoBucketBatchV1 extends MongoBucketBatch {
15
- declare public readonly db: VersionedPowerSyncMongoV1;
17
+ declare readonly db: VersionedPowerSyncMongoV1;
16
18
 
17
19
  private readonly store: SourceRecordStore;
18
20
  private needsActivation = true;
19
21
  private lastWaitingLogThrottled = 0;
20
22
 
23
+ /**
24
+ * The last persisted op that is not yet covered by a checkpoint, if any.
25
+ *
26
+ * Seeded from the persisted keepalive_op, so that ops persisted before a restart can still be
27
+ * included in the next checkpoint.
28
+ */
29
+ private persisted_op: InternalOpId | null = null;
30
+
21
31
  constructor(options: MongoBucketBatchOptions) {
22
32
  super(options);
23
- this.store = new SourceRecordStoreV1(this.db, this.group_id);
33
+ this.persisted_op = options.keepaliveOp ?? null;
34
+ this.store = new SourceRecordStoreV1(this.db, this.replicationStreamId);
35
+ }
36
+
37
+ protected override recordPersistedOp(lastOp: InternalOpId): void {
38
+ this.persisted_op = lastOp;
24
39
  }
25
40
 
26
41
  protected createPersistedBatch(writtenSize: number): PersistedBatch {
27
- return new PersistedBatchV1(this.db, this.group_id, this.mapping, writtenSize, {
42
+ return new PersistedBatchV1(this.db, this.replicationStreamId, this.mapping, writtenSize, {
28
43
  logger: this.logger
29
44
  });
30
45
  }
@@ -32,15 +47,45 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
32
47
  protected get sourceRecordStore(): SourceRecordStore {
33
48
  return this.store;
34
49
  }
35
-
36
50
  protected async cleanupDroppedSourceTables(_tables: SourceTable[]) {
37
51
  // No-op for V1: source records live in a shared collection.
38
52
  }
39
53
 
54
+ protected override async batchCreateCustomWriteCheckpoints(
55
+ session: mongo.ClientSession,
56
+ opId: InternalOpId
57
+ ): Promise<void> {
58
+ await this.db.custom_write_checkpoints.bulkWrite(
59
+ this.write_checkpoint_batch.map((checkpointOptions) => {
60
+ const set: Record<string, unknown> = {
61
+ checkpoint: checkpointOptions.checkpoint,
62
+ sync_rules_id: checkpointOptions.sync_rules_id,
63
+ op_id: opId
64
+ };
65
+ if (checkpointOptions.checkpoint_requested_at != null) {
66
+ set.checkpoint_requested_at = checkpointOptions.checkpoint_requested_at;
67
+ }
68
+
69
+ return {
70
+ updateOne: {
71
+ filter: { user_id: checkpointOptions.user_id, sync_rules_id: checkpointOptions.sync_rules_id },
72
+ update: {
73
+ $set: set,
74
+ ...(checkpointOptions.checkpoint_requested_at == null ? { $unset: { checkpoint_requested_at: 1 } } : {})
75
+ },
76
+ upsert: true
77
+ }
78
+ };
79
+ }),
80
+ { session }
81
+ );
82
+ }
83
+
40
84
  async resolveTables(options: storage.ResolveTablesOptions): Promise<storage.ResolveTablesResult> {
41
- const syncRules = options.syncRules ?? this.sync_rules;
85
+ const syncRules = options.parsedSyncConfig?.hydratedSyncConfig ?? this.sync_rules;
86
+ const reconcile = options.reconcileSourceTables ?? storage.defaultSourceTableReconciler;
42
87
  const { connection_id, source } = options;
43
- const { schema, name, objectId, replicaIdColumns, connectionTag } = source;
88
+ const { schema, name, objectId, replicaIdColumns, connectionTag, sendsCompleteRows } = source;
44
89
 
45
90
  const normalizedReplicaIdColumns = replicaIdColumns.map((column) => ({
46
91
  name: column.name,
@@ -50,23 +95,41 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
50
95
 
51
96
  let result: storage.ResolveTablesResult | null = null;
52
97
  await this.db.client.withSession(async (session) => {
53
- const col = this.db.commonSourceTables(this.group_id);
54
- const filter: any = {
55
- group_id: this.group_id,
56
- connection_id,
57
- schema_name: schema,
58
- table_name: name,
59
- replica_id_columns2: normalizedReplicaIdColumns
60
- };
98
+ const col = this.db.sourceTablesV1(this.replicationStreamId);
99
+
100
+ // Find records that overlap by name or relation id.
101
+ const orClauses: Record<string, unknown>[] = [{ schema_name: schema, table_name: name }];
61
102
  if (objectId != null) {
62
- filter.relation_id = objectId;
103
+ orClauses.push({ relation_id: objectId });
104
+ }
105
+ const candidateDocs = await col
106
+ .find({ group_id: this.replicationStreamId, connection_id, $or: orClauses }, { session })
107
+ .toArray();
108
+
109
+ const candidateTables = candidateDocs.map((doc) =>
110
+ this.hydrateSourceTable(doc, connectionTag, syncRules, sendsCompleteRows)
111
+ );
112
+ const candidates = candidateTables.map((table) => table.clone());
113
+ const resolution = await reconcile({ source, candidates });
114
+ storage.validateSourceTableCandidateResolution(candidates, resolution);
115
+
116
+ for (const { id, sourceMetadata } of storage.diffSourceTableUpdates(candidateTables, resolution)) {
117
+ await col.updateOne({ _id: mongoTableId(id) }, { $set: { source_metadata: sourceMetadata } }, { session });
63
118
  }
64
119
 
65
- let doc = await col.findOne(filter, { session });
66
- if (doc == null) {
67
- doc = {
120
+ const materializedResolution = storage.materializeSourceTableResolution(candidateTables, resolution);
121
+ const compatibleTables = materializedResolution.compatibleTables;
122
+
123
+ // V1 keeps one record, preferring one that has already been snapshotted.
124
+ const reuse = compatibleTables.find((table) => table.snapshotComplete) ?? compatibleTables[0] ?? null;
125
+
126
+ let sourceTable: storage.SourceTable;
127
+ if (reuse != null) {
128
+ sourceTable = reuse;
129
+ } else {
130
+ const doc: SourceTableDocumentV1 = {
68
131
  _id: options.idGenerator ? (options.idGenerator() as bson.ObjectId) : new bson.ObjectId(),
69
- group_id: this.group_id,
132
+ group_id: this.replicationStreamId,
70
133
  connection_id,
71
134
  relation_id: objectId,
72
135
  schema_name: schema,
@@ -74,68 +137,18 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
74
137
  replica_id_columns: null,
75
138
  replica_id_columns2: normalizedReplicaIdColumns,
76
139
  snapshot_done: false,
77
- snapshot_status: undefined
140
+ snapshot_status: undefined,
141
+ source_metadata: resolution.newTableValues.sourceMetadata
78
142
  };
79
143
  await col.insertOne(doc, { session });
144
+ sourceTable = this.hydrateSourceTable(doc, connectionTag, syncRules, sendsCompleteRows);
80
145
  }
81
146
 
82
- const sourceTable = new storage.SourceTable({
83
- id: doc._id,
84
- ref: source,
85
- objectId,
86
- replicaIdColumns,
87
- snapshotComplete: doc.snapshot_done ?? true,
88
- ...syncRules.getMatchingSources(source)
89
- });
90
- sourceTable.syncEvent = syncRules.tableTriggersEvent(source);
91
- sourceTable.syncData = sourceTable.bucketDataSources.length > 0;
92
- sourceTable.syncParameters = sourceTable.parameterLookupSources.length > 0;
93
- sourceTable.snapshotStatus =
94
- doc.snapshot_status == null
95
- ? undefined
96
- : {
97
- lastKey: doc.snapshot_status.last_key?.buffer ?? null,
98
- totalEstimatedCount: doc.snapshot_status.total_estimated_count,
99
- replicatedCount: doc.snapshot_status.replicated_count
100
- };
101
-
102
- const truncateFilter = [{ schema_name: schema, table_name: name }] as any[];
103
- if (objectId != null) {
104
- truncateFilter.push({ relation_id: objectId });
105
- }
106
- const truncate = await col
107
- .find(
108
- {
109
- group_id: this.group_id,
110
- connection_id,
111
- _id: { $ne: doc._id },
112
- $or: truncateFilter
113
- },
114
- { session }
115
- )
116
- .toArray();
117
- const dropTables = truncate.map((dropDoc) => {
118
- const ref = {
119
- connectionTag,
120
- schema: dropDoc.schema_name,
121
- name: dropDoc.table_name
122
- };
123
- const table = new storage.SourceTable({
124
- id: dropDoc._id,
125
- ref,
126
- objectId: dropDoc.relation_id,
127
- replicaIdColumns:
128
- dropDoc.replica_id_columns2?.map(
129
- (c) => ({ name: c.name, typeId: c.type_oid, type: c.type }) satisfies ColumnDescriptor
130
- ) ?? [],
131
- snapshotComplete: dropDoc.snapshot_done ?? true,
132
- ...syncRules.getMatchingSources(ref)
133
- });
134
- table.syncEvent = syncRules.tableTriggersEvent(ref);
135
- table.syncData = table.bucketDataSources.length > 0;
136
- table.syncParameters = table.parameterLookupSources.length > 0;
137
- return table;
138
- });
147
+ const dropTables = [
148
+ ...materializedResolution.incompatibleTables,
149
+ // V1 only keeps one SourceTable per physical table.
150
+ ...compatibleTables.filter((table) => !storage.sourceTableIdEquals(table.id, sourceTable.id))
151
+ ];
139
152
 
140
153
  result = { tables: [sourceTable], dropTables };
141
154
  });
@@ -143,38 +156,33 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
143
156
  return result!;
144
157
  }
145
158
 
146
- async getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null> {
147
- const doc = (await this.db.commonSourceTables(this.group_id).findOne(
148
- {
149
- group_id: this.group_id,
150
- _id: mongoTableId(table.id)
151
- },
152
- { session: this.session }
153
- )) as SourceTableDocumentV1 | null;
154
- if (doc == null) {
155
- return null;
156
- }
157
-
158
- const ref = {
159
- connectionTag: table.ref.connectionTag,
160
- schema: doc.schema_name,
161
- name: doc.table_name
162
- };
163
- const sourceTable = new storage.SourceTable({
159
+ /**
160
+ * Build a SourceTable from its persisted v1 record.
161
+ */
162
+ private hydrateSourceTable(
163
+ doc: SourceTableDocumentV1,
164
+ connectionTag: string,
165
+ syncRules: HydratedSyncConfig,
166
+ sendsCompleteRows: boolean | undefined
167
+ ): storage.SourceTable {
168
+ const ref = { connectionTag, schema: doc.schema_name, name: doc.table_name };
169
+ const table = new storage.SourceTable({
164
170
  id: doc._id,
165
171
  ref,
166
172
  objectId: doc.relation_id,
167
173
  replicaIdColumns:
168
174
  doc.replica_id_columns2?.map(
169
- (c) => ({ name: c.name, typeId: c.type_oid, type: c.type }) satisfies ColumnDescriptor
175
+ (column) => ({ name: column.name, typeId: column.type_oid, type: column.type }) satisfies ColumnDescriptor
170
176
  ) ?? [],
171
177
  snapshotComplete: doc.snapshot_done ?? true,
172
- ...this.sync_rules.getMatchingSources(ref)
178
+ sourceMetadata: doc.source_metadata ?? null,
179
+ ...syncRules.getMatchingSources(ref)
173
180
  });
174
- sourceTable.syncEvent = this.sync_rules.tableTriggersEvent(ref);
175
- sourceTable.syncData = sourceTable.bucketDataSources.length > 0;
176
- sourceTable.syncParameters = sourceTable.parameterLookupSources.length > 0;
177
- sourceTable.snapshotStatus =
181
+ table.syncEvent = syncRules.tableTriggersEvent(ref);
182
+ table.syncData = table.bucketDataSources.length > 0;
183
+ table.syncParameters = table.parameterLookupSources.length > 0;
184
+ table.storeCurrentData = sendsCompleteRows !== true;
185
+ table.snapshotStatus =
178
186
  doc.snapshot_status == null
179
187
  ? undefined
180
188
  : {
@@ -182,7 +190,23 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
182
190
  totalEstimatedCount: doc.snapshot_status.total_estimated_count,
183
191
  replicatedCount: doc.snapshot_status.replicated_count
184
192
  };
185
- return sourceTable;
193
+ return table;
194
+ }
195
+
196
+ async getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null> {
197
+ const doc = await this.db.sourceTablesV1(this.replicationStreamId).findOne(
198
+ {
199
+ group_id: this.replicationStreamId,
200
+ _id: mongoTableId(table.id)
201
+ },
202
+ { session: this.session }
203
+ );
204
+ if (doc == null) {
205
+ return null;
206
+ }
207
+
208
+ // Use the default storeCurrentData value when reading outside resolution.
209
+ return this.hydrateSourceTable(doc, table.ref.connectionTag, this.sync_rules, undefined);
186
210
  }
187
211
 
188
212
  async commit(lsn: string, options?: storage.BucketBatchCommitOptions): Promise<storage.CheckpointResult> {
@@ -190,6 +214,8 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
190
214
 
191
215
  await this.flush(options);
192
216
 
217
+ using _ = this.tracer.span('storage', 'commit');
218
+
193
219
  const now = new Date();
194
220
 
195
221
  await this.db.write_checkpoints.updateMany(
@@ -242,7 +268,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
242
268
  };
243
269
 
244
270
  const preUpdateDocument = (await this.db.sync_rules.findOneAndUpdate(
245
- { _id: this.group_id },
271
+ { _id: this.replicationStreamId },
246
272
  [
247
273
  {
248
274
  $set: {
@@ -295,7 +321,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
295
321
 
296
322
  if (preUpdateDocument == null) {
297
323
  throw new ReplicationAssertionError(
298
- 'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.group_id
324
+ 'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.replicationStreamId
299
325
  );
300
326
  }
301
327
 
@@ -329,7 +355,6 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
329
355
  await this.autoActivate(lsn);
330
356
  await this.db.notifyCheckpoint();
331
357
  this.persisted_op = null;
332
- this.last_checkpoint_lsn = lsn;
333
358
  if (checkpointState.newLastCheckpoint != null) {
334
359
  await this.sourceRecordStore.postCommitCleanup(checkpointState.newLastCheckpoint, this.logger);
335
360
  }
@@ -346,23 +371,30 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
346
371
  }
347
372
 
348
373
  async setResumeLsn(lsn: string): Promise<void> {
374
+ using _ = this.tracer.span('storage', 'set_resume_lsn');
349
375
  await this.db.sync_rules.updateOne(
350
376
  {
351
- _id: this.group_id
377
+ _id: this.replicationStreamId
352
378
  },
353
379
  {
354
380
  $set: {
355
381
  snapshot_lsn: lsn
356
382
  }
357
383
  },
358
- { session: this.session }
384
+ {
385
+ session: this.session,
386
+ // Losing occasional resume LSN is fine. That may mean reprocessing
387
+ // some source changes in some edge cases, which is not an issue since
388
+ // changes are processed in an idempotent way.
389
+ writeConcern: { w: 1 }
390
+ }
359
391
  );
360
392
  }
361
393
 
362
394
  async markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void> {
363
395
  await this.db.sync_rules.updateOne(
364
396
  {
365
- _id: this.group_id
397
+ _id: this.replicationStreamId
366
398
  },
367
399
  {
368
400
  $set: {
@@ -380,17 +412,24 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
380
412
  async markSnapshotDone(no_checkpoint_before_lsn: string, options?: { throwOnConflict?: boolean }): Promise<void> {
381
413
  await this.withTransaction(async () => {
382
414
  // Protect against race conditions
383
- const count = await this.db.commonSourceTables(this.group_id).countDocuments(
384
- {
385
- group_id: this.group_id,
386
- snapshot_done: false
387
- },
388
- { session: this.session }
389
- );
390
- if (count > 0) {
415
+ const blockingTables = await this.db
416
+ .sourceTablesV1(this.replicationStreamId)
417
+ .find(
418
+ {
419
+ group_id: this.replicationStreamId,
420
+ snapshot_done: false
421
+ },
422
+ {
423
+ session: this.session,
424
+ projection: { schema_name: 1, table_name: 1 }
425
+ }
426
+ )
427
+ .toArray();
428
+
429
+ if (blockingTables.length > 0) {
391
430
  if (options?.throwOnConflict ?? true) {
392
431
  throw new ReplicationAssertionError(
393
- `Cannot mark snapshot done while ${count} source table${count == 1 ? '' : 's'} still require snapshotting`
432
+ `Cannot mark snapshot done while source tables still require snapshotting. Tables: ${blockingTables.map((t) => `${t.schema_name}.${t.table_name}`).join(', ')}`
394
433
  );
395
434
  } else {
396
435
  return;
@@ -404,7 +443,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
404
443
  async markTableSnapshotRequired(_table: storage.SourceTable): Promise<void> {
405
444
  await this.db.sync_rules.updateOne(
406
445
  {
407
- _id: this.group_id
446
+ _id: this.replicationStreamId
408
447
  },
409
448
  {
410
449
  $set: {
@@ -423,7 +462,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
423
462
  const ids = tables.map((table) => mongoTableId(table.id));
424
463
 
425
464
  await this.withTransaction(async () => {
426
- await this.db.commonSourceTables(this.group_id).updateMany(
465
+ await this.db.sourceTablesV1(this.replicationStreamId).updateMany(
427
466
  { _id: { $in: ids } },
428
467
  {
429
468
  $set: {
@@ -439,7 +478,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
439
478
  if (no_checkpoint_before_lsn != null) {
440
479
  await this.db.sync_rules.updateOne(
441
480
  {
442
- _id: this.group_id
481
+ _id: this.replicationStreamId
443
482
  },
444
483
  {
445
484
  $set: {
@@ -467,12 +506,20 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
467
506
 
468
507
  const session = this.session;
469
508
  let activated = false;
509
+ let needsFutureActivationCheck = true;
470
510
  await session.withTransaction(async () => {
471
- const doc = (await this.db.sync_rules.findOne({ _id: this.group_id }, { session })) as SyncRuleDocumentV1;
511
+ // Reset on transaction retries.
512
+ activated = false;
513
+ needsFutureActivationCheck = true;
514
+
515
+ const doc = (await this.db.sync_rules.findOne(
516
+ { _id: this.replicationStreamId },
517
+ { session }
518
+ )) as SyncRuleDocumentV1;
472
519
  if (doc && doc.state == storage.SyncRuleState.PROCESSING && doc.snapshot_done && doc.last_checkpoint != null) {
473
520
  await this.db.sync_rules.updateOne(
474
521
  {
475
- _id: this.group_id
522
+ _id: this.replicationStreamId
476
523
  },
477
524
  {
478
525
  $set: {
@@ -484,7 +531,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
484
531
 
485
532
  await this.db.sync_rules.updateMany(
486
533
  {
487
- _id: { $ne: this.group_id },
534
+ _id: { $ne: this.replicationStreamId },
488
535
  state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
489
536
  },
490
537
  {
@@ -496,13 +543,15 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
496
543
  );
497
544
  activated = true;
498
545
  } else if (doc?.state != storage.SyncRuleState.PROCESSING) {
499
- this.needsActivation = false;
546
+ needsFutureActivationCheck = false;
500
547
  }
501
548
  });
502
549
  if (activated) {
503
550
  this.logger.info(`Activated new replication stream at ${lsn}`);
504
551
  await this.db.notifyCheckpoint();
505
552
  this.needsActivation = false;
553
+ } else if (!needsFutureActivationCheck) {
554
+ this.needsActivation = false;
506
555
  }
507
556
  }
508
557
  }
@@ -1,20 +1,31 @@
1
+ import * as lib_mongo from '@powersync/lib-service-mongodb';
1
2
  import {
3
+ addPartialChecksums,
2
4
  bson,
3
5
  BucketChecksum,
4
6
  FetchPartialBucketChecksum,
5
7
  InternalOpId,
6
- PartialChecksumMap
8
+ isPartialChecksum,
9
+ PartialChecksumMap,
10
+ PartialOrFullChecksum
7
11
  } from '@powersync/service-core';
8
- import { FetchPartialBucketChecksumByBucket, MongoChecksums } from '../MongoChecksums.js';
12
+ import {
13
+ checksumFromAggregate,
14
+ DEFAULT_OPERATION_BATCH_LIMIT,
15
+ FetchPartialBucketChecksumByBucket,
16
+ MongoChecksums,
17
+ MongoChecksumSessionContext
18
+ } from '../MongoChecksums.js';
9
19
  import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
10
-
11
20
  export class MongoChecksumsV1 extends MongoChecksums {
12
21
  declare protected readonly db: VersionedPowerSyncMongoV1;
13
22
 
14
23
  async computePartialChecksumsDirectByBucket(
15
- batch: FetchPartialBucketChecksumByBucket[]
24
+ batch: FetchPartialBucketChecksumByBucket[],
25
+ context: MongoChecksumSessionContext
16
26
  ): Promise<PartialChecksumMap> {
17
- return this.computePartialChecksumsForCollection(batch, this.db.bucketDataV1, (request) => ({
27
+ const collection = this.db.bucketDataV1;
28
+ const createFilter = (request: FetchPartialBucketChecksumByBucket) => ({
18
29
  _id: {
19
30
  $gt: {
20
31
  g: this.group_id,
@@ -27,11 +38,134 @@ export class MongoChecksumsV1 extends MongoChecksums {
27
38
  o: request.end
28
39
  }
29
40
  }
30
- }));
41
+ });
42
+
43
+ const batchLimit = this.options?.operationBatchLimit ?? DEFAULT_OPERATION_BATCH_LIMIT;
44
+
45
+ // Map requests by bucket. We adjust this as we get partial results.
46
+ let requests = new Map<string, FetchPartialBucketChecksumByBucket>();
47
+ for (let request of batch) {
48
+ requests.set(request.bucket, request);
49
+ }
50
+
51
+ const partialChecksums = new Map<string, PartialOrFullChecksum>();
52
+
53
+ while (requests.size > 0) {
54
+ const filters = Array.from(requests.values(), createFilter);
55
+
56
+ // Historically, checksum may be stored as 'int' or 'double'.
57
+ // More recently, this should be a 'long'.
58
+ // $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
59
+ const checksumLong = this.storageConfig.longChecksums ? '$checksum' : { $toLong: '$checksum' };
60
+
61
+ // Aggregate over a max of `batchLimit` operations at a time.
62
+ // Let's say we have 3 buckets (A, B, C), each with 10 operations, and our batch limit is 12.
63
+ // Then we'll do three batches:
64
+ // 1. Query: A[1-end], B[1-end], C[1-end]
65
+ // Returns: A[1-10], B[1-2]
66
+ // 2. Query: B[3-end], C[1-end]
67
+ // Returns: B[3-10], C[1-4]
68
+ // 3. Query: C[5-end]
69
+ // Returns: C[5-10]
70
+ const aggregate = await collection
71
+ .aggregate(
72
+ [
73
+ {
74
+ $match: {
75
+ $or: filters
76
+ }
77
+ },
78
+ // sort and limit _before_ grouping
79
+ { $sort: { _id: 1 } },
80
+ { $limit: batchLimit },
81
+ {
82
+ $group: {
83
+ _id: '$_id.b',
84
+ checksum_total: { $sum: checksumLong },
85
+ count: { $sum: 1 },
86
+ has_clear_op: {
87
+ $max: {
88
+ $cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
89
+ }
90
+ },
91
+ last_op: { $max: '$_id.o' }
92
+ }
93
+ },
94
+ // Sort the aggregated results (100 max, so should be fast).
95
+ // This is important to identify which buckets we have partial data for.
96
+ { $sort: { _id: 1 } }
97
+ ],
98
+ {
99
+ ...context.readOptions,
100
+ maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS
101
+ }
102
+ )
103
+ .toArray()
104
+ .catch((e) => {
105
+ throw lib_mongo.mapQueryError(e, 'while reading checksums');
106
+ });
107
+
108
+ let batchCount = 0;
109
+ let limitReached = false;
110
+ for (let doc of aggregate) {
111
+ const bucket = doc._id;
112
+ const checksum = checksumFromAggregate(doc);
113
+
114
+ const existing = partialChecksums.get(bucket);
115
+ if (existing != null) {
116
+ partialChecksums.set(bucket, addPartialChecksums(bucket, existing, checksum));
117
+ } else {
118
+ partialChecksums.set(bucket, checksum);
119
+ }
120
+
121
+ batchCount += doc.count;
122
+ if (batchCount == batchLimit) {
123
+ // Limit reached. Request more in the next batch.
124
+ // Note that this only affects the _last_ bucket in a batch.
125
+ limitReached = true;
126
+ const req = requests.get(bucket);
127
+ requests.set(bucket, {
128
+ ...req!,
129
+ start: doc.last_op
130
+ });
131
+ } else {
132
+ // All done for this bucket
133
+ requests.delete(bucket);
134
+ }
135
+ }
136
+ if (!limitReached) {
137
+ break;
138
+ }
139
+ }
140
+
141
+ return new Map<string, PartialOrFullChecksum>(
142
+ batch.map((request) => {
143
+ const bucket = request.bucket;
144
+ let partialChecksum = partialChecksums.get(bucket);
145
+ // Could be null if we got no data
146
+ if (partialChecksum == null) {
147
+ partialChecksum = {
148
+ bucket,
149
+ partialCount: 0,
150
+ partialChecksum: 0
151
+ };
152
+ }
153
+ if (request.start == null && isPartialChecksum(partialChecksum)) {
154
+ partialChecksum = {
155
+ bucket,
156
+ count: partialChecksum.partialCount,
157
+ checksum: partialChecksum.partialChecksum
158
+ };
159
+ }
160
+
161
+ return [bucket, partialChecksum];
162
+ })
163
+ );
31
164
  }
32
165
 
33
166
  protected async fetchPreStates(
34
- batch: FetchPartialBucketChecksum[]
167
+ batch: FetchPartialBucketChecksum[],
168
+ context: MongoChecksumSessionContext
35
169
  ): Promise<Map<string, { opId: InternalOpId; checksum: BucketChecksum }>> {
36
170
  const preFilters = batch
37
171
  .filter((request) => request.start == null)
@@ -49,9 +183,14 @@ export class MongoChecksumsV1 extends MongoChecksums {
49
183
  }
50
184
 
51
185
  const states = await this.db.bucketStateV1
52
- .find({
53
- $or: preFilters
54
- })
186
+ .find(
187
+ {
188
+ $or: preFilters
189
+ },
190
+ {
191
+ ...context.readOptions
192
+ }
193
+ )
55
194
  .toArray();
56
195
 
57
196
  for (const state of states) {
@@ -69,7 +208,10 @@ export class MongoChecksumsV1 extends MongoChecksums {
69
208
  return preStates;
70
209
  }
71
210
 
72
- protected async computePartialChecksumsInternal(batch: FetchPartialBucketChecksum[]): Promise<PartialChecksumMap> {
73
- return this.computePartialChecksumsDirectByBucket(batch);
211
+ protected async computePartialChecksumsInternal(
212
+ batch: FetchPartialBucketChecksum[],
213
+ context: MongoChecksumSessionContext
214
+ ): Promise<PartialChecksumMap> {
215
+ return this.computePartialChecksumsDirectByBucket(batch, context);
74
216
  }
75
217
  }