@powersync/service-module-mongodb-storage 0.17.0 → 0.18.1

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 (185) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/storage/MongoBucketStorage.d.ts +34 -16
  3. package/dist/storage/MongoBucketStorage.js +219 -70
  4. package/dist/storage/MongoBucketStorage.js.map +1 -1
  5. package/dist/storage/implementation/MongoBucketBatch.d.ts +42 -23
  6. package/dist/storage/implementation/MongoBucketBatch.js +61 -44
  7. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  8. package/dist/storage/implementation/MongoChecksums.d.ts +10 -9
  9. package/dist/storage/implementation/MongoChecksums.js +3 -111
  10. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  11. package/dist/storage/implementation/MongoCompactor.d.ts +2 -0
  12. package/dist/storage/implementation/MongoCompactor.js +18 -9
  13. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  14. package/dist/storage/implementation/MongoHydrationState.d.ts +11 -0
  15. package/dist/storage/implementation/MongoHydrationState.js +49 -0
  16. package/dist/storage/implementation/MongoHydrationState.js.map +1 -0
  17. package/dist/storage/implementation/MongoParameterCompactor.d.ts +6 -5
  18. package/dist/storage/implementation/MongoParameterCompactor.js +24 -1
  19. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  20. package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +13 -0
  21. package/dist/storage/implementation/MongoParsedSyncConfigSet.js +64 -0
  22. package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -0
  23. package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +36 -0
  24. package/dist/storage/implementation/MongoPersistedReplicationStream.js +90 -0
  25. package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -0
  26. package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +25 -0
  27. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +123 -0
  28. package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -0
  29. package/dist/storage/implementation/MongoStorageProvider.js +5 -2
  30. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  31. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +41 -23
  32. package/dist/storage/implementation/MongoSyncBucketStorage.js +51 -43
  33. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  34. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +1 -1
  35. package/dist/storage/implementation/MongoSyncRulesLock.js +3 -3
  36. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  37. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +6 -0
  38. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js +2 -0
  39. package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.js.map +1 -0
  40. package/dist/storage/implementation/common/PersistedBatch.d.ts +1 -2
  41. package/dist/storage/implementation/common/PersistedBatch.js +5 -0
  42. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
  43. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +1 -1
  44. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
  45. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +2 -2
  46. package/dist/storage/implementation/createMongoSyncBucketStorage.js +4 -4
  47. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -1
  48. package/dist/storage/implementation/db.js +9 -14
  49. package/dist/storage/implementation/db.js.map +1 -1
  50. package/dist/storage/implementation/models.d.ts +4 -13
  51. package/dist/storage/implementation/models.js.map +1 -1
  52. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +9 -1
  53. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +42 -26
  54. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
  55. package/dist/storage/implementation/v1/MongoChecksumsV1.js +114 -3
  56. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -1
  57. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +15 -17
  58. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +47 -45
  59. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
  60. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +2 -1
  61. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +3 -0
  62. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -1
  63. package/dist/storage/implementation/v1/models.d.ts +10 -1
  64. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +8 -4
  65. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +286 -259
  66. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
  67. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +27 -7
  68. package/dist/storage/implementation/v3/MongoChecksumsV3.js +202 -18
  69. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -1
  70. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +19 -2
  71. package/dist/storage/implementation/v3/MongoCompactorV3.js +511 -22
  72. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
  73. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +62 -0
  74. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +350 -0
  75. package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -0
  76. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +40 -28
  77. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +232 -116
  78. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
  79. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +1 -1
  80. package/dist/storage/implementation/v3/PersistedBatchV3.js +43 -20
  81. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
  82. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +52 -16
  83. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -1
  84. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +3 -3
  85. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +13 -11
  86. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
  87. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +13 -12
  88. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +29 -27
  89. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
  90. package/dist/storage/implementation/v3/bucket-format.d.ts +4 -0
  91. package/dist/storage/implementation/v3/bucket-format.js +58 -0
  92. package/dist/storage/implementation/v3/bucket-format.js.map +1 -0
  93. package/dist/storage/implementation/v3/chunking.d.ts +10 -0
  94. package/dist/storage/implementation/v3/chunking.js +28 -0
  95. package/dist/storage/implementation/v3/chunking.js.map +1 -0
  96. package/dist/storage/implementation/v3/models.d.ts +64 -21
  97. package/dist/storage/implementation/v3/models.js +11 -28
  98. package/dist/storage/implementation/v3/models.js.map +1 -1
  99. package/dist/storage/implementation/v3/source-table-utils.d.ts +55 -0
  100. package/dist/storage/implementation/v3/source-table-utils.js +214 -0
  101. package/dist/storage/implementation/v3/source-table-utils.js.map +1 -0
  102. package/dist/storage/storage-index.d.ts +4 -4
  103. package/dist/storage/storage-index.js +4 -4
  104. package/dist/storage/storage-index.js.map +1 -1
  105. package/dist/types/types.d.ts +2 -2
  106. package/dist/utils/test-utils.d.ts +1 -2
  107. package/dist/utils/test-utils.js +5 -1
  108. package/dist/utils/test-utils.js.map +1 -1
  109. package/dist/utils/util.d.ts +1 -1
  110. package/dist/utils/util.js +3 -2
  111. package/dist/utils/util.js.map +1 -1
  112. package/package.json +9 -19
  113. package/src/storage/MongoBucketStorage.ts +334 -87
  114. package/src/storage/implementation/MongoBucketBatch.ts +86 -58
  115. package/src/storage/implementation/MongoChecksums.ts +6 -138
  116. package/src/storage/implementation/MongoCompactor.ts +19 -9
  117. package/src/storage/implementation/MongoHydrationState.ts +61 -0
  118. package/src/storage/implementation/MongoParameterCompactor.ts +24 -5
  119. package/src/storage/implementation/MongoParsedSyncConfigSet.ts +88 -0
  120. package/src/storage/implementation/MongoPersistedReplicationStream.ts +118 -0
  121. package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +154 -0
  122. package/src/storage/implementation/MongoStorageProvider.ts +5 -2
  123. package/src/storage/implementation/MongoSyncBucketStorage.ts +70 -56
  124. package/src/storage/implementation/MongoSyncRulesLock.ts +7 -4
  125. package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +7 -0
  126. package/src/storage/implementation/common/PersistedBatch.ts +7 -3
  127. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +1 -1
  128. package/src/storage/implementation/createMongoSyncBucketStorage.ts +21 -7
  129. package/src/storage/implementation/db.ts +11 -19
  130. package/src/storage/implementation/models.ts +4 -14
  131. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +55 -34
  132. package/src/storage/implementation/v1/MongoChecksumsV1.ts +137 -4
  133. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +70 -64
  134. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +11 -1
  135. package/src/storage/implementation/v1/models.ts +11 -1
  136. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +358 -314
  137. package/src/storage/implementation/v3/MongoChecksumsV3.ts +262 -26
  138. package/src/storage/implementation/v3/MongoCompactorV3.ts +646 -29
  139. package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +480 -0
  140. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +315 -157
  141. package/src/storage/implementation/v3/PersistedBatchV3.ts +62 -36
  142. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +59 -19
  143. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +12 -12
  144. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +41 -36
  145. package/src/storage/implementation/v3/bucket-format.ts +70 -0
  146. package/src/storage/implementation/v3/chunking.ts +38 -0
  147. package/src/storage/implementation/v3/models.ts +93 -55
  148. package/src/storage/implementation/v3/source-table-utils.ts +392 -0
  149. package/src/storage/storage-index.ts +12 -4
  150. package/src/utils/test-utils.ts +6 -3
  151. package/src/utils/util.ts +3 -2
  152. package/test/src/__snapshots__/storage.test.ts.snap +0 -75
  153. package/test/src/__snapshots__/storage_sync.test.ts.snap +48 -48
  154. package/test/src/cleanup-stopped-sync-configs.test.ts +648 -0
  155. package/test/src/storage.test.ts +11 -11
  156. package/test/src/storage_compacting.test.ts +1956 -6
  157. package/test/src/storage_sync.test.ts +1077 -68
  158. package/test/src/storeCurrentData.test.ts +17 -7
  159. package/test/src/util.ts +2 -1
  160. package/tsconfig.json +1 -4
  161. package/tsconfig.tsbuildinfo +1 -1
  162. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +0 -15
  163. package/dist/storage/implementation/BucketDefinitionMapping.js +0 -58
  164. package/dist/storage/implementation/BucketDefinitionMapping.js.map +0 -1
  165. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +0 -14
  166. package/dist/storage/implementation/MongoPersistedSyncRules.js +0 -67
  167. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +0 -1
  168. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +0 -29
  169. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +0 -76
  170. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +0 -1
  171. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +0 -13
  172. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +0 -2
  173. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +0 -1
  174. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +0 -9
  175. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +0 -18
  176. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +0 -1
  177. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +0 -4
  178. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +0 -9
  179. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +0 -1
  180. package/src/storage/implementation/BucketDefinitionMapping.ts +0 -75
  181. package/src/storage/implementation/MongoPersistedSyncRules.ts +0 -82
  182. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +0 -101
  183. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +0 -15
  184. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +0 -24
  185. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +0 -11
