@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,45 +1,66 @@
1
1
  import { mongo } from '@powersync/lib-service-mongodb';
2
2
  import { BaseObserver, DO_NOT_LOG, Logger } from '@powersync/lib-services-framework';
3
- import { CheckpointChanges, GetCheckpointChangesOptions, InternalOpId, PopulateChecksumCacheOptions, PopulateChecksumCacheResults, ReplicationCheckpoint, storage, utils, WatchWriteCheckpointOptions } from '@powersync/service-core';
3
+ import { CheckpointChanges, CompactInitialReplicationOptions, CompactInitialReplicationResults, GetCheckpointChangesOptions, InternalOpId, ReplicationStreamStorageIds, storage, utils, WatchWriteCheckpointOptions } from '@powersync/service-core';
4
4
  import { HydratedSyncConfig, ParameterLookupRows, ScopedParameterLookup } from '@powersync/service-sync-rules';
5
- import * as bson from 'bson';
6
5
  import { MongoBucketStorage } from '../MongoBucketStorage.js';
7
- import { MongoSyncBucketStorageContext } from './common/MongoSyncBucketStorageContext.js';
6
+ import { MongoGetCheckpointChangesOptions, MongoSyncBucketStorageCheckpoint } from './common/MongoSyncBucketStorageCheckpoint.js';
8
7
  import type { VersionedPowerSyncMongo } from './db.js';
9
8
  import { StorageConfig } from './models.js';
10
9
  import { MongoBucketBatchOptions } from './MongoBucketBatch.js';
11
10
  import { MongoChecksumOptions, MongoChecksums } from './MongoChecksums.js';
12
11
  import { MongoCompactOptions, MongoCompactor } from './MongoCompactor.js';
13
12
  import { MongoParameterCompactor } from './MongoParameterCompactor.js';
