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

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 (239) hide show
  1. package/CHANGELOG.md +101 -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 +41 -16
  6. package/dist/storage/MongoBucketStorage.js +226 -71
  7. package/dist/storage/MongoBucketStorage.js.map +1 -1
  8. package/dist/storage/implementation/MongoBucketBatch.d.ts +56 -25
  9. package/dist/storage/implementation/MongoBucketBatch.js +110 -62
  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 +17 -19
  15. package/dist/storage/implementation/MongoCompactor.js +80 -263
  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 +7 -6
  21. package/dist/storage/implementation/MongoParameterCompactor.js +25 -2
  22. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  23. package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +13 -0
  24. package/dist/storage/implementation/MongoParsedSyncConfigSet.js +64 -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 +123 -0
  31. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
  32. package/dist/storage/implementation/MongoStorageProvider.js +39 -4
  33. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  34. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +57 -29
  35. package/dist/storage/implementation/MongoSyncBucketStorage.js +87 -61
  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 +3 -3
  39. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  40. package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +2 -1
  41. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +171 -24
  42. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  43. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +8 -0
  44. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js +2 -0
  45. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js.map +1 -0
  46. package/dist/storage/implementation/common/PersistedBatch.d.ts +7 -2
  47. package/dist/storage/implementation/common/PersistedBatch.js +12 -0
  48. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
  49. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
  50. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
  51. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
  52. package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
  53. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
  54. package/dist/storage/implementation/db.js +17 -15
  55. package/dist/storage/implementation/db.js.map +1 -1
  56. package/dist/storage/implementation/models.d.ts +20 -13
  57. package/dist/storage/implementation/models.js.map +1 -1
  58. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +9 -1
  59. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +61 -29
  60. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
  61. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +4 -4
  62. package/dist/storage/implementation/v1/MongoChecksumsV1.js +119 -7
  63. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
  64. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +11 -2
  65. package/dist/storage/implementation/v1/MongoCompactorV1.js +251 -2
  66. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
  67. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +18 -19
  68. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +82 -58
  69. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
  70. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +9 -10
  71. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +2 -8
  72. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -1
  73. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +2 -1
  74. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +3 -0
  75. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -1
  76. package/dist/storage/implementation/v1/models.d.ts +10 -1
  77. package/dist/storage/implementation/v3/BucketDataContextV3.d.ts +13 -0
  78. package/dist/storage/implementation/v3/BucketDataContextV3.js +23 -0
  79. package/dist/storage/implementation/v3/BucketDataContextV3.js.map +1 -0
  80. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +8 -4
  81. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +309 -259
  82. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
  83. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +30 -9
  84. package/dist/storage/implementation/v3/MongoChecksumsV3.js +161 -20
  85. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
  86. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +40 -2
  87. package/dist/storage/implementation/v3/MongoCompactorV3.js +647 -23
  88. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
  89. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +67 -0
  90. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +351 -0
  91. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
  92. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +45 -30
  93. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +272 -140
  94. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
  95. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +6 -3
  96. package/dist/storage/implementation/v3/PersistedBatchV3.js +84 -21
  97. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
  98. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +3 -3
  99. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +13 -11
  100. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
  101. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +15 -13
  102. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +32 -27
  103. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
  104. package/dist/storage/implementation/v3/bucket-format.d.ts +13 -0
  105. package/dist/storage/implementation/v3/bucket-format.js +77 -0
  106. package/dist/storage/implementation/v3/bucket-format.js.map +1 -0
  107. package/dist/storage/implementation/v3/chunking.d.ts +10 -0
  108. package/dist/storage/implementation/v3/chunking.js +30 -0
  109. package/dist/storage/implementation/v3/chunking.js.map +1 -0
  110. package/dist/storage/implementation/v3/models.d.ts +101 -21
  111. package/dist/storage/implementation/v3/models.js +11 -28
  112. package/dist/storage/implementation/v3/models.js.map +1 -1
  113. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +21 -0
  114. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +44 -0
  115. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -0
  116. package/dist/storage/implementation/v3/object-storage/ObjectStorage.d.ts +30 -0
  117. package/dist/storage/implementation/v3/object-storage/ObjectStorage.js +12 -0
  118. package/dist/storage/implementation/v3/object-storage/ObjectStorage.js.map +1 -0
  119. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +70 -0
  120. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +146 -0
  121. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -0
  122. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.d.ts +42 -0
  123. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +285 -0
  124. package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -0
  125. package/dist/storage/implementation/v3/source-table-utils.d.ts +55 -0
  126. package/dist/storage/implementation/v3/source-table-utils.js +214 -0
  127. package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
  128. package/dist/storage/storage-index.d.ts +4 -4
  129. package/dist/storage/storage-index.js +4 -4
  130. package/dist/storage/storage-index.js.map +1 -1
  131. package/dist/types/types.d.ts +49 -3
  132. package/dist/types/types.js +48 -1
  133. package/dist/types/types.js.map +1 -1
  134. package/dist/utils/test-utils.d.ts +1 -2
  135. package/dist/utils/test-utils.js +7 -1
  136. package/dist/utils/test-utils.js.map +1 -1
  137. package/dist/utils/util.d.ts +9 -2
  138. package/dist/utils/util.js +128 -4
  139. package/dist/utils/util.js.map +1 -1
  140. package/package.json +12 -19
  141. package/src/migrations/db/migrations/1782950400000-checkpoint-requested-at-index.ts +55 -0
  142. package/src/storage/MongoBucketStorage.ts +347 -88
  143. package/src/storage/implementation/MongoBucketBatch.ts +147 -82
  144. package/src/storage/implementation/MongoChecksums.ts +93 -153
  145. package/src/storage/implementation/MongoCompactor.ts +102 -316
  146. package/src/storage/implementation/MongoHydrationState.ts +61 -0
  147. package/src/storage/implementation/MongoParameterCompactor.ts +25 -6
  148. package/src/storage/implementation/MongoParsedSyncConfigSet.ts +88 -0
  149. package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
  150. package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +154 -0
  151. package/src/storage/implementation/MongoStorageProvider.ts +46 -4
  152. package/src/storage/implementation/MongoSyncBucketStorage.ts +121 -87
  153. package/src/storage/implementation/MongoSyncRulesLock.ts +7 -4
  154. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +202 -28
  155. package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +9 -0
  156. package/src/storage/implementation/common/PersistedBatch.ts +19 -3
  157. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
  158. package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
  159. package/src/storage/implementation/db.ts +19 -20
  160. package/src/storage/implementation/models.ts +20 -14
  161. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +75 -37
  162. package/src/storage/implementation/v1/MongoChecksumsV1.ts +154 -12
  163. package/src/storage/implementation/v1/MongoCompactorV1.ts +294 -9
  164. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +142 -105
  165. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +18 -27
  166. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
  167. package/src/storage/implementation/v1/models.ts +11 -1
  168. package/src/storage/implementation/v3/BucketDataContextV3.ts +32 -0
  169. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +382 -321
  170. package/src/storage/implementation/v3/MongoChecksumsV3.ts +250 -32
  171. package/src/storage/implementation/v3/MongoCompactorV3.ts +847 -32
  172. package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +482 -0
  173. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +359 -187
  174. package/src/storage/implementation/v3/PersistedBatchV3.ts +118 -37
  175. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
  176. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +46 -36
  177. package/src/storage/implementation/v3/bucket-format.ts +97 -0
  178. package/src/storage/implementation/v3/chunking.ts +40 -0
  179. package/src/storage/implementation/v3/models.ts +132 -55
  180. package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +54 -0
  181. package/src/storage/implementation/v3/object-storage/ObjectStorage.ts +29 -0
  182. package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +171 -0
  183. package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +343 -0
  184. package/src/storage/implementation/v3/source-table-utils.ts +392 -0
  185. package/src/storage/storage-index.ts +12 -4
  186. package/src/types/types.ts +58 -1
  187. package/src/utils/test-utils.ts +8 -3
  188. package/src/utils/util.ts +185 -6
  189. package/test/src/__snapshots__/storage.test.ts.snap +0 -75
  190. package/test/src/__snapshots__/storage_sync.test.ts.snap +35 -617
  191. package/test/src/cleanup-stopped-sync-configs.test.ts +671 -0
  192. package/test/src/helpers/MemoryObjectStorage.ts +53 -0
  193. package/test/src/helpers/s3TestFactory.ts +69 -0
  194. package/test/src/object_storage_lifecycle.test.ts +59 -0
  195. package/test/src/setup.ts +6 -1
  196. package/test/src/storage.test.ts +227 -13
  197. package/test/src/storage_compacting.test.ts +1951 -12
  198. package/test/src/storage_object_storage_inline_threshold.test.ts +116 -0
  199. package/test/src/storage_s3_checksums.test.ts +191 -0
  200. package/test/src/storage_s3_compaction_lifecycle.test.ts +417 -0
  201. package/test/src/storage_s3_reading.test.ts +526 -0
  202. package/test/src/storage_s3_writing.test.ts +135 -0
  203. package/test/src/storage_sync.test.ts +1180 -73
  204. package/test/src/storeCurrentData.test.ts +227 -0
  205. package/test/src/util.ts +2 -1
  206. package/tsconfig.json +1 -4
  207. package/tsconfig.tsbuildinfo +1 -1
  208. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -15
  209. package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
  210. package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
  211. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
  212. package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -67
  213. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
  214. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -29
  215. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -76
  216. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
  217. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
  218. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
  219. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
  220. package/dist/storage/implementation/common/SingleBucketStore.d.ts +0 -54
  221. package/dist/storage/implementation/common/SingleBucketStore.js +0 -3
  222. package/dist/storage/implementation/common/SingleBucketStore.js.map +0 -1
  223. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +0 -9
  224. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +0 -18
  225. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +0 -1
  226. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -4
  227. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
  228. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
  229. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +0 -18
  230. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +0 -48
  231. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +0 -1
  232. package/src/storage/implementation/BucketDefinitionMapping.ts +0 -75
  233. package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -82
  234. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -101
  235. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +0 -15
  236. package/src/storage/implementation/common/SingleBucketStore.ts +0 -63
  237. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +0 -24
  238. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -11
  239. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +0 -68