@@ -6,9 +6,9 @@ import {
6
6
  GetCheckpointChangesOptions,
7
7
  InternalOpId,
8
8
  internalToExternalOpId,
9
- maxLsn,
10
9
  ParameterSetLimitExceededError,
11
10
  ProtocolOpId,
11
+ SingleSyncConfigBucketDefinitionMapping,
12
12
  storage,
13
13
  utils
14
14
  } from '@powersync/service-core';
@@ -17,57 +17,103 @@ import { ParameterLookupRows, ScopedParameterLookup, SqliteJsonRow } from '@powe
17
17
  import * as bson from 'bson';
18
18
  import { mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../../utils/util.js';
19
19
  import { MongoBucketStorage } from '../../MongoBucketStorage.js';
20
- import {
21
- MongoSyncBucketStorageCheckpoint,
22
- MongoSyncBucketStorageContext
23
- } from '../common/MongoSyncBucketStorageContext.js';
24
- import { MongoBucketBatchOptions } from '../MongoBucketBatch.js';
20
+ import { BucketDataDoc } from '../common/BucketDataDoc.js';
21
+ import { MongoSyncBucketStorageCheckpoint } from '../common/MongoSyncBucketStorageCheckpoint.js';
25
22
  import { MongoChecksums } from '../MongoChecksums.js';
26
23
  import { MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
27
24
  import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
28
- import {
29
- MongoPersistedSyncRulesContentV1,
30
- MongoPersistedSyncRulesContentV3
31
- } from '../MongoPersistedSyncRulesContent.js';
25
+ import { MongoPersistedReplicationStream } from '../MongoPersistedReplicationStream.js';
32
26
  import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from '../MongoSyncBucketStorage.js';
27
+ import { loadBucketDataDocument } from './bucket-format.js';
33
28
  import {
34
29
  BucketDataDocumentV3,
35
30
  BucketParameterDocumentV3,
36
- loadBucketDataDocumentV3,
31
+ deserializeParameterLookup,
37
32
  ReplicationStreamDocumentV3,
33
+ serializeParameterLookup,
38
34
  SyncRuleConfigStateV3
39
35
  } from './models.js';
40
36
  import { MongoBucketBatchV3 } from './MongoBucketBatchV3.js';
41
37
  import { MongoChecksumsV3 } from './MongoChecksumsV3.js';
42
38
  import { MongoCompactorV3 } from './MongoCompactorV3.js';
43
- import { MongoParameterCompactorV3 } from './MongoParameterCompactorV3.js';
44
- import { deserializeParameterLookupV3, serializeParameterLookupV3 } from './MongoParameterLookupV3.js';
39
+ import { MongoStoppedSyncConfigCleanup } from './MongoStoppedSyncConfigCleanup.js';
45
40
  import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
46
41
 
42
+ export interface MongoSyncBucketStorageContextV3 {
43
+ db: VersionedPowerSyncMongoV3;
44
+ replicationStreamId: number;
45
+ /**
46
+ * Persisted mapping of the single sync config that read operations are served from.
47
+ *
48
+ * Implemented as a lazy getter: accessing it on a storage instance with multiple sync
49
+ * configs throws, but operations that don't use it remain unaffected.
50
+ */
51
+ readonly mapping: SingleSyncConfigBucketDefinitionMapping;
52
+ }
53
+
54
+ function* walkDocumentOps(
55
+ data: BucketDataDoc[],
56
+ documentOpCounts: number[],
57
+ documentSizes: number[]
58
+ ): Generator<{ row: BucketDataDoc; docIndex: number; isLastOpInDocument: boolean }> {
59
+ let opIndex = 0;
60
+ for (const [docIndex, opCount] of documentOpCounts.entries()) {
61
+ for (let i = 0; i < opCount; i++) {
62
+ yield { row: data[opIndex++], docIndex, isLastOpInDocument: i === opCount - 1 };
63
+ }
64
+ }
65
+ }
66
+
67
+ function extractRowsFromDocument(
68
+ doc: BucketDataDocumentV3,
69
+ context: { replicationStreamId: number; definitionId: string },
70
+ bucketMap: Map<string, InternalOpId>,
71
+ endOpId: InternalOpId,
72
+ remainingLimit: number
73
+ ): { rows: BucketDataDoc[]; remainingLimit: number; limitReached: boolean } {
74
+ const rows: BucketDataDoc[] = [];
75
+ for (const row of loadBucketDataDocument(context, doc)) {
76
+ const bucket = row.bucketKey.bucket;
77
+ const bucketStart = bucketMap.get(bucket);
78
+ if (bucketStart == null) {
79
+ throw new Error(`data for unexpected bucket: ${bucket}`);
80
+ }
81
+ if (row.o <= bucketStart) {
82
+ continue;
83
+ }
84
+ if (row.o > endOpId) {
85
+ continue;
86
+ }
87
+
88
+ rows.push(row);
89
+ remainingLimit--;
90
+ if (remainingLimit <= 0) {
91
+ return { rows, remainingLimit, limitReached: true };
92
+ }
93
+ }
94
+ return { rows, remainingLimit, limitReached: false };
95
+ }
96
+
47
97
  export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
48
- // Declare types to be more specific
49
98
  declare readonly db: VersionedPowerSyncMongoV3;
50
99
  declare readonly checksums: MongoChecksumsV3;
51
100
 
52
- private readonly syncRulesV3: MongoPersistedSyncRulesContentV3;
53
-
54
101
  constructor(
55
102
  factory: MongoBucketStorage,
56
- group_id: number,
57
- sync_rules: MongoPersistedSyncRulesContentV1,
58
- slot_name: string,
103
+ replicationStreamId: number,
104
+ replicationStream: MongoPersistedReplicationStream,
105
+ replicationStreamName: string,
59
106
  writeCheckpointMode: storage.WriteCheckpointMode | undefined,
60
107
  options: MongoSyncBucketStorageOptions
61
108
  ) {
62
- super(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
63
- if (!(sync_rules instanceof MongoPersistedSyncRulesContentV3)) {
109
+ super(factory, replicationStreamId, replicationStream, replicationStreamName, writeCheckpointMode, options);
110
+ if (replicationStream.syncConfigIds.length == 0) {
64
111
  throw new ServiceAssertionError('Missing sync config id for storage v3');
65
112
  }
66
- this.syncRulesV3 = sync_rules;
67
113
  }
68
114
 
69
- private get syncConfigId(): bson.ObjectId {
70
- return this.syncRulesV3.syncConfigId;
115
+ private get syncConfigIds(): bson.ObjectId[] {
116
+ return this.replicationStream.syncConfigIds;
71
117
  }
72
118
 
73
119
  private get syncRulesCollection(): mongo.Collection<ReplicationStreamDocumentV3> {
@@ -76,10 +122,10 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
76
122
 
77
123
  private syncConfigMatch(extra: mongo.Document = {}): mongo.Filter<ReplicationStreamDocumentV3> {
78
124
  return {
79
- _id: this.group_id,
125
+ _id: this.replicationStreamId,
80
126
  sync_configs: {
81
127
  $elemMatch: {
82
- _id: this.syncConfigId,
128
+ _id: { $in: this.syncConfigIds },
83
129
  ...extra
84
130
  }
85
131
  }
@@ -89,44 +135,27 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
89
135
  private syncConfigProjection(extra: mongo.Document = {}): mongo.Document {
90
136
  return {
91
137
  ...extra,
92
- sync_configs: {
93
- $elemMatch: {
94
- _id: this.syncConfigId
95
- }
96
- }
138
+ sync_configs: 1
97
139
  };
98
140
  }
99
141
 
100
- private syncConfigArrayFilters(): mongo.UpdateOptions['arrayFilters'] {
101
- return [{ 'config._id': this.syncConfigId }];
102
- }
103
-
104
- /**
105
- * For now, we only support a single sync config per replication stream.
106
- *
107
- * In the future we'll add support for multiple.
108
- */
109
- private selectedSyncConfig(
110
- doc: Pick<ReplicationStreamDocumentV3, 'sync_configs'> | null
111
- ): SyncRuleConfigStateV3 | null {
112
- return doc?.sync_configs?.[0] ?? null;
142
+ private selectedSyncConfigs(doc: Pick<ReplicationStreamDocumentV3, 'sync_configs'> | null): SyncRuleConfigStateV3[] {
143
+ return doc?.sync_configs?.filter((config) => this.syncConfigIds.some((id) => id.equals(config._id))) ?? [];
113
144
  }
114
145
 
115
146
  protected async initializeVersionStorage(): Promise<void> {
116
- const mapping = this.mapping;
117
- for (let source of mapping.allBucketDefinitionIds()) {
118
- const collection = this.db.bucketDataV3(this.group_id, source).collectionName;
119
- await this.db.db
120
- .createCollection(collection, { clusteredIndex: { name: '_id', unique: true, key: { _id: 1 } } })
121
- .catch((error) => {
122
- if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceExists') {
123
- return;
124
- }
125
- throw error;
126
- });
147
+ const storageIds = this.storageIds;
148
+ for (const source of storageIds.bucketDefinitionIds) {
149
+ const collection = this.db.bucketData(this.replicationStreamId, source).collectionName;
150
+ await this.db.db.createCollection(collection, {}).catch((error) => {
151
+ if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceExists') {
152
+ return;
153
+ }
154
+ throw error;
155
+ });
127
156
  }
128
- for (let indexId of mapping.allParameterIndexIds()) {
129
- await this.db.parameterIndexV3(this.group_id, indexId).createIndex(
157
+ for (const indexId of storageIds.parameterIndexIds) {
158
+ await this.db.parameterIndex(this.replicationStreamId, indexId).createIndex(
130
159
  {
131
160
  lookup: 1,
132
161
  key: 1,
@@ -140,10 +169,10 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
140
169
  }
141
170
 
142
171
  protected createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums {
143
- return new MongoChecksumsV3(this.db, this.group_id, {
172
+ return new MongoChecksumsV3(this.db, this.replicationStreamId, {
144
173
  ...options.checksumOptions,
145
174
  storageConfig: options?.storageConfig,
146
- mapping: this.sync_rules.mapping
175
+ syncConfigMapping: () => this.singleSyncConfigMapping()
147
176
  });
148
177
  }
149
178
 
@@ -155,11 +184,36 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
155
184
  checkpoint: InternalOpId,
156
185
  options: storage.CompactOptions
157
186
  ): MongoParameterCompactor {
158
- return new MongoParameterCompactorV3(this.db, this.group_id, checkpoint, options);
187
+ return new MongoParameterCompactor(this.db, this.replicationStreamId, checkpoint, options, () =>
188
+ this.db
189
+ .listParameterIndexCollections(this.replicationStreamId)
190
+ .then((collections) =>
191
+ collections.map((c) => c.collection as unknown as lib_mongo.mongo.Collection<lib_mongo.mongo.Document>)
192
+ )
193
+ );
194
+ }
195
+
196
+ protected async fetchPersistedOpHead(): Promise<InternalOpId | null> {
197
+ const doc = await this.syncRulesCollection.findOne(
198
+ { _id: this.replicationStreamId },
199
+ { projection: { last_persisted_op: 1 } }
200
+ );
201
+ return doc?.last_persisted_op == null ? null : BigInt(doc.last_persisted_op);
159
202
  }
160
203
 
161
- protected createWriterImpl(batchOptions: MongoBucketBatchOptions): storage.BucketStorageBatch {
162
- return new MongoBucketBatchV3(batchOptions);
204
+ protected async createWriterImpl(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch> {
205
+ const doc = await this.syncRulesCollection.findOne(
206
+ { _id: this.replicationStreamId },
207
+ { projection: { resume_lsn: 1 } }
208
+ );
209
+
210
+ return new MongoBucketBatchV3({
211
+ ...this.writerBatchOptions(options),
212
+ // The stream-level replication position - per-config checkpoint LSNs are consistency
213
+ // markers and do not affect where replication resumes.
214
+ resumeFromLsn: doc?.resume_lsn ?? null,
215
+ syncConfigIds: this.syncConfigIds
216
+ });
163
217
  }
164
218
 
165
219
  protected async fetchCheckpointState(
@@ -174,8 +228,20 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
174
228
  projection: this.syncConfigProjection()
175
229
  }
176
230
  );
177
- const syncConfig = this.selectedSyncConfig(doc);
178
- if (!syncConfig?.snapshot_done) {
231
+ // Checkpoints are served from the single active config. A PROCESSING config in the same
232
+ // stream (incremental reprocessing) does not affect checkpoints until it is activated.
233
+ const syncConfigs = this.selectedSyncConfigs(doc).filter((config) =>
234
+ [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED].includes(config.state)
235
+ );
236
+ if (syncConfigs.length > 1) {
237
+ // Activation atomically replaces the previous active config, so this cannot happen unless
238
+ // the stored state is corrupt.
239
+ throw new ServiceAssertionError(
240
+ `Expected a single active sync config, got ${syncConfigs.map((config) => config._id.toHexString()).join(', ')}`
241
+ );
242
+ }
243
+ const syncConfig = syncConfigs[0];
244
+ if (syncConfig == null || !syncConfig.snapshot_done) {
179
245
  return null;
180
246
  }
181
247
  return {
@@ -184,24 +250,10 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
184
250
  };
185
251
  }
186
252
 
187
- protected async getWriterSyncState() {
188
- const doc = await this.syncRulesCollection.findOne(this.syncConfigMatch(), {
189
- projection: this.syncConfigProjection({ snapshot_lsn: 1 })
190
- });
191
- const syncConfig = this.selectedSyncConfig(doc);
192
- const checkpointLsn = syncConfig?.last_checkpoint_lsn ?? null;
193
- return {
194
- lastCheckpointLsn: checkpointLsn,
195
- resumeFromLsn: maxLsn(checkpointLsn, doc?.snapshot_lsn),
196
- keepaliveOp: syncConfig?.keepalive_op ?? null,
197
- syncConfigId: this.syncConfigId
198
- };
199
- }
200
-
201
253
  protected async terminateSyncRuleState(): Promise<void> {
202
254
  await this.db.sync_rules.updateOne(
203
255
  {
204
- _id: this.group_id
256
+ _id: this.replicationStreamId
205
257
  },
206
258
  {
207
259
  $set: {
@@ -213,52 +265,80 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
213
265
  );
214
266
  }
215
267
 
216
- protected async getStatusImpl(): Promise<storage.SyncRuleStatus> {
268
+ protected async getStatusImpl(): Promise<storage.ReplicationStreamStatus> {
217
269
  const doc = await this.syncRulesCollection.findOne(this.syncConfigMatch(), {
218
- projection: this.syncConfigProjection({ state: 1, snapshot_lsn: 1, keepalive_op: 1 })
270
+ projection: this.syncConfigProjection({ resume_lsn: 1 })
219
271
  });
220
- const syncConfig = this.selectedSyncConfig(doc);
221
- if (doc == null || syncConfig == null) {
272
+ const syncConfigs = this.selectedSyncConfigs(doc);
273
+ if (doc == null || syncConfigs.length == 0) {
222
274
  throw new ServiceAssertionError('Cannot find replication stream status');
223
275
  }
224
276
 
225
277
  return {
226
- snapshot_done: syncConfig.snapshot_done ?? false,
227
- snapshot_lsn: doc.snapshot_lsn ?? null,
228
- active: doc.state == storage.SyncRuleState.ACTIVE && syncConfig.state == storage.SyncRuleState.ACTIVE,
229
- checkpoint_lsn: syncConfig.last_checkpoint_lsn ?? null,
230
- keepalive_op: syncConfig.keepalive_op ?? null
278
+ snapshotDone:
279
+ syncConfigs.every((config) => config.snapshot_done ?? false) &&
280
+ syncConfigs.every((config) => config.last_checkpoint_lsn != null),
281
+ resumeLsn: doc.resume_lsn ?? null
231
282
  };
232
283
  }
233
284
 
234
285
  protected async clearSyncRuleState(): Promise<void> {
286
+ // Clearing resets the entire replication stream (bucket data and the op sequence), so reset
287
+ // the checkpoint state for _all_ embedded sync configs, not only the ones selected for this
288
+ // storage instance. This maintains the invariant that no config has a last_checkpoint past
289
+ // the stream-level last_persisted_op.
235
290
  await this.syncRulesCollection.updateOne(
236
- this.syncConfigMatch(),
291
+ { _id: this.replicationStreamId },
237
292
  {
238
293
  $set: {
239
294
  persisted_lsn: null,
240
- 'sync_configs.$[config].snapshot_done': false,
241
- 'sync_configs.$[config].last_checkpoint_lsn': null,
242
- 'sync_configs.$[config].last_checkpoint': null,
243
- 'sync_configs.$[config].no_checkpoint_before': null,
244
- 'sync_configs.$[config].keepalive_op': null
295
+ 'sync_configs.$[].snapshot_done': false,
296
+ 'sync_configs.$[].last_checkpoint_lsn': null,
297
+ 'sync_configs.$[].last_checkpoint': null,
298
+ 'sync_configs.$[].no_checkpoint_before': null
245
299
  },
246
300
  $unset: {
247
- snapshot_lsn: 1
301
+ resume_lsn: 1,
302
+ last_persisted_op: 1
248
303
  }
249
304
  },
250
305
  {
251
- maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS,
252
- arrayFilters: this.syncConfigArrayFilters()
306
+ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS
253
307
  }
254
308
  );
255
309
  }
256
310
 
257
- protected override get versionContext(): MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3> {
311
+ /**
312
+ * The persisted mapping of the single sync config that read operations are served from.
313
+ *
314
+ * Reads always operate on a single sync config: read-path storage instances are
315
+ * constructed for the active sync config only (see MongoBucketStorage.getActiveSyncConfig),
316
+ * and checkpoints are served from the single active config (see fetchCheckpointState).
317
+ * Within a single sync config, unique names are the persistence key of its rule mapping,
318
+ * so its name-keyed {@link SingleSyncConfigBucketDefinitionMapping} resolves sources from
319
+ * any parse of that config unambiguously - no parsed-set identity is required.
320
+ *
321
+ * Throws on storage instances with multiple sync configs (replication-side instances),
322
+ * which must not serve reads.
323
+ */
324
+ private singleSyncConfigMapping(): SingleSyncConfigBucketDefinitionMapping {
325
+ const content = this.replicationStream.syncConfigContent;
326
+ if (content.length != 1) {
327
+ throw new ServiceAssertionError(
328
+ `Read operations require a storage instance with a single sync config, got ${content.length}`
329
+ );
330
+ }
331
+ return content[0].mapping;
332
+ }
333
+
334
+ protected get versionContext(): MongoSyncBucketStorageContextV3 {
335
+ const self = this;
258
336
  return {
259
337
  db: this.db,
260
- group_id: this.group_id,
261
- mapping: this.mapping
338
+ replicationStreamId: this.replicationStreamId,
339
+ get mapping() {
340
+ return self.singleSyncConfigMapping();
341
+ }
262
342
  };
263
343
  }
264
344
 
@@ -279,26 +359,26 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
279
359
  }
280
360
 
281
361
  protected async clearBucketData(_signal?: AbortSignal): Promise<void> {
282
- for (const collection of await this.db.listBucketDataCollectionsV3(this.group_id)) {
362
+ for (const collection of await this.db.listBucketDataCollections(this.replicationStreamId)) {
283
363
  await collection.drop();
284
364
  }
285
365
  }
286
366
 
287
367
  protected async clearParameterIndexes(_signal?: AbortSignal): Promise<void> {
288
- for (const collection of await this.db.listParameterIndexCollectionsV3(this.group_id)) {
368
+ for (const collection of await this.db.listParameterIndexCollections(this.replicationStreamId)) {
289
369
  await collection.collection.drop();
290
370
  }
291
371
  }
292
372
 
293
373
  protected async clearSourceRecords(_signal?: AbortSignal): Promise<void> {
294
- for (const collection of await this.db.listSourceRecordCollectionsV3(this.group_id)) {
374
+ for (const collection of await this.db.listSourceRecordCollections(this.replicationStreamId)) {
295
375
  await collection.drop();
296
376
  }
297
377
  }
298
378
 
299
379
  protected async clearBucketState(_signal?: AbortSignal): Promise<void> {
300
380
  await this.db
301
- .bucketStateV3(this.group_id)
381
+ .bucketState(this.replicationStreamId)
302
382
  .drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
303
383
  .catch((error) => {
304
384
  if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceNotFound') {
@@ -310,7 +390,7 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
310
390
 
311
391
  protected async clearSourceTables(_signal?: AbortSignal): Promise<void> {
312
392
  await this.db
313
- .sourceTablesV3(this.group_id)
393
+ .sourceTables(this.replicationStreamId)
314
394
  .drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
315
395
  .catch((error) => {
316
396
  if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceNotFound') {
@@ -320,6 +400,19 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
320
400
  });
321
401
  }
322
402
 
403
+ async cleanupStoppedSyncConfigs(
404
+ options: storage.CleanupStoppedSyncConfigsOptions
405
+ ): Promise<storage.CleanupStoppedSyncConfigsResult> {
406
+ return new MongoStoppedSyncConfigCleanup({
407
+ db: this.db,
408
+ replicationStreamId: this.replicationStreamId,
409
+ signal: options.signal,
410
+ logger: options.logger ?? this.logger,
411
+ defaultSchema: options.defaultSchema,
412
+ sourceConnectionTag: options.sourceConnectionTag
413
+ }).run();
414
+ }
415
+
323
416
  protected getDataBucketChangesImpl(
324
417
  options: GetCheckpointChangesOptions
325
418
  ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
@@ -334,7 +427,7 @@ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
334
427
  }
335
428
 
336
429
  export async function getParameterSetsV3(
337
- ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
430
+ ctx: MongoSyncBucketStorageContextV3,
338
431
  checkpoint: MongoSyncBucketStorageCheckpoint,
339
432
  lookups: ScopedParameterLookup[],
340
433
  limit: number
@@ -350,8 +443,8 @@ export async function getParameterSetsV3(
350
443
  pipeline: mongo.Document[];
351
444
  } => {
352
445
  const indexId = lookup.indexId;
353
- const collection = ctx.db.parameterIndexV3(ctx.group_id, indexId);
354
- const lookupFilter = serializeParameterLookupV3(lookup);
446
+ const collection = ctx.db.parameterIndex(ctx.replicationStreamId, indexId);
447
+ const lookupFilter = serializeParameterLookup(lookup);
355
448
 
356
449
  return {
357
450
  collection,
@@ -434,7 +527,7 @@ export async function getParameterSetsV3(
434
527
  }
435
528
 
436
529
  export async function* getBucketDataBatchV3(
437
- ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
530
+ ctx: MongoSyncBucketStorageContextV3,
438
531
  checkpoint: utils.InternalOpId,
439
532
  dataBuckets: storage.BucketDataRequest[],
440
533
  options?: storage.BucketDataBatchOptions
@@ -461,61 +554,125 @@ export async function* getBucketDataBatchV3(
461
554
  }
462
555
 
463
556
  const definitionGroups = Array.from(requestsByDefinition.entries());
464
- for (let groupIndex = 0; groupIndex < definitionGroups.length && remainingLimit > 0; groupIndex++) {
465
- const [definitionId, requests] = definitionGroups[groupIndex];
557
+ for (const [groupIndex, [definitionId, requests]] of definitionGroups.entries()) {
558
+ if (remainingLimit <= 0) {
559
+ break;
560
+ }
466
561
  const hasLaterDefinitionGroups = groupIndex < definitionGroups.length - 1;
467
562
  const bucketMap = new Map(requests.map((request) => [request.bucket, request.start]));
468
- const filters: mongo.Filter<BucketDataDocumentV3>[] = Array.from(bucketMap.entries()).map(([bucket, start]) => ({
563
+ const filters = Array.from(bucketMap.entries()).map(([bucket, start]) => ({
469
564
  _id: {
470
- $gt: {
471
- b: bucket,
472
- o: start
473
- },
474
- $lte: {
475
- b: bucket,
476
- o: end as any
477
- }
478
- }
479
- }));
480
-
481
- const cursor = ctx.db.bucketDataV3(ctx.group_id, definitionId).find(
482
- {
483
- $or: filters
565
+ $gt: { b: bucket, o: start },
566
+ $lte: { b: bucket, o: new bson.MaxKey() }
484
567
  },
485
- {
486
- session: undefined,
487
- sort: { _id: 1 },
488
- limit: remainingLimit,
489
- batchSize: remainingLimit + 1,
490
- raw: true,
491
- maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
492
- }
493
- ) as unknown as mongo.FindCursor<Buffer>;
494
-
495
- let { data, hasMore: batchHasMore } = await readSingleBatch(cursor).catch((e) => {
568
+ min_op: { $lte: end }
569
+ // MongoDB Filter<T> doesn't accept compound _id ranges or dotted field paths in its type.
570
+ })) as unknown as mongo.Filter<BucketDataDocumentV3>[];
571
+
572
+ const collection = ctx.db.bucketData(ctx.replicationStreamId, definitionId);
573
+ // MongoDB Filter<T> doesn't accept the $or operator in its type.
574
+ const filter = { $or: filters } as unknown as mongo.Filter<BucketDataDocumentV3>;
575
+ const context = { replicationStreamId: ctx.replicationStreamId, definitionId };
576
+ const limit = remainingLimit;
577
+
578
+ const cursorOptions = { limit: remainingLimit, batchSize: remainingLimit + 1 };
579
+
580
+ // raw: true returns Buffers, but the driver typing doesn't reflect that
581
+ // without an explicit cast to FindCursor<Buffer>.
582
+ const cursor = collection.find(filter, {
583
+ session: undefined,
584
+ sort: { _id: 1 },
585
+ raw: true,
586
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS,
587
+ ...cursorOptions
588
+ }) as unknown as mongo.FindCursor<Buffer>;
589
+
590
+ let { data: rawData, hasMore } = await readSingleBatch(cursor).catch((e: unknown) => {
496
591
  throw lib_mongo.mapQueryError(e, 'while reading bucket data');
497
592
  });
498
- if (data.length == remainingLimit) {
499
- batchHasMore = true;
593
+
594
+ if (cursorOptions.limit != null && rawData.length >= cursorOptions.limit) {
595
+ hasMore = true;
500
596
  }
597
+
598
+ const data: BucketDataDoc[] = [];
599
+ const documentOpCounts: number[] = [];
600
+ const documentSizes: number[] = [];
601
+ let sharedRemainingLimit = limit;
602
+ let limitReached = false;
603
+ // Buckets whose matched document contributed no rows after filtering.
604
+ const completeEmptyBuckets = new Set<string>();
605
+
606
+ for (const raw of rawData) {
607
+ const doc = bson.deserialize(raw, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV3;
608
+ const {
609
+ rows,
610
+ remainingLimit,
611
+ limitReached: docLimitReached
612
+ } = extractRowsFromDocument(doc, context, bucketMap, end, sharedRemainingLimit);
613
+ if (rows.length == 0) {
614
+ // The document straddles the requested (start, end] window: it matched the
615
+ // query, but none of its ops are in range. Since its _id.o (max op) must be
616
+ // > end (any op <= end would have been > start, and thus in range), and
617
+ // document ranges per bucket are disjoint, no later document for this bucket
618
+ // can match either. The bucket is complete through the checkpoint.
619
+ completeEmptyBuckets.add(doc._id.b);
620
+ }
621
+ data.push(...rows);
622
+ documentOpCounts.push(rows.length);
623
+ documentSizes.push(raw.byteLength);
624
+ sharedRemainingLimit = remainingLimit;
625
+ if (docLimitReached) {
626
+ limitReached = true;
627
+ break;
628
+ }
629
+ }
630
+
631
+ const batchHasMore = hasMore || limitReached;
632
+
633
+ // Empty chunks are not forwarded to clients, but report progress to the caller:
634
+ // the bucket's position advances to the checkpoint, so it is not re-requested.
635
+ // If the batch produced no data at all, the last empty chunk also carries the
636
+ // has_more signal, so the caller re-requests the remaining buckets instead of
637
+ // treating an all-filtered batch as the end of the stream.
638
+ const emptyBuckets = Array.from(completeEmptyBuckets);
639
+ for (const [index, bucket] of emptyBuckets.entries()) {
640
+ const startOpId = bucketMap.get(bucket);
641
+ if (startOpId == null) {
642
+ throw new ServiceAssertionError(`data for unexpected bucket: ${bucket}`);
643
+ }
644
+ const isLastChunkOfBatch = data.length == 0 && index == emptyBuckets.length - 1;
645
+ yield {
646
+ chunkData: {
647
+ bucket,
648
+ after: internalToExternalOpId(startOpId),
649
+ has_more: isLastChunkOfBatch && batchHasMore,
650
+ data: [],
651
+ next_after: internalToExternalOpId(end)
652
+ },
653
+ targetOp: null
654
+ };
655
+ }
656
+
501
657
  if (data.length == 0) {
658
+ if (batchHasMore) {
659
+ // The remaining documents are read in the next round, after the caller has
660
+ // advanced the positions of the empty buckets above.
661
+ return;
662
+ }
502
663
  continue;
503
664
  }
504
665
 
505
666
  remainingLimit -= data.length;
506
667
 
507
- let chunkSizeBytes = 0;
668
+ let currentChunkSizeBytes = 0;
508
669
  let currentChunk: utils.SyncBucketData | null = null;
509
670
  let targetOp: InternalOpId | null = null;
510
671
 
511
- for (let rawData of data) {
512
- const row = loadBucketDataDocumentV3(
513
- { replicationStreamId: ctx.group_id, definitionId },
514
- bson.deserialize(rawData, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV3
515
- );
672
+ for (const { row, docIndex, isLastOpInDocument } of walkDocumentOps(data, documentOpCounts, documentSizes)) {
516
673
  const bucket = row.bucketKey.bucket;
517
674
 
518
- if (currentChunk == null || currentChunk.bucket != bucket || chunkSizeBytes >= chunkSizeLimitBytes) {
675
+ if (currentChunk == null || currentChunk.bucket != bucket || currentChunkSizeBytes >= chunkSizeLimitBytes) {
519
676
  let start: ProtocolOpId | undefined = undefined;
520
677
  if (currentChunk != null) {
521
678
  if (currentChunk.bucket == bucket) {
@@ -525,8 +682,8 @@ export async function* getBucketDataBatchV3(
525
682
 
526
683
  const yieldChunk = currentChunk;
527
684
  currentChunk = null;
528
- chunkSizeBytes = 0;
529
- yield { chunkData: yieldChunk, targetOp: targetOp };
685
+ currentChunkSizeBytes = 0;
686
+ yield { chunkData: yieldChunk, targetOp };
530
687
  targetOp = null;
531
688
  }
532
689
 
@@ -553,13 +710,16 @@ export async function* getBucketDataBatchV3(
553
710
 
554
711
  currentChunk.data.push(entry);
555
712
  currentChunk.next_after = entry.op_id;
556
- chunkSizeBytes += rawData.byteLength;
713
+
714
+ if (isLastOpInDocument) {
715
+ currentChunkSizeBytes += documentSizes[docIndex];
716
+ }
557
717
  }
558
718
 
559
719
  if (currentChunk != null) {
560
720
  const yieldChunk = currentChunk;
561
721
  yieldChunk.has_more = batchHasMore || (remainingLimit <= 0 && hasLaterDefinitionGroups);
562
- yield { chunkData: yieldChunk, targetOp: targetOp };
722
+ yield { chunkData: yieldChunk, targetOp };
563
723
  }
564
724
 
565
725
  if (batchHasMore || remainingLimit <= 0) {
@@ -569,12 +729,12 @@ export async function* getBucketDataBatchV3(
569
729
  }
570
730
 
571
731
  export async function getDataBucketChangesV3(
572
- ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
732
+ ctx: MongoSyncBucketStorageContextV3,
573
733
  options: GetCheckpointChangesOptions
574
734
  ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
575
735
  const limit = 1000;
576
736
  const bucketStateUpdates = await ctx.db
577
- .bucketStateV3(ctx.group_id)
737
+ .bucketState(ctx.replicationStreamId)
578
738
  .aggregate<{ _id: string; last_op: bigint }>(
579
739
  [
580
740
  {
@@ -611,14 +771,14 @@ export async function getDataBucketChangesV3(
611
771
  }
612
772
 
613
773
  export async function getParameterBucketChangesV3(
614
- ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
774
+ ctx: MongoSyncBucketStorageContextV3,
615
775
  options: GetCheckpointChangesOptions
616
776
  ): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
617
777
  const limit = 1000;
618
778
  const indexIds = ctx.mapping.allParameterIndexIds();
619
779
  const collections = indexIds.map((indexId) => ({
620
780
  indexId,
621
- collection: ctx.db.parameterIndexV3(ctx.group_id, indexId)
781
+ collection: ctx.db.parameterIndex(ctx.replicationStreamId, indexId)
622
782
  }));
623
783
  if (collections.length == 0) {
624
784
  return {
@@ -671,8 +831,6 @@ export async function getParameterBucketChangesV3(
671
831
  invalidateParameterBuckets: invalidateParameterUpdates,
672
832
  updatedParameterLookups: invalidateParameterUpdates
673
833
  ? new Set<string>()
674
- : new Set<string>(
675
- parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookupV3(p.lookup, p.indexId)))
676
- )
834
+ : new Set<string>(parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookup(p.lookup, p.indexId))))
677
835
  };
678
836
  }