14
- import { MongoPersistedSyncRulesContentV1 } from './MongoPersistedSyncRulesContent.js';
13
+ import { MongoParsedSyncConfigSet } from './MongoParsedSyncConfigSet.js';
14
+ import { MongoPersistedReplicationStream } from './MongoPersistedReplicationStream.js';
15
+ import { MongoCheckpointAPIOptions, MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
16
+ import { ObjectStorage } from './v3/object-storage/ObjectStorage.js';
15
17
  export interface MongoSyncBucketStorageOptions {
16
- checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig' | 'mapping'>;
18
+ checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig'>;
19
+ readPreference?: mongo.ReadPreference;
20
+ checksumCacheTtlMs?: number;
21
+ clearBatchThrottleRate?: number;
17
22
  storageConfig: StorageConfig;
23
+ objectStorage?: ObjectStorage;
24
+ inlineThresholdBytes?: number;
25
+ }
26
+ /**
27
+ * The stream state read for a checkpoint. All fields must come from the same snapshot.
28
+ */
29
+ export interface MongoCheckpointState {
30
+ checkpoint: InternalOpId;
31
+ lsn: string | null;
32
+ /** See {@link MongoSyncBucketStorageCheckpoint.parameterChangesInvalidBefore}. */
33
+ parameterChangesInvalidBefore: InternalOpId;
18
34
  }
19
35
  interface InternalCheckpointChanges extends CheckpointChanges {
20
36
  updatedWriteCheckpoints: Map<string, bigint>;
21
37
  invalidateWriteCheckpoints: boolean;
22
38
  }
23
- interface WriterSyncState {
24
- lastCheckpointLsn: string | null;
25
- resumeFromLsn: string | null;
26
- keepaliveOp: InternalOpId | null;
27
- syncConfigId?: bson.ObjectId | null;
28
- }
29
39
  export declare abstract class MongoSyncBucketStorage extends BaseObserver<storage.SyncRulesBucketStorageListener> implements storage.SyncRulesBucketStorage {
30
40
  #private;
31
41
  readonly factory: MongoBucketStorage;
32
- readonly group_id: number;
33
- protected readonly sync_rules: MongoPersistedSyncRulesContentV1;
34
- readonly slot_name: string;
42
+ readonly replicationStreamId: number;
43
+ readonly replicationStream: MongoPersistedReplicationStream;
44
+ readonly replicationStreamName: string;
35
45
  readonly db: VersionedPowerSyncMongo;
36
46
  [DO_NOT_LOG]: boolean;
37
47
  readonly checksums: MongoChecksums;
38
- private parsedSyncRulesCache;
48
+ readonly objectStorage?: ObjectStorage;
49
+ readonly inlineThresholdBytes: number;
50
+ /**
51
+ * Canonical parsed sync config sets, keyed by defaultSchema.
52
+ *
53
+ * Entries are never evicted: each parse options value maps to exactly one parsed set for
54
+ * the lifetime of this storage instance, so parsed source objects and mappings always
55
+ * stay associated.
56
+ */
57
+ private readonly parsedSyncConfigSets;
39
58
  private writeCheckpointAPI;
40
59
  readonly logger: Logger;
41
60
  readonly storageConfig: StorageConfig;
42
- constructor(factory: MongoBucketStorage, group_id: number, sync_rules: MongoPersistedSyncRulesContentV1, slot_name: string, writeCheckpointMode: storage.WriteCheckpointMode | undefined, options: MongoSyncBucketStorageOptions);
61
+ readonly readPreference: mongo.ReadPreference | undefined;
62
+ readonly clearBatchThrottleRate: number;
63
+ constructor(factory: MongoBucketStorage, replicationStreamId: number, replicationStream: MongoPersistedReplicationStream, replicationStreamName: string, writeCheckpointMode: storage.WriteCheckpointMode | undefined, options: MongoSyncBucketStorageOptions);
43
64
  /**
44
65
  * Not for external use - public here for tests only.
45
66
  *
@@ -47,47 +68,65 @@ export declare abstract class MongoSyncBucketStorage extends BaseObserver<storag
47
68
  */
48
69
  abstract createMongoCompactor(options: MongoCompactOptions): MongoCompactor;
49
70
  protected abstract createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums;
71
+ protected createWriteCheckpointAPI(options: MongoCheckpointAPIOptions): MongoWriteCheckpointAPI;
50
72
  protected abstract createMongoParameterCompactor(checkpoint: InternalOpId, options: storage.CompactOptions): MongoParameterCompactor;
73
+ /** MongoDB parameter compaction uses a persisted operation-id cursor. */
74
+ supportsIncrementalParameterCompaction(): boolean;
51
75
  get writeCheckpointMode(): storage.WriteCheckpointMode;
52
- get mapping(): import("./BucketDefinitionMapping.js").BucketDefinitionMapping;
53
- protected get versionContext(): MongoSyncBucketStorageContext;
54
- setWriteCheckpointMode(mode: storage.WriteCheckpointMode): void;
55
- createManagedWriteCheckpoint(checkpoint: storage.ManagedWriteCheckpointOptions): Promise<bigint>;
76
+ /**
77
+ * Persisted storage ids of all sync configs in this replication stream. Parse-free.
78
+ */
79
+ get storageIds(): ReplicationStreamStorageIds;
80
+ setWriteCheckpointMode(config: storage.WriteCheckpointModeConfig): void;
81
+ createManagedWriteCheckpoints(checkpoints: storage.ManagedWriteCheckpointOptions[]): Promise<storage.CreateManagedWriteCheckpointsResult>;
56
82
  lastWriteCheckpoint(filters: storage.SyncStorageLastWriteCheckpointFilters): Promise<bigint | null>;
57
- getParsedSyncRules(options: storage.ParseSyncRulesOptions): HydratedSyncConfig;
83
+ getParsedSyncConfigSet(options: storage.ParseSyncConfigOptions): MongoParsedSyncConfigSet;
84
+ getParsedSyncRules(options: storage.ParseSyncConfigOptions): HydratedSyncConfig;
58
85
  getCheckpoint(): Promise<storage.ReplicationCheckpoint>;
59
- protected abstract fetchCheckpointState(session: mongo.ClientSession): Promise<{
60
- checkpoint: bigint;
61
- lsn: string | null;
62
- } | null>;
86
+ protected abstract fetchCheckpointState(session: mongo.ClientSession): Promise<MongoCheckpointState | null>;
63
87
  getCheckpointInternal(): Promise<storage.ReplicationCheckpoint | null>;
64
88
  protected abstract initializeVersionStorage(): Promise<void>;
65
89
  private initializeStorage;
66
- protected abstract createWriterImpl(batchOptions: MongoBucketBatchOptions): storage.BucketStorageBatch;
67
- protected abstract getWriterSyncState(): Promise<WriterSyncState>;
90
+ /**
91
+ * Create the version-specific writer. Implementations fetch their own resume state
92
+ * (e.g. resume LSN, v1 keepalive op) and construct the batch from
93
+ * {@link writerBatchOptions} plus the version-specific fields.
94
+ */
95
+ protected abstract createWriterImpl(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch>;
96
+ /**
97
+ * The version-independent part of the batch options.
98
+ */
99
+ protected writerBatchOptions(options: storage.CreateWriterOptions): Omit<MongoBucketBatchOptions, 'resumeFromLsn'>;
68
100
  createWriter(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch>;
69
101
  startBatch(options: storage.CreateWriterOptions, callback: (batch: storage.BucketStorageBatch) => Promise<void>): Promise<storage.FlushedResult | null>;
70
102
  protected abstract getParameterSetsImpl(checkpoint: MongoReplicationCheckpoint, lookups: ScopedParameterLookup[], limit: number): Promise<ParameterLookupRows[]>;
71
103
  getParameterSets(checkpoint: MongoReplicationCheckpoint, lookups: ScopedParameterLookup[], limit: number): Promise<ParameterLookupRows[]>;
72
- protected abstract getBucketDataBatchImpl(checkpoint: utils.InternalOpId, dataBuckets: storage.BucketDataRequest[], options?: storage.BucketDataBatchOptions): AsyncIterable<storage.SyncBucketDataChunk>;
73
- getBucketDataBatch(checkpoint: utils.InternalOpId, dataBuckets: storage.BucketDataRequest[], options?: storage.BucketDataBatchOptions): AsyncIterable<storage.SyncBucketDataChunk>;
74
- getChecksums(checkpoint: utils.InternalOpId, buckets: storage.BucketChecksumRequest[]): Promise<utils.ChecksumMap>;
104
+ protected abstract getBucketDataBatchImpl(checkpoint: MongoReplicationCheckpoint, dataBuckets: storage.BucketDataRequest[], options?: storage.BucketDataBatchOptions): AsyncIterable<storage.SyncBucketDataChunk | storage.SyncBucketDataBatchEnd>;
105
+ getBucketDataBatch(checkpoint: storage.ReplicationCheckpoint, dataBuckets: storage.BucketDataRequest[], options?: storage.BucketDataBatchOptions): AsyncIterable<storage.SyncBucketDataChunk | storage.SyncBucketDataBatchEnd>;
106
+ getChecksums(checkpoint: storage.ReplicationCheckpoint, buckets: storage.BucketChecksumRequest[], options?: storage.BucketChecksumOptions): Promise<utils.ChecksumMap>;
75
107
  clearChecksumCache(): void;
76
108
  protected abstract terminateSyncRuleState(): Promise<void>;
77
109
  terminate(options?: storage.TerminateOptions): Promise<void>;
78
- protected abstract getStatusImpl(): Promise<storage.SyncRuleStatus>;
79
- getStatus(): Promise<storage.SyncRuleStatus>;
110
+ protected abstract getStatusImpl(): Promise<storage.ReplicationStreamStatus>;
111
+ getStatus(): Promise<storage.ReplicationStreamStatus>;
80
112
  protected abstract clearBucketData(signal?: AbortSignal): Promise<void>;
81
113
  protected abstract clearParameterIndexes(signal?: AbortSignal): Promise<void>;
82
114
  protected abstract clearSourceRecords(signal?: AbortSignal): Promise<void>;
83
115
  protected abstract clearBucketState(signal?: AbortSignal): Promise<void>;
84
116
  protected abstract clearSourceTables(signal?: AbortSignal): Promise<void>;
117
+ protected clearCustomCheckpointRequests(_signal?: AbortSignal): Promise<void>;
85
118
  protected abstract clearSyncRuleState(): Promise<void>;
86
119
  clear(options?: storage.ClearStorageOptions): Promise<void>;
87
- protected clearDeleteMany(label: string, operation: () => Promise<mongo.DeleteResult>, signal?: AbortSignal): Promise<void>;
88
120
  reportError(e: any): Promise<void>;
89
121
  compact(options?: storage.CompactOptions): Promise<void>;
90
- populatePersistentChecksumCache(options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults>;
122
+ abstract compactInitialReplication(options: CompactInitialReplicationOptions): Promise<CompactInitialReplicationResults>;
123
+ /**
124
+ * The highest op id persisted for this stream, whether or not covered by a checkpoint.
125
+ *
126
+ * Used as the default `maxOpId` for {@link compactInitialReplication}, which runs after
127
+ * initial replication but before the first checkpoint exists.
128
+ */
129
+ protected abstract fetchPersistedOpHead(): Promise<InternalOpId | null>;
91
130
  private watchActiveCheckpoint;
92
131
  private readonly sharedIter;
93
132
  watchCheckpointChanges(options: WatchWriteCheckpointOptions): AsyncIterable<storage.StorageCheckpointUpdate>;
@@ -95,18 +134,23 @@ export declare abstract class MongoSyncBucketStorage extends BaseObserver<storag
95
134
  private checkpointTimeoutStream;
96
135
  protected abstract getDataBucketChangesImpl(options: GetCheckpointChangesOptions): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>>;
97
136
  private getDataBucketChanges;
98
- protected abstract getParameterBucketChangesImpl(options: GetCheckpointChangesOptions): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>>;
137
+ protected abstract getParameterBucketChangesImpl(options: MongoGetCheckpointChangesOptions): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>>;
99
138
  private getParameterBucketChanges;
100
139
  private checkpointChangesCache;
101
140
  getCheckpointChanges(options: GetCheckpointChangesOptions): Promise<InternalCheckpointChanges>;
102
141
  private getCheckpointChangesInternal;
103
142
  }
104
- declare class MongoReplicationCheckpoint implements ReplicationCheckpoint {
143
+ declare class MongoReplicationCheckpoint implements MongoSyncBucketStorageCheckpoint {
105
144
  #private;
106
145
  readonly checkpoint: InternalOpId;
107
146
  readonly lsn: string | null;
108
147
  snapshotTime: mongo.Timestamp;
109
- constructor(storage: MongoSyncBucketStorage, checkpoint: InternalOpId, lsn: string | null, snapshotTime: mongo.Timestamp);
148
+ clusterTime: mongo.ClusterTime;
149
+ /** Captured in the same snapshot as the checkpoint. */
150
+ readonly parameterChangesInvalidBefore: InternalOpId;
151
+ constructor(storage: MongoSyncBucketStorage, checkpoint: InternalOpId, lsn: string | null, snapshotTime: mongo.Timestamp, clusterTime: mongo.ClusterTime,
152
+ /** Captured in the same snapshot as the checkpoint. */
153
+ parameterChangesInvalidBefore: InternalOpId);
110
154
  getParameterSets(lookups: ScopedParameterLookup[], limit: number): Promise<ParameterLookupRows[]>;
111
155
  }
112
156
  export {};
@@ -1,9 +1,10 @@
1
1
  import * as lib_mongo from '@powersync/lib-service-mongodb';
2
- import { BaseObserver, DO_NOT_LOG, ReplicationAbortedError, ServiceAssertionError } from '@powersync/lib-services-framework';
3
- import { BroadcastIterable, CHECKPOINT_INVALIDATE_ALL, mergeAsyncIterables, storage } from '@powersync/service-core';
2
+ import { BaseObserver, logger as defaultLogger, DO_NOT_LOG, ReplicationAbortedError, ServiceAssertionError } from '@powersync/lib-services-framework';
3
+ import { BroadcastIterable, CHECKPOINT_INVALIDATE_ALL, mergeAsyncIterables, storage, SyncRuleState } from '@powersync/service-core';
4
4
  import { LRUCache } from 'lru-cache';
5
5
  import * as timers from 'timers/promises';
6
- import { retryOnMongoMaxTimeMSExpired } from '../../utils/util.js';
6
+ import { DEFAULT_CLEAR_BATCH_THROTTLE_RATE } from '../../types/types.js';
7
+ import { DEFAULT_INLINE_THRESHOLD_BYTES } from './common/PersistedBatch.js';
7
8
  import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
8
9
  /**
9
10
  * Only keep checkpoints around for a minute, before fetching a fresh one.
@@ -17,64 +18,102 @@ import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
17
18
  const CHECKPOINT_TIMEOUT_MS = 60_000;
18
19
  export class MongoSyncBucketStorage extends BaseObserver {
19
20
  factory;
20
- group_id;
21
- sync_rules;
22
- slot_name;
21
+ replicationStreamId;
22
+ replicationStream;
23
+ replicationStreamName;
23
24
  db;
24
25
  [DO_NOT_LOG] = true;
25
26
  checksums;
26
- parsedSyncRulesCache;
27
+ objectStorage;
28
+ inlineThresholdBytes;
29
+ /**
30
+ * Canonical parsed sync config sets, keyed by defaultSchema.
31
+ *
32
+ * Entries are never evicted: each parse options value maps to exactly one parsed set for
33
+ * the lifetime of this storage instance, so parsed source objects and mappings always
34
+ * stay associated.
35
+ */
36
+ parsedSyncConfigSets = new Map();
27
37
  writeCheckpointAPI;
28
38
  logger;
29
39
  storageConfig;
40
+ readPreference;
41
+ clearBatchThrottleRate;
30
42
  #storageInitialized = false;
31
- constructor(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options) {
43
+ constructor(factory, replicationStreamId, replicationStream, replicationStreamName, writeCheckpointMode, options) {
32
44
  super();
33
45
  this.factory = factory;
34
- this.group_id = group_id;
35
- this.sync_rules = sync_rules;
36
- this.slot_name = slot_name;
46
+ this.replicationStreamId = replicationStreamId;
47
+ this.replicationStream = replicationStream;
48
+ this.replicationStreamName = replicationStreamName;
37
49
  this.storageConfig = options.storageConfig;
50
+ this.objectStorage = options.objectStorage;
51
+ // Keep small chunks inline in MongoDB rather than offloading them to S3.
52
+ // Configurable via object_storage.inline_threshold_bytes.
53
+ this.inlineThresholdBytes = options.inlineThresholdBytes ?? DEFAULT_INLINE_THRESHOLD_BYTES;
54
+ this.readPreference = options.readPreference;
55
+ this.clearBatchThrottleRate = options.clearBatchThrottleRate ?? DEFAULT_CLEAR_BATCH_THROTTLE_RATE;
38
56
  this.db = factory.db.versioned(this.storageConfig);
39
57
  this.checksums = this.createMongoChecksums(options);
40
- this.writeCheckpointAPI = new MongoWriteCheckpointAPI({
58
+ this.writeCheckpointAPI = this.createWriteCheckpointAPI({
41
59
  db: this.db,
42
- mode: writeCheckpointMode ?? storage.WriteCheckpointMode.MANAGED,
43
- sync_rules_id: group_id
60
+ writeCheckpointMode: { mode: writeCheckpointMode ?? storage.WriteCheckpointMode.MANAGED },
61
+ replicationStreamId
44
62
  });
45
- this.logger = sync_rules.logger;
63
+ // Include both replication stream name and sync config version labels in log prefix.
64
+ // Examples:
65
+ // No sync config with a label: [stream]
66
+ // All sync configs have a label: [stream][v5,v6]
67
+ // Mixed: [stream][?,v6]
68
+ const hasVersionLabel = replicationStream.syncConfigContent.some((config) => config.version_label != null);
69
+ const versionLabels = hasVersionLabel
70
+ ? replicationStream.syncConfigContent.map((config) => config.version_label ?? '?')
71
+ : [];
72
+ this.logger =
73
+ versionLabels.length == 0
74
+ ? replicationStream.logger
75
+ : defaultLogger.child({
76
+ prefix: `[${replicationStreamName}][${versionLabels.join(',')}] `
77
+ });
78
+ }
79
+ createWriteCheckpointAPI(options) {
80
+ return new MongoWriteCheckpointAPI(options);
81
+ }
82
+ /** MongoDB parameter compaction uses a persisted operation-id cursor. */
83
+ supportsIncrementalParameterCompaction() {
84
+ return true;
46
85
  }
47
86
  get writeCheckpointMode() {
48
87
  return this.writeCheckpointAPI.writeCheckpointMode;
49
88
  }
50
- get mapping() {
51
- return this.sync_rules.mapping;
89
+ /**
90
+ * Persisted storage ids of all sync configs in this replication stream. Parse-free.
91
+ */
92
+ get storageIds() {
93
+ return this.replicationStream.storageIds;
52
94
  }
53
- get versionContext() {
54
- return {
55
- db: this.db,
56
- group_id: this.group_id,
57
- mapping: this.mapping
58
- };
59
- }
60
- setWriteCheckpointMode(mode) {
61
- this.writeCheckpointAPI.setWriteCheckpointMode(mode);
95
+ setWriteCheckpointMode(config) {
96
+ this.writeCheckpointAPI.setWriteCheckpointMode(config);
62
97
  }
63
- createManagedWriteCheckpoint(checkpoint) {
64
- return this.writeCheckpointAPI.createManagedWriteCheckpoint(checkpoint);
98
+ createManagedWriteCheckpoints(checkpoints) {
99
+ return this.writeCheckpointAPI.createManagedWriteCheckpoints(checkpoints);
65
100
  }
66
101
  lastWriteCheckpoint(filters) {
67
102
  return this.writeCheckpointAPI.lastWriteCheckpoint({
68
103
  ...filters,
69
- sync_rules_id: this.group_id
104
+ sync_rules_id: this.replicationStreamId
70
105
  });
71
106
  }
72
- getParsedSyncRules(options) {
73
- const { parsed, options: cachedOptions } = this.parsedSyncRulesCache ?? {};
74
- if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema) {
75
- this.parsedSyncRulesCache = { parsed: this.sync_rules.parsed(options).hydratedSyncConfig(), options };
107
+ getParsedSyncConfigSet(options) {
108
+ let parsed = this.parsedSyncConfigSets.get(options.defaultSchema);
109
+ if (parsed == null) {
110
+ parsed = this.replicationStream.parsed(options);
111
+ this.parsedSyncConfigSets.set(options.defaultSchema, parsed);
76
112
  }
77
- return this.parsedSyncRulesCache.parsed;
113
+ return parsed;
114
+ }
115
+ getParsedSyncRules(options) {
116
+ return this.getParsedSyncConfigSet(options).hydratedSyncConfig;
78
117
  }
79
118
  async getCheckpoint() {
80
119
  return (await this.getCheckpointInternal()) ?? new EmptyReplicationCheckpoint();
@@ -86,41 +125,47 @@ export class MongoSyncBucketStorage extends BaseObserver {
86
125
  return null;
87
126
  }
88
127
  const snapshotTime = session.snapshotTime;
128
+ const clusterTime = session.clusterTime;
89
129
  if (snapshotTime == null) {
90
130
  throw new ServiceAssertionError('Missing snapshotTime in getCheckpoint()');
91
131
  }
92
- return new MongoReplicationCheckpoint(this, state.checkpoint, state.lsn, snapshotTime);
132
+ if (clusterTime == null) {
133
+ throw new ServiceAssertionError('Missing clusterTime in getCheckpoint()');
134
+ }
135
+ return new MongoReplicationCheckpoint(this, state.checkpoint, state.lsn, snapshotTime, clusterTime, state.parameterChangesInvalidBefore);
93
136
  });
94
137
  }
95
138
  async initializeStorage() {
96
139
  if (this.#storageInitialized) {
97
140
  return;
98
141
  }
99
- await this.db.initializeStreamStorage(this.group_id);
142
+ await this.db.initializeStreamStorage(this.replicationStreamId);
100
143
  await this.initializeVersionStorage();
101
144
  this.#storageInitialized = true;
102
145
  }
103
- async createWriter(options) {
104
- await this.initializeStorage();
105
- const state = await this.getWriterSyncState();
106
- const batchOptions = {
146
+ /**
147
+ * The version-independent part of the batch options.
148
+ */
149
+ writerBatchOptions(options) {
150
+ return {
107
151
  logger: options.logger ?? this.logger,
108
152
  db: this.db,
109
- syncRules: this.sync_rules.parsed(options).hydratedSyncConfig(),
110
- mapping: this.sync_rules.mapping,
111
- groupId: this.group_id,
112
- slotName: this.slot_name,
113
- lastCheckpointLsn: state.lastCheckpointLsn,
114
- resumeFromLsn: state.resumeFromLsn,
115
- keepaliveOp: state.keepaliveOp,
153
+ parsedSyncConfig: this.getParsedSyncConfigSet(options),
154
+ replicationStreamId: this.replicationStreamId,
155
+ replicationStreamName: this.replicationStreamName,
116
156
  storeCurrentData: options.storeCurrentData,
117
157
  skipExistingRows: options.skipExistingRows ?? false,
118
158
  markRecordUnavailable: options.markRecordUnavailable,
119
159
  hooks: options.hooks,
120
- syncConfigId: state.syncConfigId,
121
- tracer: options.tracer
160
+ tracer: options.tracer,
161
+ signal: options.signal,
162
+ objectStorage: this.objectStorage,
163
+ inlineThresholdBytes: this.inlineThresholdBytes
122
164
  };
123
- const writer = this.createWriterImpl(batchOptions);
165
+ }
166
+ async createWriter(options) {
167
+ await this.initializeStorage();
168
+ const writer = await this.createWriterImpl(options);
124
169
  this.iterateListeners((cb) => cb.batchStarted?.(writer));
125
170
  return writer;
126
171
  }
@@ -136,8 +181,14 @@ export class MongoSyncBucketStorage extends BaseObserver {
136
181
  async *getBucketDataBatch(checkpoint, dataBuckets, options) {
137
182
  yield* this.getBucketDataBatchImpl(checkpoint, dataBuckets, options);
138
183
  }
139
- async getChecksums(checkpoint, buckets) {
140
- return this.checksums.getChecksums(checkpoint, buckets);
184
+ async getChecksums(checkpoint, buckets, options) {
185
+ const mongoCheckpoint = checkpoint;
186
+ const snapshotTime = mongoCheckpoint.snapshotTime; // May be undefined in tests
187
+ return this.checksums.getChecksums(checkpoint.checkpoint, buckets, {
188
+ snapshotTime,
189
+ clusterTime: mongoCheckpoint.clusterTime,
190
+ readPreference: options?.requestHint == 'bulk' ? this.readPreference : undefined
191
+ });
141
192
  }
142
193
  clearChecksumCache() {
143
194
  this.checksums.clearCache();
@@ -152,6 +203,7 @@ export class MongoSyncBucketStorage extends BaseObserver {
152
203
  async getStatus() {
153
204
  return this.getStatusImpl();
154
205
  }
206
+ async clearCustomCheckpointRequests(_signal) { }
155
207
  async clear(options) {
156
208
  const signal = options?.signal;
157
209
  if (signal?.aborted) {
@@ -163,22 +215,13 @@ export class MongoSyncBucketStorage extends BaseObserver {
163
215
  await this.clearSourceRecords(signal);
164
216
  await this.clearBucketState(signal);
165
217
  await this.clearSourceTables(signal);
218
+ await this.clearCustomCheckpointRequests(signal);
166
219
  this.#storageInitialized = false;
167
220
  }
168
- async clearDeleteMany(label, operation, signal) {
169
- await retryOnMongoMaxTimeMSExpired(operation, {
170
- signal,
171
- abortMessage: 'Aborted clearing data',
172
- retryDelayMs: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS / 5,
173
- onRetry: () => {
174
- this.logger.info(`Cleared batch of ${label} in ${lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS}ms, continuing...`);
175
- }
176
- });
177
- }
178
221
  async reportError(e) {
179
222
  const message = String(e.message ?? 'Replication failure');
180
223
  await this.db.sync_rules.updateOne({
181
- _id: this.group_id
224
+ _id: this.replicationStreamId
182
225
  }, {
183
226
  $set: {
184
227
  last_fatal_error: message,
@@ -189,29 +232,16 @@ export class MongoSyncBucketStorage extends BaseObserver {
189
232
  async compact(options) {
190
233
  let maxOpId = options?.maxOpId;
191
234
  if (maxOpId == null) {
235
+ // For PROCESSING streams, this will be undefined.
192
236
  const checkpoint = await this.getCheckpointInternal();
193
237
  maxOpId = checkpoint?.checkpoint ?? undefined;
194
238
  }
195
239
  await this.createMongoCompactor({ ...options, maxOpId, logger: this.logger }).compact();
196
- if (maxOpId != null && options?.compactParameterData) {
197
- await this.createMongoParameterCompactor(maxOpId, options).compact();
240
+ if (maxOpId != null && options?.compactParameterData && this.replicationStream.state == SyncRuleState.ACTIVE) {
241
+ // Use the stream-scoped logger, matching bucket compaction above.
242
+ await this.createMongoParameterCompactor(maxOpId, { ...options, logger: this.logger }).compact();
198
243
  }
199
244
  }
200
- async populatePersistentChecksumCache(options) {
201
- this.logger.info(`Populating persistent checksum cache...`);
202
- const start = Date.now();
203
- const compactor = this.createMongoCompactor({
204
- ...options,
205
- memoryLimitMB: 0,
206
- logger: this.logger
207
- });
208
- const result = await compactor.populateChecksums({
209
- minBucketChanges: options.minBucketChanges ?? 10
210
- });
211
- const duration = Date.now() - start;
212
- this.logger.info(`Populated persistent checksum cache in ${(duration / 1000).toFixed(1)}s`);
213
- return result;
214
- }
215
245
  async *watchActiveCheckpoint(signal) {
216
246
  if (signal.aborted) {
217
247
  return;
@@ -239,7 +269,7 @@ export class MongoSyncBucketStorage extends BaseObserver {
239
269
  for await (const nextCheckpoint of iter) {
240
270
  if (nextCheckpoint.lsn != null && !queriedInitialWriteCheckpoint) {
241
271
  writeCheckpoint = await this.writeCheckpointAPI.lastWriteCheckpoint({
242
- sync_rules_id: this.group_id,
272
+ sync_rules_id: this.replicationStreamId,
243
273
  user_id: options.user_id,
244
274
  heads: {
245
275
  '1': nextCheckpoint.lsn
@@ -268,7 +298,7 @@ export class MongoSyncBucketStorage extends BaseObserver {
268
298
  let updatedWriteCheckpoint = updates.updatedWriteCheckpoints.get(options.user_id) ?? null;
269
299
  if (updates.invalidateWriteCheckpoints) {
270
300
  updatedWriteCheckpoint = await this.writeCheckpointAPI.lastWriteCheckpoint({
271
- sync_rules_id: this.group_id,
301
+ sync_rules_id: this.replicationStreamId,
272
302
  user_id: options.user_id,
273
303
  heads: {
274
304
  '1': nextCheckpoint.lsn
@@ -355,11 +385,32 @@ export class MongoSyncBucketStorage extends BaseObserver {
355
385
  return this.getDataBucketChangesImpl(options);
356
386
  }
357
387
  async getParameterBucketChanges(options) {
358
- return this.getParameterBucketChangesImpl(options);
388
+ const nextCheckpoint = requireMongoCheckpoint(options.nextCheckpoint);
389
+ if (options.lastCheckpoint.checkpoint < nextCheckpoint.parameterChangesInvalidBefore) {
390
+ // Parameter compaction may have deleted parameter entries in the range we'd have to query
391
+ // to find the individual changed lookups. Invalidate all parameter buckets instead.
392
+ //
393
+ // The fence is committed before the first delete of a compaction pass, and captured in the
394
+ // same snapshot as the checkpoint, so a checkpoint that could miss a deleted entry always
395
+ // observes the fence as well.
396
+ return {
397
+ invalidateParameterBuckets: true,
398
+ updatedParameterLookups: new Set()
399
+ };
400
+ }
401
+ // The query below runs at the checkpoint snapshot, which still sees entries deleted by a
402
+ // compaction pass that started after the snapshot.
403
+ return this.getParameterBucketChangesImpl({
404
+ lastCheckpoint: options.lastCheckpoint,
405
+ nextCheckpoint
406
+ });
359
407
  }
360
408
  checkpointChangesCache = new LRUCache({
361
409
  max: 50,
362
410
  maxSize: 12 * 1024 * 1024,
411
+ // When we have more fetches than the cache size, complete the fetches instead
412
+ // of failing with Error('evicted').
413
+ ignoreFetchAbort: true,
363
414
  sizeCalculation: (value) => {
364
415
  const paramSize = [...value.updatedParameterLookups].reduce((a, b) => a + b.length, 0);
365
416
  const bucketSize = [...value.updatedDataBuckets].reduce((a, b) => a + b.length, 0);
@@ -371,7 +422,10 @@ export class MongoSyncBucketStorage extends BaseObserver {
371
422
  }
372
423
  });
373
424
  async getCheckpointChanges(options) {
374
- const key = `${options.lastCheckpoint.checkpoint}_${options.lastCheckpoint.lsn}__${options.nextCheckpoint.checkpoint}_${options.nextCheckpoint.lsn}`;
425
+ // The invalidation fence is part of the identity: the same checkpoint pair read before and
426
+ // after a compaction pass produces different results (specific lookups vs. invalidate-all).
427
+ const fence = requireMongoCheckpoint(options.nextCheckpoint).parameterChangesInvalidBefore;
428
+ const key = `${options.lastCheckpoint.checkpoint}_${options.lastCheckpoint.lsn}__${options.nextCheckpoint.checkpoint}_${options.nextCheckpoint.lsn}_${fence}`;
375
429
  const result = await this.checkpointChangesCache.fetch(key, { context: { options } });
376
430
  return result;
377
431
  }
@@ -386,25 +440,44 @@ export class MongoSyncBucketStorage extends BaseObserver {
386
440
  };
387
441
  }
388
442
  }
443
+ /**
444
+ * We don't support any other constructions of ReplicationCheckpoint.
445
+ */
446
+ function requireMongoCheckpoint(checkpoint) {
447
+ if (!(checkpoint instanceof MongoReplicationCheckpoint)) {
448
+ throw new ServiceAssertionError(`Checkpoint changes require a checkpoint from getCheckpointInternal(), got ${checkpoint.constructor.name}`);
449
+ }
450
+ return checkpoint;
451
+ }
389
452
  class MongoReplicationCheckpoint {
390
453
  checkpoint;
391
454
  lsn;
392
455
  snapshotTime;
456
+ clusterTime;
457
+ parameterChangesInvalidBefore;
393
458
  #storage;
394
- constructor(storage, checkpoint, lsn, snapshotTime) {
459
+ constructor(storage, checkpoint, lsn, snapshotTime, clusterTime,
460
+ /** Captured in the same snapshot as the checkpoint. */
461
+ parameterChangesInvalidBefore) {
395
462
  this.checkpoint = checkpoint;
396
463
  this.lsn = lsn;
397
464
  this.snapshotTime = snapshotTime;
465
+ this.clusterTime = clusterTime;
466
+ this.parameterChangesInvalidBefore = parameterChangesInvalidBefore;
398
467
  this.#storage = storage;
399
468
  }
400
469
  async getParameterSets(lookups, limit) {
401
470
  return this.#storage.getParameterSets(this, lookups, limit);
402
471
  }
403
472
  }
473
+ /**
474
+ * Used when no checkpoint has been persisted yet. This has no snapshot or invalidation fence, so
475
+ * it cannot be used for checkpoint change detection - see {@link requireMongoCheckpoint}.
476
+ */
404
477
  class EmptyReplicationCheckpoint {
405
478
  checkpoint = 0n;
406
479
  lsn = null;
407
- async getParameterSets(_lookups) {
480
+ async getParameterSets(_lookups, _limit) {
408
481
  return [];
409
482
  }
410
483
  }