@@ -1,30 +1,55 @@
1
- import { GetIntanceOptions, LEGACY_STORAGE_VERSION, storage } from '@powersync/service-core';
1
+ import {
2
+ GetIntanceOptions,
3
+ LEGACY_STORAGE_VERSION,
4
+ SingleSyncConfigBucketDefinitionMapping,
5
+ storage
6
+ } from '@powersync/service-core';
2
7
 
3
- import { DO_NOT_LOG, ErrorCode, ServiceError } from '@powersync/lib-services-framework';
8
+ import { DO_NOT_LOG, ErrorCode, logger, ServiceError } from '@powersync/lib-services-framework';
4
9
  import { v4 as uuid } from 'uuid';
5
10
 
6
11
  import * as lib_mongo from '@powersync/lib-service-mongodb';
7
12
  import { mongo } from '@powersync/lib-service-mongodb';
8
13
 
14
+ import {
15
+ describeIncrementalSyncConfigUpdate,
16
+ formatIncrementalSyncConfigUpdateLog,
17
+ isCompatible
18
+ } from '@powersync/service-core';
9
19
  import { ObjectId } from 'bson';
10
- import { generateSlotName } from '../utils/util.js';
11
- import { BucketDefinitionMapping } from './implementation/BucketDefinitionMapping.js';
20
+ import { DEFAULT_CLEAR_BATCH_THROTTLE_RATE } from '../types/types.js';
21
+ import { generateReplicationStreamName } from '../utils/util.js';
12
22
  import type { MongoSyncBucketStorage } from './implementation/createMongoSyncBucketStorage.js';
