@powersync/service-core-tests 0.13.2 → 0.15.0
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 +43 -0
- package/dist/test-utils/general-utils.d.ts +9 -3
- package/dist/test-utils/general-utils.js +26 -26
- package/dist/test-utils/general-utils.js.map +1 -1
- package/dist/tests/register-compacting-tests.d.ts +1 -1
- package/dist/tests/register-compacting-tests.js +136 -93
- package/dist/tests/register-compacting-tests.js.map +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.js +44 -27
- package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
- package/dist/tests/register-data-storage-data-tests.d.ts +2 -2
- package/dist/tests/register-data-storage-data-tests.js +715 -207
- package/dist/tests/register-data-storage-data-tests.js.map +1 -1
- package/dist/tests/register-data-storage-parameter-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-parameter-tests.js +123 -58
- package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
- package/dist/tests/register-parameter-compacting-tests.d.ts +1 -1
- package/dist/tests/register-parameter-compacting-tests.js +13 -13
- package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
- package/dist/tests/register-sync-tests.d.ts +4 -1
- package/dist/tests/register-sync-tests.js +63 -34
- package/dist/tests/register-sync-tests.js.map +1 -1
- package/dist/tests/util.d.ts +6 -1
- package/dist/tests/util.js +31 -2
- package/dist/tests/util.js.map +1 -1
- package/package.json +3 -3
- package/src/test-utils/general-utils.ts +42 -28
- package/src/tests/register-compacting-tests.ts +153 -103
- package/src/tests/register-data-storage-checkpoint-tests.ts +70 -22
- package/src/tests/register-data-storage-data-tests.ts +732 -110
- package/src/tests/register-data-storage-parameter-tests.ts +168 -59
- package/src/tests/register-parameter-compacting-tests.ts +18 -13
- package/src/tests/register-sync-tests.ts +71 -35
- package/src/tests/util.ts +52 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
import { storage } from '@powersync/service-core';
|
|
1
|
+
import { addChecksums, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
2
2
|
import { expect, test } from 'vitest';
|
|
3
3
|
import * as test_utils from '../test-utils/test-utils-index.js';
|
|
4
|
+
import { bucketRequest } from '../test-utils/test-utils-index.js';
|
|
5
|
+
import { bucketRequestMap, bucketRequests } from './util.js';
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
export function registerCompactTests(config: storage.TestStorageConfig) {
|
|
8
|
+
const generateStorageFactory = config.factory;
|
|
9
|
+
const TEST_TABLE = test_utils.makeTestTable('test', ['id'], config);
|
|
6
10
|
|
|
7
|
-
export function registerCompactTests(generateStorageFactory: storage.TestStorageFactory) {
|
|
8
11
|
test('compacting (1)', async () => {
|
|
9
12
|
await using factory = await generateStorageFactory();
|
|
10
|
-
const syncRules = await factory.updateSyncRules(
|
|
11
|
-
|
|
13
|
+
const syncRules = await factory.updateSyncRules(
|
|
14
|
+
updateSyncRulesFromYaml(`
|
|
12
15
|
bucket_definitions:
|
|
13
16
|
global:
|
|
14
17
|
data: [select * from test]
|
|
15
|
-
`
|
|
16
|
-
|
|
18
|
+
`)
|
|
19
|
+
);
|
|
17
20
|
const bucketStorage = factory.getInstance(syncRules);
|
|
18
21
|
|
|
19
22
|
const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
23
|
+
await batch.markAllSnapshotDone('1/1');
|
|
20
24
|
await batch.save({
|
|
21
25
|
sourceTable: TEST_TABLE,
|
|
22
26
|
tag: storage.SaveOperationTag.INSERT,
|
|
@@ -49,27 +53,24 @@ bucket_definitions:
|
|
|
49
53
|
|
|
50
54
|
const checkpoint = result!.flushed_op;
|
|
51
55
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
);
|
|
56
|
+
const request = bucketRequest(syncRules, 'global[]');
|
|
57
|
+
|
|
58
|
+
const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
|
|
55
59
|
const dataBefore = batchBefore.chunkData.data;
|
|
56
|
-
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [
|
|
60
|
+
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
57
61
|
|
|
58
62
|
expect(dataBefore).toMatchObject([
|
|
59
63
|
{
|
|
60
|
-
checksum: 2634521662,
|
|
61
64
|
object_id: 't1',
|
|
62
65
|
op: 'PUT',
|
|
63
66
|
op_id: '1'
|
|
64
67
|
},
|
|
65
68
|
{
|
|
66
|
-
checksum: 4243212114,
|
|
67
69
|
object_id: 't2',
|
|
68
70
|
op: 'PUT',
|
|
69
71
|
op_id: '2'
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
|
-
checksum: 4243212114,
|
|
73
74
|
object_id: 't2',
|
|
74
75
|
op: 'PUT',
|
|
75
76
|
op_id: '3'
|
|
@@ -85,53 +86,47 @@ bucket_definitions:
|
|
|
85
86
|
minChangeRatio: 0
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
89
|
-
bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', 0n]]))
|
|
90
|
-
);
|
|
89
|
+
const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
|
|
91
90
|
const dataAfter = batchAfter.chunkData.data;
|
|
92
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [
|
|
91
|
+
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
93
92
|
bucketStorage.clearChecksumCache();
|
|
94
|
-
const checksumAfter2 = await bucketStorage.getChecksums(checkpoint, [
|
|
93
|
+
const checksumAfter2 = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
95
94
|
|
|
96
95
|
expect(batchAfter.targetOp).toEqual(3n);
|
|
97
96
|
expect(dataAfter).toMatchObject([
|
|
97
|
+
dataBefore[0],
|
|
98
98
|
{
|
|
99
|
-
checksum:
|
|
100
|
-
object_id: 't1',
|
|
101
|
-
op: 'PUT',
|
|
102
|
-
op_id: '1'
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
checksum: 4243212114,
|
|
99
|
+
checksum: dataBefore[1].checksum,
|
|
106
100
|
op: 'MOVE',
|
|
107
101
|
op_id: '2'
|
|
108
102
|
},
|
|
109
103
|
{
|
|
110
|
-
checksum:
|
|
104
|
+
checksum: dataBefore[2].checksum,
|
|
111
105
|
object_id: 't2',
|
|
112
106
|
op: 'PUT',
|
|
113
107
|
op_id: '3'
|
|
114
108
|
}
|
|
115
109
|
]);
|
|
116
110
|
|
|
117
|
-
expect(checksumAfter.get(
|
|
118
|
-
expect(checksumAfter2.get(
|
|
111
|
+
expect(checksumAfter.get(request.bucket)).toEqual(checksumBefore.get(request.bucket));
|
|
112
|
+
expect(checksumAfter2.get(request.bucket)).toEqual(checksumBefore.get(request.bucket));
|
|
119
113
|
|
|
120
114
|
test_utils.validateCompactedBucket(dataBefore, dataAfter);
|
|
121
115
|
});
|
|
122
116
|
|
|
123
117
|
test('compacting (2)', async () => {
|
|
124
118
|
await using factory = await generateStorageFactory();
|
|
125
|
-
const syncRules = await factory.updateSyncRules(
|
|
126
|
-
|
|
119
|
+
const syncRules = await factory.updateSyncRules(
|
|
120
|
+
updateSyncRulesFromYaml(`
|
|
127
121
|
bucket_definitions:
|
|
128
122
|
global:
|
|
129
123
|
data: [select * from test]
|
|
130
|
-
`
|
|
131
|
-
|
|
124
|
+
`)
|
|
125
|
+
);
|
|
132
126
|
const bucketStorage = factory.getInstance(syncRules);
|
|
133
127
|
|
|
134
128
|
const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
129
|
+
await batch.markAllSnapshotDone('1/1');
|
|
135
130
|
await batch.save({
|
|
136
131
|
sourceTable: TEST_TABLE,
|
|
137
132
|
tag: storage.SaveOperationTag.INSERT,
|
|
@@ -172,37 +167,29 @@ bucket_definitions:
|
|
|
172
167
|
});
|
|
173
168
|
|
|
174
169
|
const checkpoint = result!.flushed_op;
|
|
170
|
+
const request = bucketRequest(syncRules, 'global[]');
|
|
175
171
|
|
|
176
|
-
const batchBefore = await test_utils.oneFromAsync(
|
|
177
|
-
bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', 0n]]))
|
|
178
|
-
);
|
|
172
|
+
const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
|
|
179
173
|
const dataBefore = batchBefore.chunkData.data;
|
|
180
|
-
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [
|
|
174
|
+
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
181
175
|
|
|
176
|
+
// op_id sequence depends on the storage implementation
|
|
182
177
|
expect(dataBefore).toMatchObject([
|
|
183
178
|
{
|
|
184
|
-
checksum: 2634521662,
|
|
185
179
|
object_id: 't1',
|
|
186
|
-
op: 'PUT'
|
|
187
|
-
op_id: '1'
|
|
180
|
+
op: 'PUT'
|
|
188
181
|
},
|
|
189
182
|
{
|
|
190
|
-
checksum: 4243212114,
|
|
191
183
|
object_id: 't2',
|
|
192
|
-
op: 'PUT'
|
|
193
|
-
op_id: '2'
|
|
184
|
+
op: 'PUT'
|
|
194
185
|
},
|
|
195
186
|
{
|
|
196
|
-
checksum: 4228978084,
|
|
197
187
|
object_id: 't1',
|
|
198
|
-
op: 'REMOVE'
|
|
199
|
-
op_id: '3'
|
|
188
|
+
op: 'REMOVE'
|
|
200
189
|
},
|
|
201
190
|
{
|
|
202
|
-
checksum: 4243212114,
|
|
203
191
|
object_id: 't2',
|
|
204
|
-
op: 'PUT'
|
|
205
|
-
op_id: '4'
|
|
192
|
+
op: 'PUT'
|
|
206
193
|
}
|
|
207
194
|
]);
|
|
208
195
|
|
|
@@ -214,29 +201,28 @@ bucket_definitions:
|
|
|
214
201
|
minChangeRatio: 0
|
|
215
202
|
});
|
|
216
203
|
|
|
217
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
218
|
-
bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', 0n]]))
|
|
219
|
-
);
|
|
204
|
+
const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
|
|
220
205
|
const dataAfter = batchAfter.chunkData.data;
|
|
221
206
|
bucketStorage.clearChecksumCache();
|
|
222
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [
|
|
207
|
+
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
223
208
|
|
|
224
|
-
expect(batchAfter.targetOp).
|
|
209
|
+
expect(batchAfter.targetOp).toBeLessThanOrEqual(checkpoint);
|
|
225
210
|
expect(dataAfter).toMatchObject([
|
|
226
211
|
{
|
|
227
|
-
checksum:
|
|
228
|
-
|
|
229
|
-
|
|
212
|
+
checksum: addChecksums(
|
|
213
|
+
addChecksums(dataBefore[0].checksum as number, dataBefore[1].checksum as number),
|
|
214
|
+
dataBefore[2].checksum as number
|
|
215
|
+
),
|
|
216
|
+
op: 'CLEAR'
|
|
230
217
|
},
|
|
231
218
|
{
|
|
232
|
-
checksum:
|
|
219
|
+
checksum: dataBefore[3].checksum,
|
|
233
220
|
object_id: 't2',
|
|
234
|
-
op: 'PUT'
|
|
235
|
-
op_id: '4'
|
|
221
|
+
op: 'PUT'
|
|
236
222
|
}
|
|
237
223
|
]);
|
|
238
|
-
expect(checksumAfter.get(
|
|
239
|
-
...checksumBefore.get(
|
|
224
|
+
expect(checksumAfter.get(request.bucket)).toEqual({
|
|
225
|
+
...checksumBefore.get(request.bucket),
|
|
240
226
|
count: 2
|
|
241
227
|
});
|
|
242
228
|
|
|
@@ -245,16 +231,17 @@ bucket_definitions:
|
|
|
245
231
|
|
|
246
232
|
test('compacting (3)', async () => {
|
|
247
233
|
await using factory = await generateStorageFactory();
|
|
248
|
-
const syncRules = await factory.updateSyncRules(
|
|
249
|
-
|
|
234
|
+
const syncRules = await factory.updateSyncRules(
|
|
235
|
+
updateSyncRulesFromYaml(`
|
|
250
236
|
bucket_definitions:
|
|
251
237
|
global:
|
|
252
238
|
data: [select * from test]
|
|
253
|
-
`
|
|
254
|
-
|
|
239
|
+
`)
|
|
240
|
+
);
|
|
255
241
|
const bucketStorage = factory.getInstance(syncRules);
|
|
256
242
|
|
|
257
243
|
const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
244
|
+
await batch.markAllSnapshotDone('1/1');
|
|
258
245
|
await batch.save({
|
|
259
246
|
sourceTable: TEST_TABLE,
|
|
260
247
|
tag: storage.SaveOperationTag.INSERT,
|
|
@@ -286,7 +273,8 @@ bucket_definitions:
|
|
|
286
273
|
});
|
|
287
274
|
|
|
288
275
|
const checkpoint1 = result!.flushed_op;
|
|
289
|
-
const
|
|
276
|
+
const request = bucketRequest(syncRules, 'global[]');
|
|
277
|
+
const checksumBefore = await bucketStorage.getChecksums(checkpoint1, [request]);
|
|
290
278
|
|
|
291
279
|
const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
292
280
|
await batch.save({
|
|
@@ -309,43 +297,39 @@ bucket_definitions:
|
|
|
309
297
|
minChangeRatio: 0
|
|
310
298
|
});
|
|
311
299
|
|
|
312
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
313
|
-
bucketStorage.getBucketDataBatch(checkpoint2, new Map([['global[]', 0n]]))
|
|
314
|
-
);
|
|
300
|
+
const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint2, [request]));
|
|
315
301
|
const dataAfter = batchAfter.chunkData.data;
|
|
316
302
|
await bucketStorage.clearChecksumCache();
|
|
317
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [
|
|
303
|
+
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
318
304
|
|
|
319
|
-
expect(batchAfter.targetOp).toEqual(4n);
|
|
320
305
|
expect(dataAfter).toMatchObject([
|
|
321
306
|
{
|
|
322
|
-
|
|
323
|
-
op: 'CLEAR',
|
|
324
|
-
op_id: '4'
|
|
307
|
+
op: 'CLEAR'
|
|
325
308
|
}
|
|
326
309
|
]);
|
|
327
|
-
expect(checksumAfter.get(
|
|
328
|
-
bucket:
|
|
310
|
+
expect(checksumAfter.get(request.bucket)).toEqual({
|
|
311
|
+
bucket: request.bucket,
|
|
329
312
|
count: 1,
|
|
330
|
-
checksum:
|
|
313
|
+
checksum: dataAfter[0].checksum
|
|
331
314
|
});
|
|
332
315
|
});
|
|
333
316
|
|
|
334
317
|
test('compacting (4)', async () => {
|
|
335
318
|
await using factory = await generateStorageFactory();
|
|
336
|
-
const syncRules = await factory.updateSyncRules(
|
|
337
|
-
|
|
319
|
+
const syncRules = await factory.updateSyncRules(
|
|
320
|
+
updateSyncRulesFromYaml(` bucket_definitions:
|
|
338
321
|
grouped:
|
|
339
322
|
# The parameter query here is not important
|
|
340
323
|
# We specifically don't want to create bucket_parameter records here
|
|
341
324
|
# since the op_ids for bucket_data could vary between storage implementations.
|
|
342
325
|
parameters: select 'b' as b
|
|
343
326
|
data:
|
|
344
|
-
- select * from test where b = bucket.b`
|
|
345
|
-
|
|
327
|
+
- select * from test where b = bucket.b`)
|
|
328
|
+
);
|
|
346
329
|
const bucketStorage = factory.getInstance(syncRules);
|
|
347
330
|
|
|
348
331
|
const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
332
|
+
await batch.markAllSnapshotDone('1/1');
|
|
349
333
|
/**
|
|
350
334
|
* Repeatedly create operations which fall into different buckets.
|
|
351
335
|
* The bucket operations are purposely interleaved as the op_id increases.
|
|
@@ -426,7 +410,7 @@ bucket_definitions:
|
|
|
426
410
|
const batchAfter = await test_utils.fromAsync(
|
|
427
411
|
bucketStorage.getBucketDataBatch(
|
|
428
412
|
checkpoint,
|
|
429
|
-
|
|
413
|
+
bucketRequestMap(syncRules, [
|
|
430
414
|
['grouped["b1"]', 0n],
|
|
431
415
|
['grouped["b2"]', 0n]
|
|
432
416
|
])
|
|
@@ -463,16 +447,17 @@ bucket_definitions:
|
|
|
463
447
|
|
|
464
448
|
test('partial checksums after compacting', async () => {
|
|
465
449
|
await using factory = await generateStorageFactory();
|
|
466
|
-
const syncRules = await factory.updateSyncRules(
|
|
467
|
-
|
|
450
|
+
const syncRules = await factory.updateSyncRules(
|
|
451
|
+
updateSyncRulesFromYaml(`
|
|
468
452
|
bucket_definitions:
|
|
469
453
|
global:
|
|
470
454
|
data: [select * from test]
|
|
471
|
-
`
|
|
472
|
-
|
|
455
|
+
`)
|
|
456
|
+
);
|
|
473
457
|
const bucketStorage = factory.getInstance(syncRules);
|
|
474
458
|
|
|
475
|
-
|
|
459
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
460
|
+
await batch.markAllSnapshotDone('1/1');
|
|
476
461
|
await batch.save({
|
|
477
462
|
sourceTable: TEST_TABLE,
|
|
478
463
|
tag: storage.SaveOperationTag.INSERT,
|
|
@@ -523,27 +508,32 @@ bucket_definitions:
|
|
|
523
508
|
await batch.commit('2/1');
|
|
524
509
|
});
|
|
525
510
|
const checkpoint2 = result2!.flushed_op;
|
|
511
|
+
const request = bucketRequest(syncRules, 'global[]');
|
|
526
512
|
await bucketStorage.clearChecksumCache();
|
|
527
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
513
|
+
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
514
|
+
const globalChecksum = checksumAfter.get(request.bucket);
|
|
515
|
+
expect(globalChecksum).toMatchObject({
|
|
516
|
+
bucket: request.bucket,
|
|
517
|
+
count: 4
|
|
532
518
|
});
|
|
519
|
+
|
|
520
|
+
// storage-specific checksum - just check that it does not change
|
|
521
|
+
expect(globalChecksum).toMatchSnapshot();
|
|
533
522
|
});
|
|
534
523
|
|
|
535
524
|
test('partial checksums after compacting (2)', async () => {
|
|
536
525
|
await using factory = await generateStorageFactory();
|
|
537
|
-
const syncRules = await factory.updateSyncRules(
|
|
538
|
-
|
|
526
|
+
const syncRules = await factory.updateSyncRules(
|
|
527
|
+
updateSyncRulesFromYaml(`
|
|
539
528
|
bucket_definitions:
|
|
540
529
|
global:
|
|
541
530
|
data: [select * from test]
|
|
542
|
-
`
|
|
543
|
-
|
|
531
|
+
`)
|
|
532
|
+
);
|
|
544
533
|
const bucketStorage = factory.getInstance(syncRules);
|
|
545
534
|
|
|
546
535
|
const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
536
|
+
await batch.markAllSnapshotDone('1/1');
|
|
547
537
|
await batch.save({
|
|
548
538
|
sourceTable: TEST_TABLE,
|
|
549
539
|
tag: storage.SaveOperationTag.INSERT,
|
|
@@ -566,7 +556,7 @@ bucket_definitions:
|
|
|
566
556
|
});
|
|
567
557
|
|
|
568
558
|
// Get checksums here just to populate the cache
|
|
569
|
-
await bucketStorage.getChecksums(result!.flushed_op, ['global[]']);
|
|
559
|
+
await bucketStorage.getChecksums(result!.flushed_op, bucketRequests(syncRules, ['global[]']));
|
|
570
560
|
const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
571
561
|
await batch.save({
|
|
572
562
|
sourceTable: TEST_TABLE,
|
|
@@ -588,12 +578,72 @@ bucket_definitions:
|
|
|
588
578
|
});
|
|
589
579
|
|
|
590
580
|
const checkpoint2 = result2!.flushed_op;
|
|
581
|
+
const request = bucketRequest(syncRules, 'global[]');
|
|
591
582
|
// Check that the checksum was correctly updated with the clear operation after having a cached checksum
|
|
592
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
583
|
+
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
584
|
+
const globalChecksum = checksumAfter.get(request.bucket);
|
|
585
|
+
expect(globalChecksum).toMatchObject({
|
|
586
|
+
bucket: request.bucket,
|
|
587
|
+
count: 1
|
|
588
|
+
});
|
|
589
|
+
// storage-specific checksum - just check that it does not change
|
|
590
|
+
expect(globalChecksum).toMatchSnapshot();
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test('defaults maxOpId to current checkpoint', async () => {
|
|
594
|
+
await using factory = await generateStorageFactory();
|
|
595
|
+
const syncRules = await factory.updateSyncRules(
|
|
596
|
+
updateSyncRulesFromYaml(`
|
|
597
|
+
bucket_definitions:
|
|
598
|
+
global:
|
|
599
|
+
data: [select * from test]
|
|
600
|
+
`)
|
|
601
|
+
);
|
|
602
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
603
|
+
|
|
604
|
+
const result1 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
605
|
+
await batch.markAllSnapshotDone('1/1');
|
|
606
|
+
await batch.save({
|
|
607
|
+
sourceTable: TEST_TABLE,
|
|
608
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
609
|
+
after: { id: 't1' },
|
|
610
|
+
afterReplicaId: test_utils.rid('t1')
|
|
611
|
+
});
|
|
612
|
+
await batch.commit('1/1');
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
const checkpoint1 = result1!.flushed_op;
|
|
616
|
+
|
|
617
|
+
const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
618
|
+
// This is flushed but not committed (does not advance the checkpoint)
|
|
619
|
+
await batch.save({
|
|
620
|
+
sourceTable: TEST_TABLE,
|
|
621
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
622
|
+
after: { id: 't1' },
|
|
623
|
+
afterReplicaId: test_utils.rid('t1')
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
const checkpoint2 = result2!.flushed_op;
|
|
627
|
+
|
|
628
|
+
const checkpointBeforeCompact = await bucketStorage.getCheckpoint();
|
|
629
|
+
expect(checkpointBeforeCompact.checkpoint).toEqual(checkpoint1);
|
|
630
|
+
|
|
631
|
+
// With default options, Postgres compaction should use the active checkpoint.
|
|
632
|
+
await bucketStorage.compact({
|
|
633
|
+
moveBatchLimit: 1,
|
|
634
|
+
moveBatchQueryLimit: 1,
|
|
635
|
+
minBucketChanges: 1,
|
|
636
|
+
minChangeRatio: 0
|
|
597
637
|
});
|
|
638
|
+
|
|
639
|
+
const batchAfterDefaultCompact = await test_utils.oneFromAsync(
|
|
640
|
+
bucketStorage.getBucketDataBatch(checkpoint2, bucketRequestMap(syncRules, [['global[]', 0n]]))
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
// Operation 1 should remain a PUT because op_id=2 is above the default maxOpId checkpoint.
|
|
644
|
+
expect(batchAfterDefaultCompact.chunkData.data).toMatchObject([
|
|
645
|
+
{ op_id: '1', op: 'PUT', object_id: 't1' },
|
|
646
|
+
{ op_id: '2', op: 'PUT', object_id: 't1' }
|
|
647
|
+
]);
|
|
598
648
|
});
|
|
599
649
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { storage } from '@powersync/service-core';
|
|
1
|
+
import { storage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
2
2
|
import { expect, test } from 'vitest';
|
|
3
3
|
import * as test_utils from '../test-utils/test-utils-index.js';
|
|
4
4
|
|
|
@@ -12,17 +12,25 @@ import * as test_utils from '../test-utils/test-utils-index.js';
|
|
|
12
12
|
*
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
|
-
export function registerDataStorageCheckpointTests(
|
|
15
|
+
export function registerDataStorageCheckpointTests(config: storage.TestStorageConfig) {
|
|
16
|
+
const generateStorageFactory = config.factory;
|
|
17
|
+
const storageVersion = config.storageVersion;
|
|
18
|
+
|
|
16
19
|
test('managed write checkpoints - checkpoint after write', async (context) => {
|
|
17
20
|
await using factory = await generateStorageFactory();
|
|
18
|
-
const r = await factory.configureSyncRules(
|
|
19
|
-
|
|
21
|
+
const r = await factory.configureSyncRules(
|
|
22
|
+
updateSyncRulesFromYaml(
|
|
23
|
+
`
|
|
20
24
|
bucket_definitions:
|
|
21
25
|
mybucket:
|
|
22
26
|
data: []
|
|
23
27
|
`,
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
{
|
|
29
|
+
validate: false,
|
|
30
|
+
storageVersion
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
);
|
|
26
34
|
const bucketStorage = factory.getInstance(r.persisted_sync_rules!);
|
|
27
35
|
|
|
28
36
|
const abortController = new AbortController();
|
|
@@ -31,6 +39,10 @@ bucket_definitions:
|
|
|
31
39
|
.watchCheckpointChanges({ user_id: 'user1', signal: abortController.signal })
|
|
32
40
|
[Symbol.asyncIterator]();
|
|
33
41
|
|
|
42
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
43
|
+
await batch.markAllSnapshotDone('1/1');
|
|
44
|
+
});
|
|
45
|
+
|
|
34
46
|
const writeCheckpoint = await bucketStorage.createManagedWriteCheckpoint({
|
|
35
47
|
heads: { '1': '5/0' },
|
|
36
48
|
user_id: 'user1'
|
|
@@ -55,16 +67,25 @@ bucket_definitions:
|
|
|
55
67
|
|
|
56
68
|
test('managed write checkpoints - write after checkpoint', async (context) => {
|
|
57
69
|
await using factory = await generateStorageFactory();
|
|
58
|
-
const r = await factory.configureSyncRules(
|
|
59
|
-
|
|
70
|
+
const r = await factory.configureSyncRules(
|
|
71
|
+
updateSyncRulesFromYaml(
|
|
72
|
+
`
|
|
60
73
|
bucket_definitions:
|
|
61
74
|
mybucket:
|
|
62
75
|
data: []
|
|
63
76
|
`,
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
{
|
|
78
|
+
validate: false,
|
|
79
|
+
storageVersion
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
);
|
|
66
83
|
const bucketStorage = factory.getInstance(r.persisted_sync_rules!);
|
|
67
84
|
|
|
85
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
86
|
+
await batch.markAllSnapshotDone('1/1');
|
|
87
|
+
});
|
|
88
|
+
|
|
68
89
|
const abortController = new AbortController();
|
|
69
90
|
context.onTestFinished(() => abortController.abort());
|
|
70
91
|
const iter = bucketStorage
|
|
@@ -117,17 +138,26 @@ bucket_definitions:
|
|
|
117
138
|
|
|
118
139
|
test('custom write checkpoints - checkpoint after write', async (context) => {
|
|
119
140
|
await using factory = await generateStorageFactory();
|
|
120
|
-
const r = await factory.configureSyncRules(
|
|
121
|
-
|
|
141
|
+
const r = await factory.configureSyncRules(
|
|
142
|
+
updateSyncRulesFromYaml(
|
|
143
|
+
`
|
|
122
144
|
bucket_definitions:
|
|
123
145
|
mybucket:
|
|
124
146
|
data: []
|
|
125
147
|
`,
|
|
126
|
-
|
|
127
|
-
|
|
148
|
+
{
|
|
149
|
+
validate: false,
|
|
150
|
+
storageVersion
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
);
|
|
128
154
|
const bucketStorage = factory.getInstance(r.persisted_sync_rules!);
|
|
129
155
|
bucketStorage.setWriteCheckpointMode(storage.WriteCheckpointMode.CUSTOM);
|
|
130
156
|
|
|
157
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
158
|
+
await batch.markAllSnapshotDone('1/1');
|
|
159
|
+
});
|
|
160
|
+
|
|
131
161
|
const abortController = new AbortController();
|
|
132
162
|
context.onTestFinished(() => abortController.abort());
|
|
133
163
|
const iter = bucketStorage
|
|
@@ -157,17 +187,26 @@ bucket_definitions:
|
|
|
157
187
|
|
|
158
188
|
test('custom write checkpoints - standalone checkpoint', async (context) => {
|
|
159
189
|
await using factory = await generateStorageFactory();
|
|
160
|
-
const r = await factory.configureSyncRules(
|
|
161
|
-
|
|
190
|
+
const r = await factory.configureSyncRules(
|
|
191
|
+
updateSyncRulesFromYaml(
|
|
192
|
+
`
|
|
162
193
|
bucket_definitions:
|
|
163
194
|
mybucket:
|
|
164
195
|
data: []
|
|
165
196
|
`,
|
|
166
|
-
|
|
167
|
-
|
|
197
|
+
{
|
|
198
|
+
validate: false,
|
|
199
|
+
storageVersion
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
);
|
|
168
203
|
const bucketStorage = factory.getInstance(r.persisted_sync_rules!);
|
|
169
204
|
bucketStorage.setWriteCheckpointMode(storage.WriteCheckpointMode.CUSTOM);
|
|
170
205
|
|
|
206
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
207
|
+
await batch.markAllSnapshotDone('1/1');
|
|
208
|
+
});
|
|
209
|
+
|
|
171
210
|
const abortController = new AbortController();
|
|
172
211
|
context.onTestFinished(() => abortController.abort());
|
|
173
212
|
const iter = bucketStorage
|
|
@@ -200,17 +239,26 @@ bucket_definitions:
|
|
|
200
239
|
|
|
201
240
|
test('custom write checkpoints - write after checkpoint', async (context) => {
|
|
202
241
|
await using factory = await generateStorageFactory();
|
|
203
|
-
const r = await factory.configureSyncRules(
|
|
204
|
-
|
|
242
|
+
const r = await factory.configureSyncRules(
|
|
243
|
+
updateSyncRulesFromYaml(
|
|
244
|
+
`
|
|
205
245
|
bucket_definitions:
|
|
206
246
|
mybucket:
|
|
207
247
|
data: []
|
|
208
248
|
`,
|
|
209
|
-
|
|
210
|
-
|
|
249
|
+
{
|
|
250
|
+
validate: false,
|
|
251
|
+
storageVersion
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
);
|
|
211
255
|
const bucketStorage = factory.getInstance(r.persisted_sync_rules!);
|
|
212
256
|
bucketStorage.setWriteCheckpointMode(storage.WriteCheckpointMode.CUSTOM);
|
|
213
257
|
|
|
258
|
+
await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
|
|
259
|
+
await batch.markAllSnapshotDone('1/1');
|
|
260
|
+
});
|
|
261
|
+
|
|
214
262
|
const abortController = new AbortController();
|
|
215
263
|
context.onTestFinished(() => abortController.abort());
|
|
216
264
|
const iter = bucketStorage
|