@powersync/service-core-tests 0.17.0 → 0.17.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.
- package/CHANGELOG.md +18 -0
- package/dist/test-utils/AbstractStreamTestContext.d.ts +3 -3
- package/dist/test-utils/AbstractStreamTestContext.js +1 -1
- package/dist/test-utils/AbstractStreamTestContext.js.map +1 -1
- package/dist/test-utils/StorageDataHelpers.d.ts +3 -3
- package/dist/test-utils/StorageDataHelpers.js.map +1 -1
- package/dist/test-utils/bucket-validation.d.ts +3 -3
- package/dist/test-utils/bucket-validation.js +3 -3
- package/dist/test-utils/general-utils.d.ts +1 -0
- package/dist/test-utils/general-utils.js +9 -0
- package/dist/test-utils/general-utils.js.map +1 -1
- package/dist/tests/register-compacting-tests.js +16 -16
- package/dist/tests/register-compacting-tests.js.map +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.js +8 -2
- package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
- package/dist/tests/register-data-storage-data-tests.js +46 -28
- package/dist/tests/register-data-storage-data-tests.js.map +1 -1
- package/dist/tests/register-sync-tests.js +135 -5
- package/dist/tests/register-sync-tests.js.map +1 -1
- package/package.json +5 -5
- package/src/test-utils/AbstractStreamTestContext.ts +3 -2
- package/src/test-utils/StorageDataHelpers.ts +3 -2
- package/src/test-utils/bucket-validation.ts +3 -3
- package/src/test-utils/general-utils.ts +10 -0
- package/src/tests/register-compacting-tests.ts +32 -16
- package/src/tests/register-data-storage-checkpoint-tests.ts +12 -2
- package/src/tests/register-data-storage-data-tests.ts +82 -32
- package/src/tests/register-sync-tests.ts +153 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -75,7 +75,9 @@ bucket_definitions:
|
|
|
75
75
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
76
76
|
|
|
77
77
|
const request = bucketRequest(syncRules, 'global[]');
|
|
78
|
-
const batch = await test_utils.fromAsync(
|
|
78
|
+
const batch = await test_utils.fromAsync(
|
|
79
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
80
|
+
);
|
|
79
81
|
const data = batch[0].chunkData.data.map((d) => {
|
|
80
82
|
return {
|
|
81
83
|
op: d.op,
|
|
@@ -92,7 +94,9 @@ bucket_definitions:
|
|
|
92
94
|
{ op: 'REMOVE', object_id: 'test1', checksum: c2 }
|
|
93
95
|
]);
|
|
94
96
|
|
|
95
|
-
const checksums = [
|
|
97
|
+
const checksums = [
|
|
98
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
99
|
+
];
|
|
96
100
|
expect(checksums).toEqual([
|
|
97
101
|
{
|
|
98
102
|
bucket: request.bucket,
|
|
@@ -143,7 +147,9 @@ bucket_definitions:
|
|
|
143
147
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
144
148
|
|
|
145
149
|
const request = bucketRequest(syncRules, 'global[]');
|
|
146
|
-
const batch = await test_utils.fromAsync(
|
|
150
|
+
const batch = await test_utils.fromAsync(
|
|
151
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
152
|
+
);
|
|
147
153
|
const data = batch[0].chunkData.data.map((d) => {
|
|
148
154
|
return {
|
|
149
155
|
op: d.op,
|
|
@@ -156,7 +162,9 @@ bucket_definitions:
|
|
|
156
162
|
|
|
157
163
|
expect(data).toEqual([{ op: 'PUT', object_id: 'test1', checksum: c1 }]);
|
|
158
164
|
|
|
159
|
-
const checksums = [
|
|
165
|
+
const checksums = [
|
|
166
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
167
|
+
];
|
|
160
168
|
expect(checksums).toEqual([
|
|
161
169
|
{
|
|
162
170
|
bucket: request.bucket,
|
|
@@ -212,7 +220,9 @@ bucket_definitions:
|
|
|
212
220
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
213
221
|
|
|
214
222
|
const request = bucketRequest(syncRules, 'global[]');
|
|
215
|
-
const batch = await test_utils.fromAsync(
|
|
223
|
+
const batch = await test_utils.fromAsync(
|
|
224
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
225
|
+
);
|
|
216
226
|
const data = batch[0].chunkData.data.map((d) => {
|
|
217
227
|
return {
|
|
218
228
|
op: d.op,
|
|
@@ -225,7 +235,9 @@ bucket_definitions:
|
|
|
225
235
|
|
|
226
236
|
expect(data).toEqual([{ op: 'PUT', object_id: 'test1', checksum: c1 }]);
|
|
227
237
|
|
|
228
|
-
const checksums = [
|
|
238
|
+
const checksums = [
|
|
239
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
240
|
+
];
|
|
229
241
|
expect(checksums).toEqual([
|
|
230
242
|
{
|
|
231
243
|
bucket: request.bucket,
|
|
@@ -275,7 +287,9 @@ bucket_definitions:
|
|
|
275
287
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
276
288
|
|
|
277
289
|
const request = bucketRequest(syncRules, 'global[]');
|
|
278
|
-
const batch = await test_utils.fromAsync(
|
|
290
|
+
const batch = await test_utils.fromAsync(
|
|
291
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
292
|
+
);
|
|
279
293
|
const data = batch[0].chunkData.data.map((d) => {
|
|
280
294
|
return {
|
|
281
295
|
op: d.op,
|
|
@@ -288,7 +302,9 @@ bucket_definitions:
|
|
|
288
302
|
|
|
289
303
|
expect(data).toEqual([{ op: 'PUT', object_id: 'test1', checksum: c1 }]);
|
|
290
304
|
|
|
291
|
-
const checksums = [
|
|
305
|
+
const checksums = [
|
|
306
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
307
|
+
];
|
|
292
308
|
expect(checksums).toEqual([
|
|
293
309
|
{
|
|
294
310
|
bucket: request.bucket,
|
|
@@ -362,7 +378,9 @@ bucket_definitions:
|
|
|
362
378
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
363
379
|
|
|
364
380
|
const request = bucketRequest(syncRules, 'global[]');
|
|
365
|
-
const batch = await test_utils.fromAsync(
|
|
381
|
+
const batch = await test_utils.fromAsync(
|
|
382
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
383
|
+
);
|
|
366
384
|
|
|
367
385
|
expect(reduceBucket(batch[0].chunkData.data).slice(1)).toEqual([]);
|
|
368
386
|
|
|
@@ -432,7 +450,7 @@ bucket_definitions:
|
|
|
432
450
|
await writer.commit('1/1');
|
|
433
451
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
434
452
|
const batch = await test_utils.fromAsync(
|
|
435
|
-
bucketStorage.getBucketDataBatch(checkpoint, [bucketRequest(syncRules, 'global[]')])
|
|
453
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [bucketRequest(syncRules, 'global[]')])
|
|
436
454
|
);
|
|
437
455
|
const data = batch[0].chunkData.data.map((d) => {
|
|
438
456
|
return {
|
|
@@ -499,7 +517,9 @@ bucket_definitions:
|
|
|
499
517
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
500
518
|
|
|
501
519
|
const request = bucketRequest(syncRules, 'global[]');
|
|
502
|
-
const batch = await test_utils.fromAsync(
|
|
520
|
+
const batch = await test_utils.fromAsync(
|
|
521
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
522
|
+
);
|
|
503
523
|
const data = batch[0].chunkData.data.map((d) => {
|
|
504
524
|
return {
|
|
505
525
|
op: d.op,
|
|
@@ -516,7 +536,9 @@ bucket_definitions:
|
|
|
516
536
|
{ op: 'REMOVE', object_id: 'test1', checksum: c2 }
|
|
517
537
|
]);
|
|
518
538
|
|
|
519
|
-
const checksums = [
|
|
539
|
+
const checksums = [
|
|
540
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
541
|
+
];
|
|
520
542
|
expect(checksums).toEqual([
|
|
521
543
|
{
|
|
522
544
|
bucket: bucketRequest(syncRules, 'global[]').bucket,
|
|
@@ -619,7 +641,9 @@ bucket_definitions:
|
|
|
619
641
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
620
642
|
|
|
621
643
|
const request = bucketRequest(syncRules, 'global[]');
|
|
622
|
-
const batch = await test_utils.fromAsync(
|
|
644
|
+
const batch = await test_utils.fromAsync(
|
|
645
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
646
|
+
);
|
|
623
647
|
|
|
624
648
|
const data = batch[0].chunkData.data.map((d) => {
|
|
625
649
|
return {
|
|
@@ -639,7 +663,9 @@ bucket_definitions:
|
|
|
639
663
|
{ op: 'REMOVE', object_id: 'test1', checksum: c2 }
|
|
640
664
|
]);
|
|
641
665
|
|
|
642
|
-
const checksums = [
|
|
666
|
+
const checksums = [
|
|
667
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
668
|
+
];
|
|
643
669
|
expect(checksums).toEqual([
|
|
644
670
|
{
|
|
645
671
|
bucket: bucketRequest(syncRules, 'global[]').bucket,
|
|
@@ -782,7 +808,9 @@ bucket_definitions:
|
|
|
782
808
|
const checkpoint2 = result2!.flushed_op;
|
|
783
809
|
|
|
784
810
|
const request = bucketRequest(syncRules, 'global[]', checkpoint1);
|
|
785
|
-
const batch = await test_utils.fromAsync(
|
|
811
|
+
const batch = await test_utils.fromAsync(
|
|
812
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint2), [request])
|
|
813
|
+
);
|
|
786
814
|
|
|
787
815
|
const data = batch[0].chunkData.data.map((d) => {
|
|
788
816
|
return {
|
|
@@ -887,7 +915,7 @@ bucket_definitions:
|
|
|
887
915
|
|
|
888
916
|
const request = bucketRequest(syncRules, 'global[]');
|
|
889
917
|
const batch = await test_utils.fromAsync(
|
|
890
|
-
bucketStorage.getBucketDataBatch(checkpoint3, [{ ...request, start: checkpoint1 }])
|
|
918
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint3), [{ ...request, start: checkpoint1 }])
|
|
891
919
|
);
|
|
892
920
|
const data = batch[0].chunkData.data.map((d) => {
|
|
893
921
|
return {
|
|
@@ -1001,7 +1029,7 @@ bucket_definitions:
|
|
|
1001
1029
|
|
|
1002
1030
|
const request = bucketRequest(syncRules, 'global[]');
|
|
1003
1031
|
const batch = await test_utils.fromAsync(
|
|
1004
|
-
bucketStorage.getBucketDataBatch(checkpoint3, [{ ...request, start: checkpoint1 }])
|
|
1032
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint3), [{ ...request, start: checkpoint1 }])
|
|
1005
1033
|
);
|
|
1006
1034
|
const data = batch[0].chunkData.data.map((d) => {
|
|
1007
1035
|
return {
|
|
@@ -1108,7 +1136,9 @@ bucket_definitions:
|
|
|
1108
1136
|
};
|
|
1109
1137
|
|
|
1110
1138
|
const request = bucketRequest(syncRules, 'global[]');
|
|
1111
|
-
const batch1 = await test_utils.fromAsync(
|
|
1139
|
+
const batch1 = await test_utils.fromAsync(
|
|
1140
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request], options)
|
|
1141
|
+
);
|
|
1112
1142
|
expect(test_utils.getBatchData(batch1)).toEqual([
|
|
1113
1143
|
{ op_id: '1', op: 'PUT', object_id: 'test1', checksum: 2871785649 },
|
|
1114
1144
|
{ op_id: '2', op: 'PUT', object_id: 'large1', checksum: 454746904 }
|
|
@@ -1121,7 +1151,7 @@ bucket_definitions:
|
|
|
1121
1151
|
|
|
1122
1152
|
const batch2 = await test_utils.fromAsync(
|
|
1123
1153
|
bucketStorage.getBucketDataBatch(
|
|
1124
|
-
checkpoint,
|
|
1154
|
+
test_utils.testCheckpoint(checkpoint),
|
|
1125
1155
|
[{ ...request, start: BigInt(batch1[0].chunkData.next_after) }],
|
|
1126
1156
|
options
|
|
1127
1157
|
)
|
|
@@ -1138,7 +1168,7 @@ bucket_definitions:
|
|
|
1138
1168
|
|
|
1139
1169
|
const batch3 = await test_utils.fromAsync(
|
|
1140
1170
|
bucketStorage.getBucketDataBatch(
|
|
1141
|
-
checkpoint,
|
|
1171
|
+
test_utils.testCheckpoint(checkpoint),
|
|
1142
1172
|
[{ ...request, start: BigInt(batch2[0].chunkData.next_after) }],
|
|
1143
1173
|
options
|
|
1144
1174
|
)
|
|
@@ -1186,7 +1216,9 @@ bucket_definitions:
|
|
|
1186
1216
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
1187
1217
|
|
|
1188
1218
|
const request = bucketRequest(syncRules, 'global[]');
|
|
1189
|
-
const batch1 = await test_utils.oneFromAsync(
|
|
1219
|
+
const batch1 = await test_utils.oneFromAsync(
|
|
1220
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request], { limit: 4 })
|
|
1221
|
+
);
|
|
1190
1222
|
|
|
1191
1223
|
expect(test_utils.getBatchData(batch1)).toEqual([
|
|
1192
1224
|
{ op_id: '1', op: 'PUT', object_id: 'test1', checksum: 2871785649 },
|
|
@@ -1202,9 +1234,13 @@ bucket_definitions:
|
|
|
1202
1234
|
});
|
|
1203
1235
|
|
|
1204
1236
|
const batch2 = await test_utils.oneFromAsync(
|
|
1205
|
-
bucketStorage.getBucketDataBatch(
|
|
1206
|
-
|
|
1207
|
-
|
|
1237
|
+
bucketStorage.getBucketDataBatch(
|
|
1238
|
+
test_utils.testCheckpoint(checkpoint),
|
|
1239
|
+
[{ ...request, start: BigInt(batch1.chunkData.next_after) }],
|
|
1240
|
+
{
|
|
1241
|
+
limit: 4
|
|
1242
|
+
}
|
|
1243
|
+
)
|
|
1208
1244
|
);
|
|
1209
1245
|
expect(test_utils.getBatchData(batch2)).toEqual([
|
|
1210
1246
|
{ op_id: '5', op: 'PUT', object_id: 'test5', checksum: 3686902721 },
|
|
@@ -1218,9 +1254,13 @@ bucket_definitions:
|
|
|
1218
1254
|
});
|
|
1219
1255
|
|
|
1220
1256
|
const batch3 = await test_utils.fromAsync(
|
|
1221
|
-
bucketStorage.getBucketDataBatch(
|
|
1222
|
-
|
|
1223
|
-
|
|
1257
|
+
bucketStorage.getBucketDataBatch(
|
|
1258
|
+
test_utils.testCheckpoint(checkpoint),
|
|
1259
|
+
[{ ...request, start: BigInt(batch2.chunkData.next_after) }],
|
|
1260
|
+
{
|
|
1261
|
+
limit: 4
|
|
1262
|
+
}
|
|
1263
|
+
)
|
|
1224
1264
|
);
|
|
1225
1265
|
expect(test_utils.getBatchData(batch3)).toEqual([]);
|
|
1226
1266
|
|
|
@@ -1269,7 +1309,11 @@ bucket_definitions:
|
|
|
1269
1309
|
const global1Request = bucketRequest(syncRules, 'global1[]', 0n);
|
|
1270
1310
|
const global2Request = bucketRequest(syncRules, 'global2[]', 0n);
|
|
1271
1311
|
const batch = await test_utils.fromAsync(
|
|
1272
|
-
bucketStorage.getBucketDataBatch(
|
|
1312
|
+
bucketStorage.getBucketDataBatch(
|
|
1313
|
+
test_utils.testCheckpoint(checkpoint),
|
|
1314
|
+
[global1Request, global2Request],
|
|
1315
|
+
options
|
|
1316
|
+
)
|
|
1273
1317
|
);
|
|
1274
1318
|
|
|
1275
1319
|
return { batch, global1Request, global2Request };
|
|
@@ -1519,9 +1563,13 @@ bucket_definitions:
|
|
|
1519
1563
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
1520
1564
|
|
|
1521
1565
|
const request = bucketRequest(syncRules, 'global[]');
|
|
1522
|
-
const checksums = [
|
|
1566
|
+
const checksums = [
|
|
1567
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request])).values()
|
|
1568
|
+
];
|
|
1523
1569
|
expect(checksums).toEqual([{ bucket: request.bucket, checksum: 1917136889, count: 1 }]);
|
|
1524
|
-
const checksums2 = [
|
|
1570
|
+
const checksums2 = [
|
|
1571
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint + 1n), [request])).values()
|
|
1572
|
+
];
|
|
1525
1573
|
expect(checksums2).toEqual([{ bucket: request.bucket, checksum: 1917136889, count: 1 }]);
|
|
1526
1574
|
});
|
|
1527
1575
|
|
|
@@ -1724,7 +1772,7 @@ bucket_definitions:
|
|
|
1724
1772
|
const cp = await bucketStorage.getCheckpoint();
|
|
1725
1773
|
expect(cp.lsn).toEqual('3/1');
|
|
1726
1774
|
const data = await test_utils.fromAsync(
|
|
1727
|
-
bucketStorage.getBucketDataBatch(cp.checkpoint, [bucketRequest(syncRules, 'global[]')])
|
|
1775
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(cp.checkpoint), [bucketRequest(syncRules, 'global[]')])
|
|
1728
1776
|
);
|
|
1729
1777
|
|
|
1730
1778
|
expect(data).toEqual([]);
|
|
@@ -1781,7 +1829,9 @@ bucket_definitions:
|
|
|
1781
1829
|
const users = ['u1', 'u2', 'u3', 'u4'];
|
|
1782
1830
|
const expectedChecksums = [346204588, 5261081, 134760718, -302639724];
|
|
1783
1831
|
const bucketRequests = users.map((user) => bucketRequest(syncRules, `user["${user}"]`));
|
|
1784
|
-
const checksums = [
|
|
1832
|
+
const checksums = [
|
|
1833
|
+
...(await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), bucketRequests)).values()
|
|
1834
|
+
];
|
|
1785
1835
|
checksums.sort((a, b) => a.bucket.localeCompare(b.bucket));
|
|
1786
1836
|
const expected = bucketRequests.map((request, index) => ({
|
|
1787
1837
|
bucket: request.bucket,
|
|
@@ -687,7 +687,7 @@ bucket_definitions:
|
|
|
687
687
|
receivedCompletions++;
|
|
688
688
|
if (receivedCompletions == 1) {
|
|
689
689
|
// Trigger an empty bucket update.
|
|
690
|
-
await bucketStorage.
|
|
690
|
+
await bucketStorage.createManagedWriteCheckpoints([{ user_id: '', heads: { '1': '1/0' } }]);
|
|
691
691
|
await writer.commit('1/0');
|
|
692
692
|
} else {
|
|
693
693
|
break;
|
|
@@ -1238,6 +1238,150 @@ bucket_definitions:
|
|
|
1238
1238
|
});
|
|
1239
1239
|
});
|
|
1240
1240
|
|
|
1241
|
+
test('compacting high-priority data invalidates checkpoint before lower priorities', async (context) => {
|
|
1242
|
+
// Scenario:
|
|
1243
|
+
// 1. Bucket priorities are used.
|
|
1244
|
+
// 2. Client syncs a high-priority bucket.
|
|
1245
|
+
// 3. Due to a concurrent compaction, the checkpoint is invalidated (via target_op).
|
|
1246
|
+
// 4. The service picks up that invalidation, and the batch stops, without emitting a "partial_checkpoint_complete". The intention is continuing with the next checkpoint.
|
|
1247
|
+
// 5. However, the loop handling priorities does not see that invalidation, it only sees "bucket data for this priority is done".
|
|
1248
|
+
// 6. The priority loop incorrectly continues with the next priority.
|
|
1249
|
+
// 7. The service emits the data, as well as a final checkpoint_complete.
|
|
1250
|
+
// 8. The client now received a checkpoint_complete without the full data for that checkpoint. It would pick up the missing data in the checksum check, requiring a full re-download of the affected buckets.
|
|
1251
|
+
await using f = await factory();
|
|
1252
|
+
|
|
1253
|
+
const syncRules = await updateSyncRules(f, {
|
|
1254
|
+
content: `
|
|
1255
|
+
bucket_definitions:
|
|
1256
|
+
high_priority:
|
|
1257
|
+
priority: 1
|
|
1258
|
+
data:
|
|
1259
|
+
- SELECT * FROM test WHERE substring(id, 1, 4) = 'high';
|
|
1260
|
+
low_priority:
|
|
1261
|
+
priority: 2
|
|
1262
|
+
data:
|
|
1263
|
+
- SELECT * FROM test WHERE id = 'low';
|
|
1264
|
+
`
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
const bucketStorage = await f.getInstance(syncRules);
|
|
1268
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
1269
|
+
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
1270
|
+
|
|
1271
|
+
await writer.markAllSnapshotDone('0/1');
|
|
1272
|
+
await writer.save({
|
|
1273
|
+
sourceTable: testTable,
|
|
1274
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
1275
|
+
after: {
|
|
1276
|
+
id: 'high1',
|
|
1277
|
+
description: 'High priority 1'
|
|
1278
|
+
},
|
|
1279
|
+
afterReplicaId: 'high1'
|
|
1280
|
+
});
|
|
1281
|
+
await writer.save({
|
|
1282
|
+
sourceTable: testTable,
|
|
1283
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
1284
|
+
after: {
|
|
1285
|
+
id: 'high2',
|
|
1286
|
+
description: 'High priority 2'
|
|
1287
|
+
},
|
|
1288
|
+
afterReplicaId: 'high2'
|
|
1289
|
+
});
|
|
1290
|
+
await writer.save({
|
|
1291
|
+
sourceTable: testTable,
|
|
1292
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
1293
|
+
after: {
|
|
1294
|
+
id: 'low',
|
|
1295
|
+
description: 'Low priority'
|
|
1296
|
+
},
|
|
1297
|
+
afterReplicaId: 'low'
|
|
1298
|
+
});
|
|
1299
|
+
await writer.commit('0/1');
|
|
1300
|
+
|
|
1301
|
+
const stream = sync.streamResponse({
|
|
1302
|
+
syncContext,
|
|
1303
|
+
bucketStorage,
|
|
1304
|
+
syncRules: bucketStorage.getParsedSyncRules(test_utils.PARSE_OPTIONS),
|
|
1305
|
+
params: {
|
|
1306
|
+
buckets: [],
|
|
1307
|
+
include_checksum: true,
|
|
1308
|
+
raw_data: true
|
|
1309
|
+
},
|
|
1310
|
+
tracker,
|
|
1311
|
+
token: new JwtPayload({ sub: '', exp: Date.now() / 1000 + 10 }),
|
|
1312
|
+
isEncodingAsBson: false
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
const iter = stream[Symbol.asyncIterator]();
|
|
1316
|
+
await using _ = {
|
|
1317
|
+
async [Symbol.asyncDispose]() {
|
|
1318
|
+
await iter.return?.();
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
const checkpoint = await consumeIterator(iter, {
|
|
1323
|
+
consume: false,
|
|
1324
|
+
isDone: (line) => (line as any)?.checkpoint != null
|
|
1325
|
+
});
|
|
1326
|
+
expect(checkpoint[0]).toEqual({
|
|
1327
|
+
checkpoint: expect.objectContaining({
|
|
1328
|
+
last_op_id: '3'
|
|
1329
|
+
})
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
await writer.save({
|
|
1333
|
+
sourceTable: testTable,
|
|
1334
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
1335
|
+
after: {
|
|
1336
|
+
id: 'high1',
|
|
1337
|
+
description: 'Updated high priority 1'
|
|
1338
|
+
},
|
|
1339
|
+
afterReplicaId: 'high1'
|
|
1340
|
+
});
|
|
1341
|
+
await writer.save({
|
|
1342
|
+
sourceTable: testTable,
|
|
1343
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
1344
|
+
after: {
|
|
1345
|
+
id: 'high2',
|
|
1346
|
+
description: 'Updated high priority 2'
|
|
1347
|
+
},
|
|
1348
|
+
afterReplicaId: 'high2'
|
|
1349
|
+
});
|
|
1350
|
+
await writer.commit('0/2');
|
|
1351
|
+
|
|
1352
|
+
await bucketStorage.compact({
|
|
1353
|
+
minBucketChanges: 1,
|
|
1354
|
+
minChangeRatio: 0
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1357
|
+
const lines = await getCheckpointLines(iter, { consume: true });
|
|
1358
|
+
const nextCheckpointIndex = lines.findIndex((line) => (line as any)?.checkpoint_diff != null);
|
|
1359
|
+
expect(nextCheckpointIndex).toBeGreaterThan(0);
|
|
1360
|
+
|
|
1361
|
+
const invalidatedCheckpointLines = lines.slice(0, nextCheckpointIndex);
|
|
1362
|
+
expect(invalidatedCheckpointLines).not.toContainEqual(
|
|
1363
|
+
expect.objectContaining({ checkpoint_complete: expect.anything() })
|
|
1364
|
+
);
|
|
1365
|
+
expect(invalidatedCheckpointLines).not.toContainEqual(
|
|
1366
|
+
expect.objectContaining({
|
|
1367
|
+
data: expect.objectContaining({
|
|
1368
|
+
bucket: expect.stringContaining('low_priority')
|
|
1369
|
+
})
|
|
1370
|
+
})
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
expect(lines[nextCheckpointIndex]).toEqual({
|
|
1374
|
+
checkpoint_diff: expect.objectContaining({
|
|
1375
|
+
last_op_id: '5'
|
|
1376
|
+
})
|
|
1377
|
+
});
|
|
1378
|
+
expect(lines.at(-1)).toEqual({
|
|
1379
|
+
checkpoint_complete: expect.objectContaining({
|
|
1380
|
+
last_op_id: '5'
|
|
1381
|
+
})
|
|
1382
|
+
});
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1241
1385
|
test('write checkpoint', async () => {
|
|
1242
1386
|
await using f = await factory();
|
|
1243
1387
|
|
|
@@ -1252,10 +1396,14 @@ bucket_definitions:
|
|
|
1252
1396
|
// <= the managed write checkpoint LSN below
|
|
1253
1397
|
await writer.commit('0/1');
|
|
1254
1398
|
|
|
1255
|
-
const checkpoint =
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1399
|
+
const checkpoint = (
|
|
1400
|
+
await bucketStorage.createManagedWriteCheckpoints([
|
|
1401
|
+
{
|
|
1402
|
+
user_id: 'test',
|
|
1403
|
+
heads: { '1': '1/0' }
|
|
1404
|
+
}
|
|
1405
|
+
])
|
|
1406
|
+
).get('test')!;
|
|
1259
1407
|
|
|
1260
1408
|
const params: sync.SyncStreamParameters = {
|
|
1261
1409
|
syncContext,
|