@powersync/service-core-tests 0.15.0 → 0.15.2

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 (28) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/test-utils/general-utils.d.ts +13 -1
  3. package/dist/test-utils/general-utils.js +30 -1
  4. package/dist/test-utils/general-utils.js.map +1 -1
  5. package/dist/test-utils/stream_utils.js +2 -2
  6. package/dist/test-utils/stream_utils.js.map +1 -1
  7. package/dist/tests/register-compacting-tests.js +266 -257
  8. package/dist/tests/register-compacting-tests.js.map +1 -1
  9. package/dist/tests/register-data-storage-checkpoint-tests.js +36 -57
  10. package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
  11. package/dist/tests/register-data-storage-data-tests.js +839 -863
  12. package/dist/tests/register-data-storage-data-tests.js.map +1 -1
  13. package/dist/tests/register-data-storage-parameter-tests.js +228 -236
  14. package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
  15. package/dist/tests/register-parameter-compacting-tests.js +81 -89
  16. package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
  17. package/dist/tests/register-sync-tests.js +468 -462
  18. package/dist/tests/register-sync-tests.js.map +1 -1
  19. package/package.json +3 -3
  20. package/src/test-utils/general-utils.ts +41 -2
  21. package/src/test-utils/stream_utils.ts +2 -2
  22. package/src/tests/register-compacting-tests.ts +279 -270
  23. package/src/tests/register-data-storage-checkpoint-tests.ts +36 -57
  24. package/src/tests/register-data-storage-data-tests.ts +673 -770
  25. package/src/tests/register-data-storage-parameter-tests.ts +245 -257
  26. package/src/tests/register-parameter-compacting-tests.ts +84 -92
  27. package/src/tests/register-sync-tests.ts +375 -391
  28. package/tsconfig.tsbuildinfo +1 -1
@@ -53,7 +53,6 @@ export function registerSyncTests(
53
53
  maxDataFetchConcurrency: 2
54
54
  });
55
55
 
