@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
@@ -1,5 +1,5 @@
1
1
  import { ReplicationAssertionError } from '@powersync/lib-services-framework';
2
- import { ColumnDescriptor, SourceTable, storage } from '@powersync/service-core';
2
+ import { ColumnDescriptor, InternalOpId, SourceTable, storage } from '@powersync/service-core';
3
3
  import * as bson from 'bson';
4
4
  import { mongoTableId } from '../../../utils/util.js';
5
5
  import { calculateCheckpointState } from '../CheckpointState.js';
@@ -9,22 +9,35 @@ import { SourceRecordStore } from '../common/SourceRecordStore.js';
9
9
  import { PersistedBatchV1 } from './PersistedBatchV1.js';
10
10
  import { SourceRecordStoreV1 } from './SourceRecordStoreV1.js';
11
11
  import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
12
- import { SourceTableDocumentV1, SyncRuleDocumentV1 } from './models.js';
12
+ import { SyncRuleDocumentV1 } from './models.js';
13
13
 
14
14
  export class MongoBucketBatchV1 extends MongoBucketBatch {
15
- declare public readonly db: VersionedPowerSyncMongoV1;
15
+ declare readonly db: VersionedPowerSyncMongoV1;
16
16
 
17
17
  private readonly store: SourceRecordStore;
18
18
  private needsActivation = true;
19
19
  private lastWaitingLogThrottled = 0;
20
20
 
21
+ /**
22
+ * The last persisted op that is not yet covered by a checkpoint, if any.
23
+ *
24
+ * Seeded from the persisted keepalive_op, so that ops persisted before a restart can still be
25
+ * included in the next checkpoint.
26
+ */
27
+ private persisted_op: InternalOpId | null = null;
28
+
21
29
  constructor(options: MongoBucketBatchOptions) {
22
30
  super(options);
23
- this.store = new SourceRecordStoreV1(this.db, this.group_id);
31
+ this.persisted_op = options.keepaliveOp ?? null;
32
+ this.store = new SourceRecordStoreV1(this.db, this.replicationStreamId);
33
+ }
34
+
35
+ protected override recordPersistedOp(lastOp: InternalOpId): void {
36
+ this.persisted_op = lastOp;
24
37
  }
25
38
 
26
39
  protected createPersistedBatch(writtenSize: number): PersistedBatch {
27
- return new PersistedBatchV1(this.db, this.group_id, this.mapping, writtenSize, {
40
+ return new PersistedBatchV1(this.db, this.replicationStreamId, this.mapping, writtenSize, {
28
41
  logger: this.logger
29
42
  });
30
43
  }
@@ -32,13 +45,12 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
32
45
  protected get sourceRecordStore(): SourceRecordStore {
33
46
  return this.store;
34
47
  }
35
-
36
48
  protected async cleanupDroppedSourceTables(_tables: SourceTable[]) {
37
49
  // No-op for V1: source records live in a shared collection.
38
50
  }
39
51
 
40
52
  async resolveTables(options: storage.ResolveTablesOptions): Promise<storage.ResolveTablesResult> {
41
- const syncRules = options.syncRules ?? this.sync_rules;
53
+ const syncRules = options.parsedSyncConfig?.hydratedSyncConfig ?? this.sync_rules;
42
54
  const { connection_id, source } = options;
43
55
  const { schema, name, objectId, replicaIdColumns, connectionTag, sendsCompleteRows } = source;
44
56
 
@@ -50,9 +62,9 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
50
62
 
51
63
  let result: storage.ResolveTablesResult | null = null;
52
64
  await this.db.client.withSession(async (session) => {
53
- const col = this.db.commonSourceTables(this.group_id);
65
+ const col = this.db.sourceTablesV1(this.replicationStreamId);
54
66
  const filter: any = {
55
- group_id: this.group_id,
67
+ group_id: this.replicationStreamId,
56
68
  connection_id,
57
69
  schema_name: schema,
58
70
  table_name: name,
@@ -66,7 +78,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
66
78
  if (doc == null) {
67
79
  doc = {
68
80
  _id: options.idGenerator ? (options.idGenerator() as bson.ObjectId) : new bson.ObjectId(),
69
- group_id: this.group_id,
81
+ group_id: this.replicationStreamId,
70
82
  connection_id,
71
83
  relation_id: objectId,
72
84
  schema_name: schema,
@@ -107,7 +119,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
107
119
  const truncate = await col
108
120
  .find(
109
121
  {
110
- group_id: this.group_id,
122
+ group_id: this.replicationStreamId,
111
123
  connection_id,
112
124
  _id: { $ne: doc._id },
113
125
  $or: truncateFilter
@@ -145,13 +157,13 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
145
157
  }
146
158
 
147
159
  async getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null> {
148
- const doc = (await this.db.commonSourceTables(this.group_id).findOne(
160
+ const doc = await this.db.sourceTablesV1(this.replicationStreamId).findOne(
149
161
  {
150
- group_id: this.group_id,
162
+ group_id: this.replicationStreamId,
151
163
  _id: mongoTableId(table.id)
152
164
  },
153
165
  { session: this.session }
154
- )) as SourceTableDocumentV1 | null;
166
+ );
155
167
  if (doc == null) {
156
168
  return null;
157
169
  }
@@ -243,7 +255,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
243
255
  };
244
256
 
245
257
  const preUpdateDocument = (await this.db.sync_rules.findOneAndUpdate(
246
- { _id: this.group_id },
258
+ { _id: this.replicationStreamId },
247
259
  [
248
260
  {
249
261
  $set: {
@@ -296,7 +308,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
296
308
 
297
309
  if (preUpdateDocument == null) {
298
310
  throw new ReplicationAssertionError(
299
- 'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.group_id
311
+ 'Failed to update checkpoint - no matching sync_rules document for _id: ' + this.replicationStreamId
300
312
  );
301
313
  }
302
314
 
@@ -330,7 +342,6 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
330
342
  await this.autoActivate(lsn);
331
343
  await this.db.notifyCheckpoint();
332
344
  this.persisted_op = null;
333
- this.last_checkpoint_lsn = lsn;
334
345
  if (checkpointState.newLastCheckpoint != null) {
335
346
  await this.sourceRecordStore.postCommitCleanup(checkpointState.newLastCheckpoint, this.logger);
336
347
  }
@@ -349,7 +360,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
349
360
  async setResumeLsn(lsn: string): Promise<void> {
350
361
  await this.db.sync_rules.updateOne(
351
362
  {
352
- _id: this.group_id
363
+ _id: this.replicationStreamId
353
364
  },
354
365
  {
355
366
  $set: {
@@ -363,7 +374,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
363
374
  async markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void> {
364
375
  await this.db.sync_rules.updateOne(
365
376
  {
366
- _id: this.group_id
377
+ _id: this.replicationStreamId
367
378
  },
368
379
  {
369
380
  $set: {
@@ -381,17 +392,24 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
381
392
  async markSnapshotDone(no_checkpoint_before_lsn: string, options?: { throwOnConflict?: boolean }): Promise<void> {
382
393
  await this.withTransaction(async () => {
383
394
  // Protect against race conditions
384
- const count = await this.db.commonSourceTables(this.group_id).countDocuments(
385
- {
386
- group_id: this.group_id,
387
- snapshot_done: false
388
- },
389
- { session: this.session }
390
- );
391
- if (count > 0) {
395
+ const blockingTables = await this.db
396
+ .sourceTablesV1(this.replicationStreamId)
397
+ .find(
398
+ {
399
+ group_id: this.replicationStreamId,
400
+ snapshot_done: false
401
+ },
402
+ {
403
+ session: this.session,
404
+ projection: { schema_name: 1, table_name: 1 }
405
+ }
406
+ )
407
+ .toArray();
408
+
409
+ if (blockingTables.length > 0) {
392
410
  if (options?.throwOnConflict ?? true) {
393
411
  throw new ReplicationAssertionError(
394
- `Cannot mark snapshot done while ${count} source table${count == 1 ? '' : 's'} still require snapshotting`
412
+ `Cannot mark snapshot done while source tables still require snapshotting. Tables: ${blockingTables.map((t) => `${t.schema_name}.${t.table_name}`).join(', ')}`
395
413
  );
396
414
  } else {
397
415
  return;
@@ -405,7 +423,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
405
423
  async markTableSnapshotRequired(_table: storage.SourceTable): Promise<void> {
406
424
  await this.db.sync_rules.updateOne(
407
425
  {
408
- _id: this.group_id
426
+ _id: this.replicationStreamId
409
427
  },
410
428
  {
411
429
  $set: {
@@ -424,7 +442,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
424
442
  const ids = tables.map((table) => mongoTableId(table.id));
425
443
 
426
444
  await this.withTransaction(async () => {
427
- await this.db.commonSourceTables(this.group_id).updateMany(
445
+ await this.db.sourceTablesV1(this.replicationStreamId).updateMany(
428
446
  { _id: { $in: ids } },
429
447
  {
430
448
  $set: {
@@ -440,7 +458,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
440
458
  if (no_checkpoint_before_lsn != null) {
441
459
  await this.db.sync_rules.updateOne(
442
460
  {
443
- _id: this.group_id
461
+ _id: this.replicationStreamId
444
462
  },
445
463
  {
446
464
  $set: {
@@ -469,11 +487,14 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
469
487
  const session = this.session;
470
488
  let activated = false;
471
489
  await session.withTransaction(async () => {
472
- const doc = (await this.db.sync_rules.findOne({ _id: this.group_id }, { session })) as SyncRuleDocumentV1;
490
+ const doc = (await this.db.sync_rules.findOne(
491
+ { _id: this.replicationStreamId },
492
+ { session }
493
+ )) as SyncRuleDocumentV1;
473
494
  if (doc && doc.state == storage.SyncRuleState.PROCESSING && doc.snapshot_done && doc.last_checkpoint != null) {
474
495
  await this.db.sync_rules.updateOne(
475
496
  {
476
- _id: this.group_id
497
+ _id: this.replicationStreamId
477
498
  },
478
499
  {
479
500
  $set: {
@@ -485,7 +506,7 @@ export class MongoBucketBatchV1 extends MongoBucketBatch {
485
506
 
486
507
  await this.db.sync_rules.updateMany(
487
508
  {
488
- _id: { $ne: this.group_id },
509
+ _id: { $ne: this.replicationStreamId },
489
510
  state: { $in: [storage.SyncRuleState.ACTIVE, storage.SyncRuleState.ERRORED] }
490
511
  },
491
512
  {
@@ -1,11 +1,20 @@
1
+ import * as lib_mongo from '@powersync/lib-service-mongodb';
1
2
  import {
3
+ addPartialChecksums,
2
4
  bson,
3
5
  BucketChecksum,
4
6
  FetchPartialBucketChecksum,
5
7
  InternalOpId,
6
- PartialChecksumMap
8
+ isPartialChecksum,
9
+ PartialChecksumMap,
10
+ PartialOrFullChecksum
7
11
  } from '@powersync/service-core';
8
- import { FetchPartialBucketChecksumByBucket, MongoChecksums } from '../MongoChecksums.js';
12
+ import {
13
+ checksumFromAggregate,
14
+ DEFAULT_OPERATION_BATCH_LIMIT,
15
+ FetchPartialBucketChecksumByBucket,
16
+ MongoChecksums
17
+ } from '../MongoChecksums.js';
9
18
  import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
10
19
 
11
20
  export class MongoChecksumsV1 extends MongoChecksums {
@@ -14,7 +23,8 @@ export class MongoChecksumsV1 extends MongoChecksums {
14
23
  async computePartialChecksumsDirectByBucket(
15
24
  batch: FetchPartialBucketChecksumByBucket[]
16
25
  ): Promise<PartialChecksumMap> {
17
- return this.computePartialChecksumsForCollection(batch, this.db.bucketDataV1, (request) => ({
26
+ const collection = this.db.bucketDataV1;
27
+ const createFilter = (request: FetchPartialBucketChecksumByBucket) => ({
18
28
  _id: {
19
29
  $gt: {
20
30
  g: this.group_id,
@@ -27,7 +37,130 @@ export class MongoChecksumsV1 extends MongoChecksums {
27
37
  o: request.end
28
38
  }
29
39
  }
30
- }));
40
+ });
41
+
42
+ const batchLimit = this.options?.operationBatchLimit ?? DEFAULT_OPERATION_BATCH_LIMIT;
43
+
44
+ // Map requests by bucket. We adjust this as we get partial results.
45
+ let requests = new Map<string, FetchPartialBucketChecksumByBucket>();
46
+ for (let request of batch) {
47
+ requests.set(request.bucket, request);
48
+ }
49
+
50
+ const partialChecksums = new Map<string, PartialOrFullChecksum>();
51
+
52
+ while (requests.size > 0) {
53
+ const filters = Array.from(requests.values(), createFilter);
54
+
55
+ // Historically, checksum may be stored as 'int' or 'double'.
56
+ // More recently, this should be a 'long'.
57
+ // $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
58
+ const checksumLong = this.storageConfig.longChecksums ? '$checksum' : { $toLong: '$checksum' };
59
+
60
+ // Aggregate over a max of `batchLimit` operations at a time.
61
+ // Let's say we have 3 buckets (A, B, C), each with 10 operations, and our batch limit is 12.
62
+ // Then we'll do three batches:
63
+ // 1. Query: A[1-end], B[1-end], C[1-end]
64
+ // Returns: A[1-10], B[1-2]
65
+ // 2. Query: B[3-end], C[1-end]
66
+ // Returns: B[3-10], C[1-4]
67
+ // 3. Query: C[5-end]
68
+ // Returns: C[5-10]
69
+ const aggregate = await collection
70
+ .aggregate(
71
+ [
72
+ {
73
+ $match: {
74
+ $or: filters
75
+ }
76
+ },
77
+ // sort and limit _before_ grouping
78
+ { $sort: { _id: 1 } },
79
+ { $limit: batchLimit },
80
+ {
81
+ $group: {
82
+ _id: '$_id.b',
83
+ checksum_total: { $sum: checksumLong },
84
+ count: { $sum: 1 },
85
+ has_clear_op: {
86
+ $max: {
87
+ $cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
88
+ }
89
+ },
90
+ last_op: { $max: '$_id.o' }
91
+ }
92
+ },
93
+ // Sort the aggregated results (100 max, so should be fast).
94
+ // This is important to identify which buckets we have partial data for.
95
+ { $sort: { _id: 1 } }
96
+ ],
97
+ {
98
+ session: undefined,
99
+ readConcern: 'snapshot',
100
+ maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS
101
+ }
102
+ )
103
+ .toArray()
104
+ .catch((e) => {
105
+ throw lib_mongo.mapQueryError(e, 'while reading checksums');
106
+ });
107
+
108
+ let batchCount = 0;
109
+ let limitReached = false;
110
+ for (let doc of aggregate) {
111
+ const bucket = doc._id;
112
+ const checksum = checksumFromAggregate(doc);
113
+
114
+ const existing = partialChecksums.get(bucket);
115
+ if (existing != null) {
116
+ partialChecksums.set(bucket, addPartialChecksums(bucket, existing, checksum));
117
+ } else {
118
+ partialChecksums.set(bucket, checksum);
119
+ }
120
+
121
+ batchCount += doc.count;
122
+ if (batchCount == batchLimit) {
123
+ // Limit reached. Request more in the next batch.
124
+ // Note that this only affects the _last_ bucket in a batch.
125
+ limitReached = true;
126
+ const req = requests.get(bucket);
127
+ requests.set(bucket, {
128
+ ...req!,
129
+ start: doc.last_op
130
+ });
131
+ } else {
132
+ // All done for this bucket
133
+ requests.delete(bucket);
134
+ }
135
+ }
136
+ if (!limitReached) {
137
+ break;
138
+ }
139
+ }
140
+
141
+ return new Map<string, PartialOrFullChecksum>(
142
+ batch.map((request) => {
143
+ const bucket = request.bucket;
144
+ let partialChecksum = partialChecksums.get(bucket);
145
+ // Could be null if we got no data
146
+ if (partialChecksum == null) {
147
+ partialChecksum = {
148
+ bucket,
149
+ partialCount: 0,
150
+ partialChecksum: 0
151
+ };
152
+ }
153
+ if (request.start == null && isPartialChecksum(partialChecksum)) {
154
+ partialChecksum = {
155
+ bucket,
156
+ count: partialChecksum.partialCount,
157
+ checksum: partialChecksum.partialChecksum
158
+ };
159
+ }
160
+
161
+ return [bucket, partialChecksum];
162
+ })
163
+ );
31
164
  }
32
165
 
33
166
  protected async fetchPreStates(