13
23
  import { createMongoSyncBucketStorage } from './implementation/createMongoSyncBucketStorage.js';
14
24
  import { PowerSyncMongo } from './implementation/db.js';
15
25
  import { getMongoStorageConfig, StorageConfig, SyncRuleDocumentBase } from './implementation/models.js';
16
26
  import { MongoChecksumOptions } from './implementation/MongoChecksums.js';
17
- import {
18
- MongoPersistedSyncRulesContentV1,
19
- MongoPersistedSyncRulesContentV3
20
- } from './implementation/MongoPersistedSyncRulesContent.js';
27
+ import { MongoPersistedReplicationStream } from './implementation/MongoPersistedReplicationStream.js';
21
28
  import { syncRuleStateUpdatePipeline } from './implementation/SyncRuleStateUpdate.js';
22
29
  import { SyncRuleDocumentV1 } from './implementation/v1/models.js';
30
+ import { ObjectStorage } from './implementation/v3/object-storage/ObjectStorage.js';
23
31
  import { VersionedPowerSyncMongoV3 } from './implementation/v3/VersionedPowerSyncMongoV3.js';
24
- import { ReplicationStreamDocumentV3, SyncConfigDefinition } from './storage-index.js';
32
+ import { ReplicationStreamDocumentV3, SyncConfigDefinition, SyncRuleConfigStateV3 } from './storage-index.js';
25
33
 
26
34
  export interface MongoBucketStorageOptions {
27
- checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig' | 'mapping'>;
35
+ checksumOptions?: Omit<MongoChecksumOptions, 'storageConfig'>;
36
+ objectStorage?: ObjectStorage;
37
+ inlineThresholdBytes?: number;
38
+ /**
39
+ * Prefix for replication stream name and Postgres logical replication slot name.
40
+ */
41
+ replicationStreamNamePrefix: string;
42
+ readPreference?: mongo.ReadPreference;
43
+ checksumCacheTtlMs?: number;
44
+ clearBatchThrottleRate?: number;
45
+ /**
46
+ * Reuse a compatible active replication stream by appending a new sync config.
47
+ *
48
+ * This currently requires source replication support. MongoDB sources can process multiple
49
+ * sync configs in one replication stream, but other source connectors still expect a single
50
+ * sync config per stream.
51
+ */
52
+ supportsMultipleSyncConfigs?: boolean;
28
53
  }
29
54
 