56
- const TEST_TABLE = test_utils.makeTestTable('test', ['id'], config);
57
56
  const updateSyncRules = (bucketStorageFactory: storage.BucketStorageFactory, updateOptions: { content: string }) => {
58
57
  return bucketStorageFactory.updateSyncRules(
59
58
  updateSyncRulesFromYaml(updateOptions.content, {
@@ -71,33 +70,33 @@ export function registerSyncTests(
71
70
  });
72
71
 
73
72
  const bucketStorage = f.getInstance(syncRules);
73
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
74
+ const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
74
75
 
75
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
76
- await batch.markAllSnapshotDone('0/1');
76
+ await writer.markAllSnapshotDone('0/1');
77
77
 
78
- await batch.save({
79
- sourceTable: TEST_TABLE,
80
- tag: storage.SaveOperationTag.INSERT,
81
- after: {
82
- id: 't1',
83
- description: 'Test 1'
84
- },
85
- afterReplicaId: 't1'
86
- });
87
-
88
- await batch.save({
89
- sourceTable: TEST_TABLE,
90
- tag: storage.SaveOperationTag.INSERT,
91
- after: {
92
- id: 't2',
93
- description: 'Test 2'
94
- },
95
- afterReplicaId: 't2'
96
- });
78
+ await writer.save({
79
+ sourceTable,
80
+ tag: storage.SaveOperationTag.INSERT,
81
+ after: {
82
+ id: 't1',
83
+ description: 'Test 1'
84
+ },
85
+ afterReplicaId: 't1'
86
+ });
97
87
 
98
- await batch.commit('0/1');
88
+ await writer.save({
89
+ sourceTable,
90
+ tag: storage.SaveOperationTag.INSERT,
91
+ after: {
92
+ id: 't2',
93
+ description: 'Test 2'
94
+ },
95
+ afterReplicaId: 't2'
99
96
  });
100
97
 
98
+ await writer.commit('0/1');
99
+
101
100
  const stream = sync.streamResponse({
102
101
  syncContext,
103
102
  bucketStorage: bucketStorage,
@@ -134,32 +133,32 @@ bucket_definitions:
134
133
  });
135
134
 
136
135
  const bucketStorage = f.getInstance(syncRules);
136
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
137
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
138
+
139
+ await writer.markAllSnapshotDone('0/1');
140
+ await writer.save({
141
+ sourceTable: testTable,
142
+ tag: storage.SaveOperationTag.INSERT,
143
+ after: {
144
+ id: 't1',
145
+ description: 'Test 1'
146
+ },
147
+ afterReplicaId: 't1'
148
+ });
137
149
 
138
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
139
- await batch.markAllSnapshotDone('0/1');
140
- await batch.save({
141
- sourceTable: TEST_TABLE,
142
- tag: storage.SaveOperationTag.INSERT,
143
- after: {
144
- id: 't1',
145
- description: 'Test 1'
146
- },
147
- afterReplicaId: 't1'
148
- });
149
-
150
- await batch.save({
151
- sourceTable: TEST_TABLE,
152
- tag: storage.SaveOperationTag.INSERT,
153
- after: {
154
- id: 'earlier',
155
- description: 'Test 2'
156
- },
157
- afterReplicaId: 'earlier'
158
- });
159
-
160
- await batch.commit('0/1');
150
+ await writer.save({
151
+ sourceTable: testTable,
152
+ tag: storage.SaveOperationTag.INSERT,
153
+ after: {
154
+ id: 'earlier',
155
+ description: 'Test 2'
156
+ },
157
+ afterReplicaId: 'earlier'
161
158
  });
162
159
 
160
+ await writer.commit('0/1');
161
+
163
162
  const stream = sync.streamResponse({
164
163
  syncContext,
165
164
  bucketStorage,
@@ -196,33 +195,33 @@ bucket_definitions:
196
195
  });
197
196
 
198
197
  const bucketStorage = f.getInstance(syncRules);
199
-
200
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
201
- await batch.markAllSnapshotDone('0/1');
202
- // Initial data: Add one priority row and 10k low-priority rows.
203
- await batch.save({
204
- sourceTable: TEST_TABLE,
198
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
199
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
200
+
201
+ await writer.markAllSnapshotDone('0/1');
202
+ // Initial data: Add one priority row and 10k low-priority rows.
203
+ await writer.save({
204
+ sourceTable: testTable,
205
+ tag: storage.SaveOperationTag.INSERT,
206
+ after: {
207
+ id: 'highprio',
208
+ description: 'High priority row'
209
+ },
210
+ afterReplicaId: 'highprio'
211
+ });
212
+ for (let i = 0; i < 10_000; i++) {
213
+ await writer.save({
214
+ sourceTable: testTable,
205
215
  tag: storage.SaveOperationTag.INSERT,
206
216
  after: {
207
- id: 'highprio',
208
- description: 'High priority row'
217
+ id: `${i}`,
218
+ description: 'low prio'
209
219
  },
210
- afterReplicaId: 'highprio'
220
+ afterReplicaId: `${i}`
211
221
  });
212
- for (let i = 0; i < 10_000; i++) {
213
- await batch.save({
214
- sourceTable: TEST_TABLE,
215
- tag: storage.SaveOperationTag.INSERT,
216
- after: {
217
- id: `${i}`,
218
- description: 'low prio'
219
- },
220
- afterReplicaId: `${i}`
221
- });
222
- }
222
+ }
223
223
 
224
- await batch.commit('0/1');
225
- });
224
+ await writer.commit('0/1');
226
225
 
227
226
  const stream = sync.streamResponse({
228
227
  syncContext,
@@ -250,20 +249,18 @@ bucket_definitions:
250
249
  if (sentCheckpoints == 1) {
251
250
  // Save new data to interrupt the low-priority sync.
252
251
 
253
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
254
- // Add another high-priority row. This should interrupt the long-running low-priority sync.
255
- await batch.save({
256
- sourceTable: TEST_TABLE,
257
- tag: storage.SaveOperationTag.INSERT,
258
- after: {
259
- id: 'highprio2',
260
- description: 'Another high-priority row'
261
- },
262
- afterReplicaId: 'highprio2'
263
- });
264
-
265
- await batch.commit('0/2');
252
+ // Add another high-priority row. This should interrupt the long-running low-priority sync.
253
+ await writer.save({
254
+ sourceTable: testTable,
255
+ tag: storage.SaveOperationTag.INSERT,
256
+ after: {
257
+ id: 'highprio2',
258
+ description: 'Another high-priority row'
259
+ },
260
+ afterReplicaId: 'highprio2'
266
261
  });
262
+
263
+ await writer.commit('0/2');
267
264
  } else {
268
265
  // Low-priority sync from the first checkpoint was interrupted. This should not happen before
269
266
  // 1000 low-priority items were synchronized.
@@ -307,33 +304,33 @@ bucket_definitions:
307
304
  });
308
305
 
309
306
  const bucketStorage = f.getInstance(syncRules);
310
-
311
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
312
- await batch.markAllSnapshotDone('0/1');
313
- // Initial data: Add one priority row and 10k low-priority rows.
314
- await batch.save({
315
- sourceTable: TEST_TABLE,
307
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
308
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
309
+
310
+ await writer.markAllSnapshotDone('0/1');
311
+ // Initial data: Add one priority row and 10k low-priority rows.
312
+ await writer.save({
313
+ sourceTable: testTable,
314
+ tag: storage.SaveOperationTag.INSERT,
315
+ after: {
316
+ id: 'highprio',
317
+ description: 'user_one'
318
+ },
319
+ afterReplicaId: 'highprio'
320
+ });
321
+ for (let i = 0; i < 10_000; i++) {
322
+ await writer.save({
323
+ sourceTable: testTable,
316
324
  tag: storage.SaveOperationTag.INSERT,
317
325
  after: {
318
- id: 'highprio',
319
- description: 'user_one'
326
+ id: `${i}`,
327
+ description: 'low prio'
320
328
  },
321
- afterReplicaId: 'highprio'
329
+ afterReplicaId: `${i}`
322
330
  });
323
- for (let i = 0; i < 10_000; i++) {
324
- await batch.save({
325
- sourceTable: TEST_TABLE,
326
- tag: storage.SaveOperationTag.INSERT,
327
- after: {
328
- id: `${i}`,
329
- description: 'low prio'
330
- },
331
- afterReplicaId: `${i}`
332
- });
333
- }
331
+ }
334
332
 
335
- await batch.commit('0/1');
336
- });
333
+ await writer.commit('0/1');
337
334
 
338
335
  const stream = sync.streamResponse({
339
336
  syncContext,
@@ -366,20 +363,18 @@ bucket_definitions:
366
363
  if (typeof next === 'object' && next !== null) {
367
364
  if ('partial_checkpoint_complete' in next) {
368
365
  if (sentCheckpoints == 1) {
369
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
370
- // Add a high-priority row that doesn't affect this sync stream.
371
- await batch.save({
372
- sourceTable: TEST_TABLE,
373
- tag: storage.SaveOperationTag.INSERT,
374
- after: {
375
- id: 'highprio2',
376
- description: 'user_two'
377
- },
378
- afterReplicaId: 'highprio2'
379
- });
380
-
381
- await batch.commit('0/2');
366
+ // Add a high-priority row that doesn't affect this sync stream.
367
+ await writer.save({
368
+ sourceTable: testTable,
369
+ tag: storage.SaveOperationTag.INSERT,
370
+ after: {
371
+ id: 'highprio2',
372
+ description: 'user_two'
373
+ },
374
+ afterReplicaId: 'highprio2'
382
375
  });
376
+
377
+ await writer.commit('0/2');
383
378
  } else {
384
379
  expect(sentCheckpoints).toBe(2);
385
380
  expect(sentRows).toBe(10002);
@@ -400,20 +395,18 @@ bucket_definitions:
400
395
  if (completedCheckpoints == 1) {
401
396
  expect(sentRows).toBe(10001);
402
397
 
403
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
404
- // Add a high-priority row that affects this sync stream.
405
- await batch.save({
406
- sourceTable: TEST_TABLE,
407
- tag: storage.SaveOperationTag.INSERT,
408
- after: {
409
- id: 'highprio3',
410
- description: 'user_one'
411
- },
412
- afterReplicaId: 'highprio3'
413
- });
414
-
415
- await batch.commit('0/3');
398
+ // Add a high-priority row that affects this sync stream.
399
+ await writer.save({
400
+ sourceTable: testTable,
401
+ tag: storage.SaveOperationTag.INSERT,
402
+ after: {
403
+ id: 'highprio3',
404
+ description: 'user_one'
405
+ },
406
+ afterReplicaId: 'highprio3'
416
407
  });
408
+
409
+ await writer.commit('0/3');
417
410
  }
418
411
  }
419
412
  }
@@ -449,33 +442,33 @@ bucket_definitions:
449
442
  });
450
443
 
451
444
  const bucketStorage = f.getInstance(syncRules);
452
-
453
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
454
- await batch.markAllSnapshotDone('0/1');
455
- // Initial data: Add one priority row and 10k low-priority rows.
456
- await batch.save({
457
- sourceTable: TEST_TABLE,
445
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
446
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
447
+
448
+ await writer.markAllSnapshotDone('0/1');
449
+ // Initial data: Add one priority row and 10k low-priority rows.
450
+ await writer.save({
451
+ sourceTable: testTable,
452
+ tag: storage.SaveOperationTag.INSERT,
453
+ after: {
454
+ id: 'highprio',
455
+ description: 'High priority row'
456
+ },
457
+ afterReplicaId: 'highprio'
458
+ });
459
+ for (let i = 0; i < 2_000; i++) {
460
+ await writer.save({
461
+ sourceTable: testTable,
458
462
  tag: storage.SaveOperationTag.INSERT,
459
463
  after: {
460
- id: 'highprio',
461
- description: 'High priority row'
464
+ id: `${i}`,
465
+ description: 'low prio'
462
466
  },
463
- afterReplicaId: 'highprio'
467
+ afterReplicaId: `${i}`
464
468
  });
465
- for (let i = 0; i < 2_000; i++) {
466
- await batch.save({
467
- sourceTable: TEST_TABLE,
468
- tag: storage.SaveOperationTag.INSERT,
469
- after: {
470
- id: `${i}`,
471
- description: 'low prio'
472
- },
473
- afterReplicaId: `${i}`
474
- });
475
- }
469
+ }
476
470
 
477
- await batch.commit('0/1');
478
- });
471
+ await writer.commit('0/1');
479
472
 
480
473
  const stream = sync.streamResponse({
481
474
  syncContext,
@@ -512,31 +505,29 @@ bucket_definitions:
512
505
 
513
506
  if (sentRows == 1001) {
514
507
  // Save new data to interrupt the low-priority sync.
515
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
516
- // Add another high-priority row. This should interrupt the long-running low-priority sync.
517
- await batch.save({
518
- sourceTable: TEST_TABLE,
519
- tag: storage.SaveOperationTag.INSERT,
520
- after: {
521
- id: 'highprio2',
522
- description: 'Another high-priority row'
523
- },
524
- afterReplicaId: 'highprio2'
525
- });
526
-
527
- // Also add a low-priority row
528
- await batch.save({
529
- sourceTable: TEST_TABLE,
530
- tag: storage.SaveOperationTag.INSERT,
531
- after: {
532
- id: '2001',
533
- description: 'Another low-priority row'
534
- },
535
- afterReplicaId: '2001'
536
- });
537
-
538
- await batch.commit('0/2');
508
+ // Add another high-priority row. This should interrupt the long-running low-priority sync.
509
+ await writer.save({
510
+ sourceTable: testTable,
511
+ tag: storage.SaveOperationTag.INSERT,
512
+ after: {
513
+ id: 'highprio2',
514
+ description: 'Another high-priority row'
515
+ },
516
+ afterReplicaId: 'highprio2'
517
+ });
518
+
519
+ // Also add a low-priority row
520
+ await writer.save({
521
+ sourceTable: testTable,
522
+ tag: storage.SaveOperationTag.INSERT,
523
+ after: {
524
+ id: '2001',
525
+ description: 'Another low-priority row'
526
+ },
527
+ afterReplicaId: '2001'
539
528
  });
529
+
530
+ await writer.commit('0/2');
540
531
  }
541
532
 
542
533
  if (sentRows >= 1000 && sentRows <= 2001) {
@@ -579,20 +570,20 @@ bucket_definitions:
579
570
  content: BASIC_SYNC_RULES
580
571
  });
581
572
  const bucketStorage = f.getInstance(syncRules);
582
-
583
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
584
- await batch.markAllSnapshotDone('0/1');
585
- await batch.save({
586
- sourceTable: TEST_TABLE,
587
- tag: storage.SaveOperationTag.INSERT,
588
- after: {
589
- id: 't1',
590
- description: 'sync'
591
- },
592
- afterReplicaId: 't1'
593
- });
594
- await batch.commit('0/1');
573
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
574
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
575
+
576
+ await writer.markAllSnapshotDone('0/1');
577
+ await writer.save({
578
+ sourceTable: testTable,
579
+ tag: storage.SaveOperationTag.INSERT,
580
+ after: {
581
+ id: 't1',
582
+ description: 'sync'
583
+ },
584
+ afterReplicaId: 't1'
595
585
  });
586
+ await writer.commit('0/1');
596
587
 
597
588
  const stream = sync.streamResponse({
598
589
  syncContext,
@@ -623,9 +614,7 @@ bucket_definitions:
623
614
  if (receivedCompletions == 1) {
624
615
  // Trigger an empty bucket update.
625
616
  await bucketStorage.createManagedWriteCheckpoint({ user_id: '', heads: { '1': '1/0' } });
626
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
627
- await batch.commit('1/0');
628
- });
617
+ await writer.commit('1/0');
629
618
  } else {
630
619
  break;
631
620
  }
@@ -637,30 +626,30 @@ bucket_definitions:
637
626
  });
638
627
 
639
628
  test('sync legacy non-raw data', async () => {
640
- const f = await factory();
629
+ await using f = await factory();
641
630
 
642
631
  const syncRules = await updateSyncRules(f, {
643
632
  content: BASIC_SYNC_RULES
644
633
  });
645
634
 
646
635
  const bucketStorage = await f.getInstance(syncRules);
647
-
648
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
649
- await batch.markAllSnapshotDone('0/1');
650
- await batch.save({
651
- sourceTable: TEST_TABLE,
652
- tag: storage.SaveOperationTag.INSERT,
653
- after: {
654
- id: 't1',
655
- description: 'Test\n"string"',
656
- large_num: 12345678901234567890n
657
- },
658
- afterReplicaId: 't1'
659
- });
660
-
661
- await batch.commit('0/1');
636
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
637
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
638
+
639
+ await writer.markAllSnapshotDone('0/1');
640
+ await writer.save({
641
+ sourceTable: testTable,
642
+ tag: storage.SaveOperationTag.INSERT,
643
+ after: {
644
+ id: 't1',
645
+ description: 'Test\n"string"',
646
+ large_num: 12345678901234567890n
647
+ },
648
+ afterReplicaId: 't1'
662
649
  });
663
650
 
651
+ await writer.commit('0/1');
652
+
664
653
  const stream = sync.streamResponse({
665
654
  syncContext,
666
655
  bucketStorage,
@@ -716,11 +705,11 @@ bucket_definitions:
716
705
  });
717
706
 
718
707
  const bucketStorage = await f.getInstance(syncRules);
708
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
709
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
719
710
  // Activate
720
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
721
- await batch.markAllSnapshotDone('0/0');
722
- await batch.keepalive('0/0');
723
- });
711
+ await writer.markAllSnapshotDone('0/0');
712
+ await writer.keepalive('0/0');
724
713
 
725
714
  const stream = sync.streamResponse({
726
715
  syncContext,
@@ -742,36 +731,32 @@ bucket_definitions:
742
731
 
743
732
  expect(await getCheckpointLines(iter)).toMatchSnapshot();
744
733
 
745
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
746
- await batch.save({
747
- sourceTable: TEST_TABLE,
748
- tag: storage.SaveOperationTag.INSERT,
749
- after: {
750
- id: 't1',
751
- description: 'Test 1'
752
- },
753
- afterReplicaId: 't1'
754
- });
755
-
756
- await batch.commit('0/1');
734
+ await writer.save({
735
+ sourceTable: testTable,
736
+ tag: storage.SaveOperationTag.INSERT,
737
+ after: {
738
+ id: 't1',
739
+ description: 'Test 1'
740
+ },
741
+ afterReplicaId: 't1'
757
742
  });
758
743
 
759
- expect(await getCheckpointLines(iter)).toMatchSnapshot();
744
+ await writer.commit('0/1');
760
745
 
761
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
762
- await batch.save({
763
- sourceTable: TEST_TABLE,
764
- tag: storage.SaveOperationTag.INSERT,
765
- after: {
766
- id: 't2',
767
- description: 'Test 2'
768
- },
769
- afterReplicaId: 't2'
770
- });
746
+ expect(await getCheckpointLines(iter)).toMatchSnapshot();
771
747
 
772
- await batch.commit('0/2');
748
+ await writer.save({
749
+ sourceTable: testTable,
750
+ tag: storage.SaveOperationTag.INSERT,
751
+ after: {
752
+ id: 't2',
753
+ description: 'Test 2'
754
+ },
755
+ afterReplicaId: 't2'
773
756
  });
774
757
 
758
+ await writer.commit('0/2');
759
+
775
760
  expect(await getCheckpointLines(iter)).toMatchSnapshot();
776
761
  });
777
762
 
@@ -787,15 +772,13 @@ bucket_definitions:
787
772
  `
788
773
  });
789
774
 
790
- const usersTable = test_utils.makeTestTable('users', ['id'], config);
791
- const listsTable = test_utils.makeTestTable('lists', ['id'], config);
792
-
793
775
  const bucketStorage = await f.getInstance(syncRules);
776
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
777
+ const usersTable = await test_utils.resolveTestTable(writer, 'users', ['id'], config, 1);
778
+
794
779
  // Activate
795
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
796
- await batch.markAllSnapshotDone('0/0');
797
- await batch.keepalive('0/0');
798
- });
780
+ await writer.markAllSnapshotDone('0/0');
781
+ await writer.keepalive('0/0');
799
782
 
800
783
  const stream = sync.streamResponse({
801
784
  syncContext,
@@ -821,22 +804,21 @@ bucket_definitions:
821
804
  expect(checkpoint1).toMatchSnapshot();
822
805
 
823
806
  // Add user
824
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
825
- await batch.save({
826
- sourceTable: usersTable,
827
- tag: storage.SaveOperationTag.INSERT,
828
- after: {
829
- id: 'user1',
830
- name: 'User 1'
831
- },
832
- afterReplicaId: 'user1'
833
- });
834
-
835
- await batch.commit('0/1');
807
+ await writer.save({
808
+ sourceTable: usersTable,
809
+ tag: storage.SaveOperationTag.INSERT,
810
+ after: {
811
+ id: 'user1',
812
+ name: 'User 1'
813
+ },
814
+ afterReplicaId: 'user1'
836
815
  });
837
816
 
838
- const { bucket } = bucketRequest(syncRules, 'by_user["user1"]');
817
+ await writer.commit('0/1');
818
+
839
819
  const checkpoint2 = await getCheckpointLines(iter);
820
+
821
+ const { bucket } = test_utils.bucketRequest(syncRules, 'by_user["user1"]');
840
822
  expect(
841
823
  (checkpoint2[0] as StreamingSyncCheckpointDiff).checkpoint_diff?.updated_buckets?.map((b) => b.bucket)
842
824
  ).toEqual([bucket]);
@@ -855,26 +837,24 @@ bucket_definitions:
855
837
  `
856
838
  });
857
839
 
858
- const usersTable = test_utils.makeTestTable('users', ['id'], config);
859
- const listsTable = test_utils.makeTestTable('lists', ['id'], config);
860
-
861
840
  const bucketStorage = await f.getInstance(syncRules);
862
-
863
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
864
- await batch.markAllSnapshotDone('0/1');
865
- await batch.save({
866
- sourceTable: usersTable,
867
- tag: storage.SaveOperationTag.INSERT,
868
- after: {
869
- id: 'user1',
870
- name: 'User 1'
871
- },
872
- afterReplicaId: 'user1'
873
- });
874
-
875
- await batch.commit('0/1');
841
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
842
+ const usersTable = await test_utils.resolveTestTable(writer, 'users', ['id'], config, 1);
843
+ const listsTable = await test_utils.resolveTestTable(writer, 'lists', ['id'], config, 2);
844
+
845
+ await writer.markAllSnapshotDone('0/1');
846
+ await writer.save({
847
+ sourceTable: usersTable,
848
+ tag: storage.SaveOperationTag.INSERT,
849
+ after: {
850
+ id: 'user1',
851
+ name: 'User 1'
852
+ },
853
+ afterReplicaId: 'user1'
876
854
  });
877
855
 
856
+ await writer.commit('0/1');
857
+
878
858
  const stream = sync.streamResponse({
879
859
  syncContext,
880
860
  bucketStorage,
@@ -895,24 +875,23 @@ bucket_definitions:
895
875
 
896
876
  const { bucket } = bucketRequest(syncRules, 'by_user["user1"]');
897
877
  const checkpoint1 = await getCheckpointLines(iter);
878
+
898
879
  expect((checkpoint1[0] as StreamingSyncCheckpoint).checkpoint?.buckets?.map((b) => b.bucket)).toEqual([bucket]);
899
880
  expect(checkpoint1).toMatchSnapshot();
900
881
 
901
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
902
- await batch.save({
903
- sourceTable: listsTable,
904
- tag: storage.SaveOperationTag.INSERT,
905
- after: {
906
- id: 'list1',
907
- user_id: 'user1',
908
- name: 'User 1'
909
- },
910
- afterReplicaId: 'list1'
911
- });
912
-
913
- await batch.commit('0/1');
882
+ await writer.save({
883
+ sourceTable: listsTable,
884
+ tag: storage.SaveOperationTag.INSERT,
885
+ after: {
886
+ id: 'list1',
887
+ user_id: 'user1',
888
+ name: 'User 1'
889
+ },
890
+ afterReplicaId: 'list1'
914
891
  });
915
892
 
893
+ await writer.commit('0/1');
894
+
916
895
  const checkpoint2 = await getCheckpointLines(iter);
917
896
  expect(
918
897
  (checkpoint2[0] as StreamingSyncCheckpointDiff).checkpoint_diff?.updated_buckets?.map((b) => b.bucket)
@@ -932,15 +911,13 @@ bucket_definitions:
932
911
  `
933
912
  });
934
913
 
935
- const usersTable = test_utils.makeTestTable('users', ['id'], config);
936
- const listsTable = test_utils.makeTestTable('lists', ['id'], config);
937
-
938
914
  const bucketStorage = await f.getInstance(syncRules);
915
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
916
+ const usersTable = await test_utils.resolveTestTable(writer, 'users', ['id'], config, 1);
917
+ const listsTable = await test_utils.resolveTestTable(writer, 'lists', ['id'], config, 2);
939
918
  // Activate
940
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
941
- await batch.markAllSnapshotDone('0/0');
942
- await batch.keepalive('0/0');
943
- });
919
+ await writer.markAllSnapshotDone('0/0');
920
+ await writer.keepalive('0/0');
944
921
 
945
922
  const stream = sync.streamResponse({
946
923
  syncContext,
@@ -963,33 +940,32 @@ bucket_definitions:
963
940
  // Initial empty checkpoint
964
941
  expect(await getCheckpointLines(iter)).toMatchSnapshot();
965
942
 
966
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
967
- await batch.markAllSnapshotDone('0/1');
968
- await batch.save({
969
- sourceTable: listsTable,
970
- tag: storage.SaveOperationTag.INSERT,
971
- after: {
972
- id: 'list1',
973
- user_id: 'user1',
974
- name: 'User 1'
975
- },
976
- afterReplicaId: 'list1'
977
- });
978
-
979
- await batch.save({
980
- sourceTable: usersTable,
981
- tag: storage.SaveOperationTag.INSERT,
982
- after: {
983
- id: 'user1',
984
- name: 'User 1'
985
- },
986
- afterReplicaId: 'user1'
987
- });
943
+ await writer.markAllSnapshotDone('0/1');
944
+ await writer.save({
945
+ sourceTable: listsTable,
946
+ tag: storage.SaveOperationTag.INSERT,
947
+ after: {
948
+ id: 'list1',
949
+ user_id: 'user1',
950
+ name: 'User 1'
951
+ },
952
+ afterReplicaId: 'list1'
953
+ });
988
954
 
989
- await batch.commit('0/1');
955
+ await writer.save({
956
+ sourceTable: usersTable,
957
+ tag: storage.SaveOperationTag.INSERT,
958
+ after: {
959
+ id: 'user1',
960
+ name: 'User 1'
961
+ },
962
+ afterReplicaId: 'user1'
990
963
  });
991
964
 
992
- const { bucket } = bucketRequest(syncRules, 'by_user["user1"]');
965
+ await writer.commit('0/1');
966
+
967
+ const { bucket } = test_utils.bucketRequest(syncRules, 'by_user["user1"]');
968
+
993
969
  const checkpoint2 = await getCheckpointLines(iter);
994
970
  expect(
995
971
  (checkpoint2[0] as StreamingSyncCheckpointDiff).checkpoint_diff?.updated_buckets?.map((b) => b.bucket)
@@ -1005,11 +981,10 @@ bucket_definitions:
1005
981
  });
1006
982
 
1007
983
  const bucketStorage = await f.getInstance(syncRules);
984
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
1008
985
  // Activate
1009
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1010
- await batch.markAllSnapshotDone('0/0');
1011
- await batch.keepalive('0/0');
1012
- });
986
+ await writer.markAllSnapshotDone('0/0');
987
+ await writer.keepalive('0/0');
1013
988
 
1014
989
  const exp = Date.now() / 1000 + 0.1;
1015
990
 
@@ -1051,32 +1026,32 @@ bucket_definitions:
1051
1026
  });
1052
1027
 
1053
1028
  const bucketStorage = await f.getInstance(syncRules);
1029
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
1030
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
1031
+
1032
+ await writer.markAllSnapshotDone('0/1');
1033
+ await writer.save({
1034
+ sourceTable: testTable,
1035
+ tag: storage.SaveOperationTag.INSERT,
1036
+ after: {
1037
+ id: 't1',
1038
+ description: 'Test 1'
1039
+ },
1040
+ afterReplicaId: 't1'
1041
+ });
1054
1042
 
1055
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1056
- await batch.markAllSnapshotDone('0/1');
1057
- await batch.save({
1058
- sourceTable: TEST_TABLE,
1059
- tag: storage.SaveOperationTag.INSERT,
1060
- after: {
1061
- id: 't1',
1062
- description: 'Test 1'
1063
- },
1064
- afterReplicaId: 't1'
1065
- });
1066
-
1067
- await batch.save({
1068
- sourceTable: TEST_TABLE,
1069
- tag: storage.SaveOperationTag.INSERT,
1070
- after: {
1071
- id: 't2',
1072
- description: 'Test 2'
1073
- },
1074
- afterReplicaId: 't2'
1075
- });
1076
-
1077
- await batch.commit('0/1');
1043
+ await writer.save({
1044
+ sourceTable: testTable,
1045
+ tag: storage.SaveOperationTag.INSERT,
1046
+ after: {
1047
+ id: 't2',
1048
+ description: 'Test 2'
1049
+ },
1050
+ afterReplicaId: 't2'
1078
1051
  });
1079
1052
 
1053
+ await writer.commit('0/1');
1054
+
1080
1055
  const stream = sync.streamResponse({
1081
1056
  syncContext,
1082
1057
  bucketStorage,
@@ -1108,31 +1083,29 @@ bucket_definitions:
1108
1083
  // Now we save additional data AND compact before continuing.
1109
1084
  // This invalidates the checkpoint we've received above.
1110
1085
 
1111
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1112
- await batch.markAllSnapshotDone('0/1');
1113
- await batch.save({
1114
- sourceTable: TEST_TABLE,
1115
- tag: storage.SaveOperationTag.UPDATE,
1116
- after: {
1117
- id: 't1',
1118
- description: 'Test 1b'
1119
- },
1120
- afterReplicaId: 't1'
1121
- });
1122
-
1123
- await batch.save({
1124
- sourceTable: TEST_TABLE,
1125
- tag: storage.SaveOperationTag.UPDATE,
1126
- after: {
1127
- id: 't2',
1128
- description: 'Test 2b'
1129
- },
1130
- afterReplicaId: 't2'
1131
- });
1086
+ await writer.markAllSnapshotDone('0/1');
1087
+ await writer.save({
1088
+ sourceTable: testTable,
1089
+ tag: storage.SaveOperationTag.UPDATE,
1090
+ after: {
1091
+ id: 't1',
1092
+ description: 'Test 1b'
1093
+ },
1094
+ afterReplicaId: 't1'
1095
+ });
1132
1096
 
1133
- await batch.commit('0/2');
1097
+ await writer.save({
1098
+ sourceTable: testTable,
1099
+ tag: storage.SaveOperationTag.UPDATE,
1100
+ after: {
1101
+ id: 't2',
1102
+ description: 'Test 2b'
1103
+ },
1104
+ afterReplicaId: 't2'
1134
1105
  });
1135
1106
 
1107
+ await writer.commit('0/2');
1108
+
1136
1109
  await bucketStorage.compact({
1137
1110
  minBucketChanges: 1,
1138
1111
  minChangeRatio: 0
@@ -1196,12 +1169,11 @@ bucket_definitions:
1196
1169
  });
1197
1170
 
1198
1171
  const bucketStorage = f.getInstance(syncRules);
1172
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
1199
1173
 
1200
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1201
- await batch.markAllSnapshotDone('0/1');
1202
- // <= the managed write checkpoint LSN below
1203
- await batch.commit('0/1');
1204
- });
1174
+ await writer.markAllSnapshotDone('0/1');
1175
+ // <= the managed write checkpoint LSN below
1176
+ await writer.commit('0/1');
1205
1177
 
1206
1178
  const checkpoint = await bucketStorage.createManagedWriteCheckpoint({
1207
1179
  user_id: 'test',
@@ -1233,11 +1205,9 @@ bucket_definitions:
1233
1205
  })
1234
1206
  });
1235
1207
 
1236
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1237
- await batch.markAllSnapshotDone('0/1');
1238
- // must be >= the managed write checkpoint LSN
1239
- await batch.commit('1/0');
1240
- });
1208
+ await writer.markAllSnapshotDone('0/1');
1209
+ // must be >= the managed write checkpoint LSN
1210
+ await writer.commit('1/0');
1241
1211
 
1242
1212
  // At this point the LSN has advanced, so the write checkpoint should be
1243
1213
  // included in the next checkpoint message.
@@ -1251,9 +1221,12 @@ bucket_definitions:
1251
1221
  });
1252
1222
  });
1253
1223
 
1254
- test('encodes sync rules id in buckes for streams', async () => {
1224
+ test('encodes sync rules id in buckets for streams', async () => {
1255
1225
  await using f = await factory();
1256
- const rules = `
1226
+ // This test relies making an actual update to sync rules to test the different bucket names.
1227
+ // The actual naming scheme may change, as long as the two buckets have different names.
1228
+ const rules = [
1229
+ `
1257
1230
  streams:
1258
1231
  test:
1259
1232
  auto_subscribe: true
@@ -1261,27 +1234,38 @@ streams:
1261
1234
 
1262
1235
  config:
1263
1236
  edition: 2
1264
- `;
1237
+ `,
1238
+ `
1239
+ streams:
1240
+ test2:
1241
+ auto_subscribe: true
1242
+ query: SELECT * FROM test WHERE 1;
1243
+
1244
+ config:
1245
+ edition: 2
1246
+ `
1247
+ ];
1265
1248
 
1266
1249
  for (let i = 0; i < 2; i++) {
1267
1250
  const syncRules = await updateSyncRules(f, {
1268
- content: rules
1251
+ content: rules[i]
1269
1252
  });
1270
1253
  const bucketStorage = f.getInstance(syncRules);
1254
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
1255
+
1256
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config, i + 1);
1271
1257
 
1272
- await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
1273
- await batch.markAllSnapshotDone('0/1');
1274
- await batch.save({
1275
- sourceTable: TEST_TABLE,
1276
- tag: storage.SaveOperationTag.INSERT,
1277
- after: {
1278
- id: 't1',
1279
- description: 'Test 1'
1280
- },
1281
- afterReplicaId: 't1'
1282
- });
1283
- await batch.commit('0/1');
1258
+ await writer.markAllSnapshotDone('0/1');
1259
+ await writer.save({
1260
+ sourceTable: testTable,
1261
+ tag: storage.SaveOperationTag.INSERT,
1262
+ after: {
1263
+ id: 't1',
1264
+ description: 'Test 1'
1265
+ },
1266
+ afterReplicaId: 't1'
1284
1267
  });
1268
+ await writer.commit('0/1');
1285
1269
 
1286
1270
  const stream = sync.streamResponse({
1287
1271
  syncContext,