30
55
  export class MongoBucketStorage extends storage.BucketStorageFactory {
@@ -32,8 +57,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
32
57
 
33
58
  private readonly client: mongo.MongoClient;
34
59
  private readonly session: mongo.ClientSession;
35
- // TODO: This is still Postgres specific and needs to be reworked
36
- public readonly slot_name_prefix: string;
60
+ public readonly replicationStreamNamePrefix: string;
37
61
 
38
62
  private activeStorageCache: MongoSyncBucketStorage | undefined;
39
63
 
@@ -41,51 +65,59 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
41
65
 
42
66
  constructor(
43
67
  db: PowerSyncMongo,
44
- options: {
45
- slot_name_prefix: string;
46
- },
47
- private internalOptions?: MongoBucketStorageOptions
68
+ private options: MongoBucketStorageOptions
48
69
  ) {
49
70
  super();
50
71
  this.client = db.client;
51
72
  this.db = db;
52
73
  this.session = this.client.startSession();
53
- this.slot_name_prefix = options.slot_name_prefix;
74
+ this.replicationStreamNamePrefix = options.replicationStreamNamePrefix;
54
75
  }
55
76
 
56
77
  async [Symbol.asyncDispose]() {
57
78
  // No-op
58
79
  }
59
80
 
60
- getInstance(syncRules: storage.PersistedSyncRulesContent, options?: GetIntanceOptions): MongoSyncBucketStorage {
61
- let { id, slot_name } = syncRules;
62
- if ((typeof id as any) == 'bigint') {
63
- id = Number(id);
81
+ getInstance(
82
+ replicationStream: storage.PersistedReplicationStream,
83
+ options?: GetIntanceOptions
84
+ ): MongoSyncBucketStorage {
85
+ if (!(replicationStream instanceof MongoPersistedReplicationStream)) {
86
+ throw new Error(`Expected MongoPersistedReplicationStream`);
64
87
  }
65
- const storageConfig = (syncRules as MongoPersistedSyncRulesContentV1).getStorageConfig();
66
- const storage = createMongoSyncBucketStorage(
88
+ let { replicationStreamId, replicationStreamName } = replicationStream;
89
+ if ((typeof replicationStreamId as any) == 'bigint') {
90
+ replicationStreamId = Number(replicationStreamId);
91
+ }
92
+ const storageConfig = replicationStream.getStorageConfig();
93
+ const syncRuleStorage = createMongoSyncBucketStorage(
67
94
  this,
68
- id,
69
- syncRules as MongoPersistedSyncRulesContentV1,
70
- slot_name,
95
+ replicationStreamId,
96
+ replicationStream,
97
+ replicationStreamName,
71
98
  undefined,
72
99
  {
73
- ...this.internalOptions,
74
- storageConfig
100
+ checksumOptions: this.options.checksumOptions,
101
+ readPreference: this.options.readPreference,
102
+ checksumCacheTtlMs: this.options.checksumCacheTtlMs,
103
+ storageConfig,
104
+ objectStorage: this.options.objectStorage,
105
+ inlineThresholdBytes: this.options.inlineThresholdBytes,
106
+ clearBatchThrottleRate: this.options.clearBatchThrottleRate ?? DEFAULT_CLEAR_BATCH_THROTTLE_RATE
75
107
  }
76
108
  );
77
109
  if (!options?.skipLifecycleHooks) {
78
- this.iterateListeners((cb) => cb.syncStorageCreated?.(storage));
110
+ this.iterateListeners((cb) => cb.syncStorageCreated?.(syncRuleStorage));
79
111
  }
80
112
 
81
- storage.registerListener({
113
+ syncRuleStorage.registerListener({
82
114
  batchStarted: (batch) => {
83
115
  batch.registerListener({
84
116
  replicationEvent: (payload) => this.iterateListeners((cb) => cb.replicationEvent?.(payload))
85
117
  });
86
118
  }
87
119
  });
88
- return storage;
120
+ return syncRuleStorage;
89
121
  }
90
122
 
91
123
  async getSystemIdentifier(): Promise<storage.BucketStorageSystemIdentifier> {
@@ -105,25 +137,25 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
105
137
  };
106
138
  }
107
139
 
108
- async restartReplication(sync_rules_group_id: number) {
109
- const next = await this.getNextSyncRulesContent();
110
- const active = await this.getActiveSyncRulesContent();
140
+ async restartReplication(replicationStreamId: number) {
141
+ const next = await this.getDeployingSyncConfig();
142
+ const active = await this.getActiveSyncConfig();
111
143
 
112
- if (next != null && next.id == sync_rules_group_id) {
144
+ if (next != null && next.content.replicationStreamId == replicationStreamId) {
113
145
  // We need to redo the "next" replication stream
114
- await this.updateSyncRules(next.asUpdateOptions());
146
+ await this.updateSyncRules(next.content.asUpdateOptions());
115
147
  // Pro-actively stop replicating
116
148
  await this.db.sync_rules.updateOne(
117
149
  {
118
- _id: next.id,
150
+ _id: next.content.replicationStreamId,
119
151
  state: storage.SyncRuleState.PROCESSING
120
152
  },
121
153
  syncRuleStateUpdatePipeline(storage.SyncRuleState.STOP)
122
154
  );
123
155
  await this.db.notifyCheckpoint();
124
- } else if (next == null && active?.id == sync_rules_group_id) {
156
+ } else if (next == null && active?.content.replicationStreamId == replicationStreamId) {
125
157
  // Slot removed for "active" replication stream, while there is no "next" one.
126
- await this.updateSyncRules(active.asUpdateOptions());
158
+ await this.updateSyncRules(active.content.asUpdateOptions());
127
159
 
128
160
  // In this case we keep the old one as active for clients, so that that existing clients
129
161
  // can still get the latest data while we replicate the new ones.
@@ -131,18 +163,18 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
131
163
 
132
164
  await this.db.sync_rules.updateOne(
133
165
  {
134
- _id: active.id,
166
+ _id: active.content.replicationStreamId,
135
167
  state: storage.SyncRuleState.ACTIVE
136
168
  },
137
169
  syncRuleStateUpdatePipeline(storage.SyncRuleState.ERRORED)
138
170
  );
139
171
  await this.db.notifyCheckpoint();
140
- } else if (next != null && active?.id == sync_rules_group_id) {
172
+ } else if (next != null && active?.content.replicationStreamId == replicationStreamId) {
141
173
  // Already have next replication stream, but need to stop replicating the active one.
142
174
 
143
175
  await this.db.sync_rules.updateOne(
144
176
  {
145
- _id: active.id,
177
+ _id: active.content.replicationStreamId,
146
178
  state: storage.SyncRuleState.ACTIVE
147
179
  },
148
180
  syncRuleStateUpdatePipeline(storage.SyncRuleState.ERRORED)
@@ -155,13 +187,54 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
155
187
  options: storage.UpdateSyncRulesOptions,
156
188
  storageVersion: number,
157
189
  storageConfig: StorageConfig
158
- ): Promise<MongoPersistedSyncRulesContentV3> {
159
- let rules: MongoPersistedSyncRulesContentV3 | undefined = undefined;
190
+ ): Promise<MongoPersistedReplicationStream> {
191
+ let rules: MongoPersistedReplicationStream | undefined = undefined;
160
192
  const versioned = this.db.versioned(storageConfig) as VersionedPowerSyncMongoV3;
161
193
 
162
194
  const session = this.session;
163
195
 
164
196
  await session.withTransaction(async () => {
197
+ const active = await this.db.sync_rules.findOne<ReplicationStreamDocumentV3>(
198
+ {
199
+ state: storage.SyncRuleState.ACTIVE,
200
+ storage_version: storageVersion
201
+ },
202
+ { session, sort: { _id: -1 }, limit: 1 }
203
+ );
204
+ if (active != null) {
205
+ const existingConfigDocs = await this.loadSyncConfigDefinitions(versioned, active, session);
206
+
207
+ if (
208
+ this.options.supportsMultipleSyncConfigs &&
209
+ isCompatible(
210
+ existingConfigDocs.map((d) => d.serialized_plan ?? null),
211
+ options.config,
212
+ logger
213
+ )
214
+ ) {
215
+ logger.info(`Using incremental reprocessing`);
216
+ await this.db.sync_rules.updateMany(
217
+ {
218
+ state: storage.SyncRuleState.PROCESSING
219
+ },
220
+ syncRuleStateUpdatePipeline(storage.SyncRuleState.STOP),
221
+ { session }
222
+ );
223
+ await this.stopEmbeddedDeployingConfigs(active, session);
224
+ rules = await this.appendSyncConfigToStream({
225
+ versioned,
226
+ existing: active,
227
+ existingConfigDocs,
228
+ options,
229
+ storageVersion,
230
+ session
231
+ });
232
+ return;
233
+ }
234
+
235
+ await this.stopEmbeddedDeployingConfigs(active, session);
236
+ }
237
+
165
238
  // Only have a single replication stream with PROCESSING.
166
239
  await this.db.sync_rules.updateMany(
167
240
  {
@@ -188,9 +261,18 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
188
261
  );
189
262
 
190
263
  const id = Number(id_doc!.op_id);
191
- const slot_name = generateSlotName(this.slot_name_prefix, id);
192
-
193
- const mapping = BucketDefinitionMapping.fromParsedSyncRules(options.config.parsed);
264
+ const replicationStreamName = generateReplicationStreamName(this.replicationStreamNamePrefix, id);
265
+
266
+ const mapping =
267
+ options.config.plan == null
268
+ ? // For legacy sync rules and streams, use the parsed config directly to create a mapping
269
+ SingleSyncConfigBucketDefinitionMapping.fromParsedSyncConfig(options.config.parsed)
270
+ : // For new sync streams, always use the serialized version
271
+ SingleSyncConfigBucketDefinitionMapping.constructIncrementalMappingFromSerializedPlans(
272
+ [],
273
+ options.config.plan.plan,
274
+ []
275
+ );
194
276
 
195
277
  const syncConfigDoc: SyncConfigDefinition = {
196
278
  _id: new ObjectId(),
@@ -210,7 +292,6 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
210
292
  {
211
293
  _id: syncConfigDoc._id,
212
294
  state: storage.SyncRuleState.PROCESSING,
213
- keepalive_op: null,
214
295
  last_checkpoint: null,
215
296
  last_checkpoint_lsn: null,
216
297
  no_checkpoint_before: null,
@@ -219,7 +300,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
219
300
  ],
220
301
  snapshot_lsn: undefined,
221
302
  state: storage.SyncRuleState.PROCESSING,
222
- slot_name: slot_name,
303
+ slot_name: replicationStreamName,
223
304
  last_checkpoint_ts: null,
224
305
  last_fatal_error: null,
225
306
  last_fatal_error_ts: null,
@@ -227,20 +308,175 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
227
308
  };
228
309
 
229
310
  await this.db.sync_rules.insertOne(doc, { session });
230
- await this.db.notifyCheckpoint();
231
- rules = new MongoPersistedSyncRulesContentV3(this.db, doc, syncConfigDoc);
311
+ rules = new MongoPersistedReplicationStream(this.db, doc, [syncConfigDoc]);
232
312
  if (options.lock) {
233
313
  // The lock is persisted on rules.current_lock
234
314
  await rules.lock(session);
235
315
  }
236
316
  });
237
317
 
318
+ // Notify only after the transaction has committed, so listeners cannot observe pre-commit state.
319
+ await this.db.notifyCheckpoint();
320
+
238
321
  return rules!;
239
322
  }
240
323
 
241
- async updateSyncRules(
242
- options: storage.UpdateSyncRulesOptions
243
- ): Promise<MongoPersistedSyncRulesContentV1 | MongoPersistedSyncRulesContentV3> {
324
+ private async loadSyncConfigDefinitions(
325
+ versioned: VersionedPowerSyncMongoV3,
326
+ existing: ReplicationStreamDocumentV3,
327
+ session: mongo.ClientSession
328
+ ) {
329
+ const activeConfigIds = existing.sync_configs
330
+ .filter((config) => config.state == storage.SyncRuleState.ACTIVE)
331
+ .map((config) => config._id);
332
+
333
+ return versioned.syncConfigDefinitions
334
+ .find(
335
+ {
336
+ _id: { $in: activeConfigIds }
337
+ },
338
+ { session }
339
+ )
340
+ .toArray();
341
+ }
342
+
343
+ /**
344
+ * Load _all_ definition mappings for a replication stream - used as a base to generate new ids.
345
+ */
346
+ private async loadHistoricalSyncConfigRuleMappings(
347
+ versioned: VersionedPowerSyncMongoV3,
348
+ replicationStreamId: number,
349
+ session: mongo.ClientSession
350
+ ) {
351
+ return versioned.syncConfigDefinitions
352
+ .find(
353
+ {
354
+ replication_stream_id: replicationStreamId
355
+ },
356
+ {
357
+ session,
358
+ projection: {
359
+ rule_mapping: 1
360
+ }
361
+ }
362
+ )
363
+ .toArray();
364
+ }
365
+
366
+ private async stopEmbeddedDeployingConfigs(existing: ReplicationStreamDocumentV3, session: mongo.ClientSession) {
367
+ const deployingConfigs = existing.sync_configs
368
+ .filter((config) => config.state == storage.SyncRuleState.PROCESSING)
369
+ .map((config) => config._id);
370
+ if (deployingConfigs.length == 0) {
371
+ return;
372
+ }
373
+
374
+ await this.db.sync_rules.updateOne(
375
+ {
376
+ _id: existing._id,
377
+ 'sync_configs._id': { $in: deployingConfigs }
378
+ },
379
+ {
380
+ $set: {
381
+ 'sync_configs.$[config].state': storage.SyncRuleState.STOP
382
+ }
383
+ },
384
+ {
385
+ session,
386
+ arrayFilters: [{ 'config._id': { $in: deployingConfigs } }]
387
+ }
388
+ );
389
+ }
390
+
391
+ private logIncrementalDefinitionChanges(changes: ReturnType<typeof describeIncrementalSyncConfigUpdate>) {
392
+ logger.info(`Incremental reprocessing sync config update:\n${formatIncrementalSyncConfigUpdateLog(changes)}`);
393
+ }
394
+
395
+ private async appendSyncConfigToStream(options: {
396
+ versioned: VersionedPowerSyncMongoV3;
397
+ existing: ReplicationStreamDocumentV3;
398
+ existingConfigDocs: SyncConfigDefinition[];
399
+ options: storage.UpdateSyncRulesOptions;
400
+ storageVersion: number;
401
+ session: mongo.ClientSession;
402
+ }): Promise<MongoPersistedReplicationStream> {
403
+ const { versioned, existing, existingConfigDocs, options: updateOptions, storageVersion, session } = options;
404
+ const compatibleConfigs = existingConfigDocs.map((doc) => ({
405
+ plan: doc.serialized_plan!.plan,
406
+ mapping: SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(doc.rule_mapping)
407
+ }));
408
+ const historicalRuleMappings = await this.loadHistoricalSyncConfigRuleMappings(versioned, existing._id, session);
409
+ const reservedMappings = historicalRuleMappings.map((doc) =>
410
+ SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(doc.rule_mapping)
411
+ );
412
+ const mappingResult = SingleSyncConfigBucketDefinitionMapping.constructIncrementalMappingWithChanges(
413
+ compatibleConfigs,
414
+ updateOptions.config.plan!.plan,
415
+ reservedMappings
416
+ );
417
+ const mapping = mappingResult.mapping;
418
+ this.logIncrementalDefinitionChanges(
419
+ describeIncrementalSyncConfigUpdate({
420
+ activeMappings: existingConfigDocs.map((doc) =>
421
+ SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(doc.rule_mapping)
422
+ ),
423
+ newMapping: mapping,
424
+ newSyncConfig: updateOptions.config.parsed,
425
+ mappingChanges: mappingResult.changes
426
+ })
427
+ );
428
+
429
+ const syncConfigDoc: SyncConfigDefinition = {
430
+ _id: new ObjectId(),
431
+ replication_stream_id: existing._id,
432
+ created_at: new Date(),
433
+ storage_version: storageVersion,
434
+ content: updateOptions.config.yaml,
435
+ serialized_plan: updateOptions.config.plan,
436
+ rule_mapping: mapping.serialize()
437
+ };
438
+ await versioned.syncConfigDefinitions.insertOne(syncConfigDoc, { session });
439
+ const syncConfigState: SyncRuleConfigStateV3 = {
440
+ _id: syncConfigDoc._id,
441
+ state: storage.SyncRuleState.PROCESSING,
442
+ last_checkpoint: null,
443
+ last_checkpoint_lsn: null,
444
+ no_checkpoint_before: null,
445
+ snapshot_done: false
446
+ };
447
+
448
+ await this.db.sync_rules.updateOne(
449
+ { _id: existing._id },
450
+ {
451
+ $push: {
452
+ sync_configs: syncConfigState
453
+ },
454
+ $set: {
455
+ last_fatal_error: null,
456
+ last_fatal_error_ts: null
457
+ }
458
+ },
459
+ { session }
460
+ );
461
+ const syncConfigStates = [
462
+ ...existing.sync_configs.filter((config) => config.state == storage.SyncRuleState.ACTIVE),
463
+ syncConfigState
464
+ ];
465
+ const stream = new MongoPersistedReplicationStream(
466
+ this.db,
467
+ {
468
+ ...existing,
469
+ sync_configs: syncConfigStates
470
+ },
471
+ [...existingConfigDocs, syncConfigDoc]
472
+ );
473
+ // The stream already exists, so an active replication job may already hold the stream lock.
474
+ // Deployment only persists the appended sync config; replication job locking is handled by
475
+ // the replicator.
476
+ return stream;
477
+ }
478
+
479
+ async updateSyncRules(options: storage.UpdateSyncRulesOptions): Promise<MongoPersistedReplicationStream> {
244
480
  const storageVersion =
245
481
  options.storageVersion ?? options.config.parsed.config.storageVersion ?? storage.CURRENT_STORAGE_VERSION;
246
482
 
@@ -249,7 +485,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
249
485
  return this.updateSyncRulesV3(options, storageVersion, storageConfig);
250
486
  }
251
487
 
252
- let rules: MongoPersistedSyncRulesContentV1 | undefined = undefined;
488
+ let rules: MongoPersistedReplicationStream | undefined = undefined;
253
489
 
254
490
  const session = this.session;
255
491
 
@@ -280,7 +516,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
280
516
  );
281
517
 
282
518
  const id = Number(id_doc!.op_id);
283
- const slot_name = generateSlotName(this.slot_name_prefix, id);
519
+ const slot_name = generateReplicationStreamName(this.replicationStreamNamePrefix, id);
284
520
 
285
521
  const doc: SyncRuleDocumentV1 = {
286
522
  _id: id,
@@ -303,7 +539,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
303
539
 
304
540
  await this.db.sync_rules.insertOne(doc, { session });
305
541
  await this.db.notifyCheckpoint();
306
- rules = new MongoPersistedSyncRulesContentV1(this.db, doc);
542
+ rules = new MongoPersistedReplicationStream(this.db, doc);
307
543
  if (options.lock) {
308
544
  // The lock is persisted on rules.current_lock
309
545
  await rules.lock(session);
@@ -313,9 +549,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
313
549
  return rules!;
314
550
  }
315
551
 
316
- async getActiveSyncRulesContent(): Promise<
317
- MongoPersistedSyncRulesContentV1 | MongoPersistedSyncRulesContentV3 | null
318
- > {
552
+ async getActiveSyncConfig(): Promise<storage.ResolvedSyncConfig | null> {
319
553
  const doc = await this.db.sync_rules.findOne(
320
554
  {
321
555
  state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
@@ -323,10 +557,12 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
323
557
  { sort: { _id: -1 }, limit: 1 }
324
558
  );
325
559
 
326
- return this.getSyncRulesContent(doc, [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED]);
560
+ return this.resolvedSyncConfigFromDoc(doc, [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED], {
561
+ cacheActiveStorage: true
562
+ });
327
563
  }
328
564
 
329
- private async getSyncRulesContent(doc: SyncRuleDocumentBase | null, stateFilter: storage.SyncRuleState[]) {
565
+ private async replicationStreamFromDoc(doc: SyncRuleDocumentBase | null, stateFilter: storage.SyncRuleState[]) {
330
566
  if (doc == null) {
331
567
  return null;
332
568
  }
@@ -334,8 +570,8 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
334
570
 
335
571
  if (storageConfig.incrementalReprocessing) {
336
572
  const v3 = doc as ReplicationStreamDocumentV3;
337
- const active = v3.sync_configs.find((c) => stateFilter.includes(c.state));
338
- if (active == null) {
573
+ const matching = v3.sync_configs.filter((c) => stateFilter.includes(c.state));
574
+ if (matching.length == 0) {
339
575
  return null;
340
576
  }
341
577
 
@@ -343,28 +579,33 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
343
579
  // that checks for active replication streams.
344
580
  // It is not a major bottleneck though, since it only runs once every couple of seconds at most.
345
581
  const db = this.db.versioned(storageConfig) as VersionedPowerSyncMongoV3;
346
- const syncConfigDoc = await db.syncConfigDefinitions.findOne({ _id: active._id });
347
- if (syncConfigDoc == null) {
582
+ const syncConfigDocs = await db.syncConfigDefinitions
583
+ .find({
584
+ _id: { $in: matching.map((config) => config._id) }
585
+ })
586
+ .toArray();
587
+
588
+ if (syncConfigDocs.length == 0) {
348
589
  return null;
349
590
  }
350
- return new MongoPersistedSyncRulesContentV3(this.db, v3, syncConfigDoc);
591
+ return new MongoPersistedReplicationStream(this.db, v3, syncConfigDocs);
351
592
  }
352
593
 
353
- return new MongoPersistedSyncRulesContentV1(this.db, doc as SyncRuleDocumentV1);
594
+ return new MongoPersistedReplicationStream(this.db, doc as SyncRuleDocumentV1);
354
595
  }
355
596
 
356
- async getNextSyncRulesContent(): Promise<MongoPersistedSyncRulesContentV1 | MongoPersistedSyncRulesContentV3 | null> {
597
+ async getDeployingSyncConfig(): Promise<storage.ResolvedSyncConfig | null> {
357
598
  const doc = await this.db.sync_rules.findOne(
358
599
  {
359
- state: storage.SyncRuleState.PROCESSING
600
+ $or: [{ state: storage.SyncRuleState.PROCESSING }, { 'sync_configs.state': storage.SyncRuleState.PROCESSING }]
360
601
  },
361
602
  { sort: { _id: -1 }, limit: 1 }
362
603
  );
363
604
 
364
- return this.getSyncRulesContent(doc, [storage.SyncRuleState.PROCESSING]);
605
+ return this.resolvedSyncConfigFromDoc(doc, [storage.SyncRuleState.PROCESSING]);
365
606
  }
366
607
 
367
- async getReplicatingSyncRules(): Promise<storage.PersistedSyncRulesContent[]> {
608
+ async getReplicatingReplicationStreams(): Promise<storage.PersistedReplicationStream[]> {
368
609
  const docs = await this.db.sync_rules
369
610
  .find({
370
611
  state: { $in: [storage.SyncRuleState.PROCESSING, storage.SyncRuleState.ACTIVE] }
@@ -374,13 +615,13 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
374
615
  return (
375
616
  await Promise.all(
376
617
  docs.map((doc) => {
377
- return this.getSyncRulesContent(doc, [storage.SyncRuleState.PROCESSING, storage.SyncRuleState.ACTIVE]);
618
+ return this.replicationStreamFromDoc(doc, [storage.SyncRuleState.PROCESSING, storage.SyncRuleState.ACTIVE]);
378
619
  })
379
620
  )
380
621
  ).filter((r) => r != null);
381
622
  }
382
623
 
383
- async getStoppedSyncRules(): Promise<storage.PersistedSyncRulesContent[]> {
624
+ async getStoppedReplicationStreams(): Promise<storage.PersistedReplicationStream[]> {
384
625
  const docs = await this.db.sync_rules
385
626
  .find({
386
627
  state: storage.SyncRuleState.STOP
@@ -390,28 +631,46 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
390
631
  return (
391
632
  await Promise.all(
392
633
  docs.map((doc) => {
393
- return this.getSyncRulesContent(doc, [storage.SyncRuleState.STOP]);
634
+ return this.replicationStreamFromDoc(doc, [storage.SyncRuleState.STOP]);
394
635
  })
395
636
  )
396
637
  ).filter((d) => d != null);
397
638
  }
398
639
 
399
- async getActiveStorage(): Promise<MongoSyncBucketStorage | null> {
400
- const content = await this.getActiveSyncRulesContent();
401
- if (content == null) {
640
+ private async resolvedSyncConfigFromDoc(
641
+ doc: SyncRuleDocumentBase | null,
642
+ stateFilter: storage.SyncRuleState[],
643
+ options: { cacheActiveStorage?: boolean } = {}
644
+ ): Promise<storage.ResolvedSyncConfig | null> {
645
+ const stream = await this.replicationStreamFromDoc(doc, stateFilter);
646
+ if (stream == null) {
402
647
  return null;
403
648
  }
404
649
 
405
- // It is important that this instance is cached.
406
- // Not for the instance construction itself, but to ensure that internal caches on the instance
407
- // are re-used properly.
408
- if (this.activeStorageCache?.group_id == content.id) {
409
- return this.activeStorageCache;
410
- } else {
411
- const instance = this.getInstance(content);
412
- this.activeStorageCache = instance;
413
- return instance;
414
- }
650
+ const content = stream.syncConfigContent[0];
651
+ const thisFactory = this;
652
+
653
+ return {
654
+ content,
655
+ replicationStream: stream,
656
+ get storage() {
657
+ // It is important that this instance is cached.
658
+ // Not for the instance construction itself, but to ensure that internal caches on the instance
659
+ // are re-used properly.
660
+ if (
661
+ options.cacheActiveStorage &&
662
+ thisFactory.activeStorageCache?.replicationStream.replicationJobId == stream.replicationJobId
663
+ ) {
664
+ return thisFactory.activeStorageCache;
665
+ }
666
+
667
+ const instance = thisFactory.getInstance(stream);
668
+ if (options.cacheActiveStorage) {
669
+ thisFactory.activeStorageCache = instance;
670
+ }
671
+ return instance;
672
+ }
673
+ };
415
674
  }
416
675
 
417
676
  async getStorageMetrics(): Promise<storage.StorageMetrics> {