@powersync/service-core 1.20.0 → 1.20.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 +16 -0
- package/dist/routes/endpoints/admin.js +1 -0
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +6 -1
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/storage/BucketStorageBatch.d.ts +21 -8
- package/dist/storage/BucketStorageBatch.js.map +1 -1
- package/dist/storage/BucketStorageFactory.d.ts +5 -0
- package/dist/storage/ChecksumCache.d.ts +5 -2
- package/dist/storage/ChecksumCache.js +8 -4
- package/dist/storage/ChecksumCache.js.map +1 -1
- package/dist/storage/PersistedSyncRulesContent.d.ts +6 -2
- package/dist/storage/PersistedSyncRulesContent.js +2 -1
- package/dist/storage/PersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/SourceTable.d.ts +7 -2
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/StorageVersionConfig.d.ts +33 -0
- package/dist/storage/StorageVersionConfig.js +39 -6
- package/dist/storage/StorageVersionConfig.js.map +1 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +12 -3
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/sync/BucketChecksumState.d.ts +3 -3
- package/dist/sync/BucketChecksumState.js +12 -42
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.d.ts +1 -0
- package/dist/sync/util.js +10 -0
- package/dist/sync/util.js.map +1 -1
- package/package.json +4 -4
- package/src/routes/endpoints/admin.ts +1 -0
- package/src/routes/endpoints/sync-stream.ts +6 -1
- package/src/storage/BucketStorageBatch.ts +23 -9
- package/src/storage/BucketStorageFactory.ts +6 -0
- package/src/storage/ChecksumCache.ts +14 -6
- package/src/storage/PersistedSyncRulesContent.ts +7 -2
- package/src/storage/SourceTable.ts +7 -1
- package/src/storage/StorageVersionConfig.ts +54 -6
- package/src/storage/SyncRulesBucketStorage.ts +18 -3
- package/src/sync/BucketChecksumState.ts +18 -49
- package/src/sync/sync.ts +9 -3
- package/src/sync/util.ts +10 -0
- package/test/src/checksum_cache.test.ts +102 -57
- package/test/src/sync/BucketChecksumState.test.ts +53 -21
- package/test/src/utils.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -14,15 +14,43 @@ import {
|
|
|
14
14
|
} from '@/index.js';
|
|
15
15
|
import { JSONBig } from '@powersync/service-jsonbig';
|
|
16
16
|
import {
|
|
17
|
+
ParameterIndexLookupCreator,
|
|
17
18
|
RequestJwtPayload,
|
|
18
19
|
ScopedParameterLookup,
|
|
19
20
|
SqliteJsonRow,
|
|
21
|
+
SqliteRow,
|
|
20
22
|
SqlSyncRules,
|
|
23
|
+
TablePattern,
|
|
24
|
+
SourceTableInterface,
|
|
21
25
|
versionedHydrationState
|
|
22
26
|
} from '@powersync/service-sync-rules';
|
|
27
|
+
import { ParameterLookupScope } from '@powersync/service-sync-rules/src/HydrationState.js';
|
|
23
28
|
import { beforeEach, describe, expect, test } from 'vitest';
|
|
24
29
|
|
|
25
30
|
describe('BucketChecksumState', () => {
|
|
31
|
+
const LOOKUP_SOURCE: ParameterIndexLookupCreator = {
|
|
32
|
+
get defaultLookupScope(): ParameterLookupScope {
|
|
33
|
+
return {
|
|
34
|
+
lookupName: 'lookup',
|
|
35
|
+
queryId: '0',
|
|
36
|
+
source: LOOKUP_SOURCE
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
getSourceTables(): Set<TablePattern> {
|
|
40
|
+
return new Set();
|
|
41
|
+
},
|
|
42
|
+
evaluateParameterRow(_sourceTable: SourceTableInterface, _row: SqliteRow) {
|
|
43
|
+
return [];
|
|
44
|
+
},
|
|
45
|
+
tableSyncsParameters(_table: SourceTableInterface): boolean {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function lookupScope(lookupName: string, queryId: string): ParameterLookupScope {
|
|
51
|
+
return { lookupName, queryId, source: LOOKUP_SOURCE };
|
|
52
|
+
}
|
|
53
|
+
|
|
26
54
|
// Single global[] bucket.
|
|
27
55
|
// We don't care about data in these tests
|
|
28
56
|
const SYNC_RULES_GLOBAL = SqlSyncRules.fromYaml(
|
|
@@ -67,6 +95,10 @@ bucket_definitions:
|
|
|
67
95
|
const syncRequest: StreamingSyncRequest = {};
|
|
68
96
|
const tokenPayload = new JwtPayload({ sub: '' });
|
|
69
97
|
|
|
98
|
+
function bucketStarts(requests: { bucket: string; start: InternalOpId }[]) {
|
|
99
|
+
return new Map(requests.map((request) => [request.bucket, request.start]));
|
|
100
|
+
}
|
|
101
|
+
|
|
70
102
|
test('global bucket with update', async () => {
|
|
71
103
|
const storage = new MockBucketChecksumStateStorage();
|
|
72
104
|
// Set intial state
|
|
@@ -94,14 +126,14 @@ bucket_definitions:
|
|
|
94
126
|
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
95
127
|
}
|
|
96
128
|
});
|
|
97
|
-
expect(line.bucketsToFetch).
|
|
129
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
98
130
|
{
|
|
99
131
|
bucket: '1#global[]',
|
|
100
132
|
priority: 3
|
|
101
133
|
}
|
|
102
134
|
]);
|
|
103
135
|
// This is the bucket data to be fetched
|
|
104
|
-
expect(line.getFilteredBucketPositions()).toEqual(new Map([['1#global[]', 0n]]));
|
|
136
|
+
expect(bucketStarts(line.getFilteredBucketPositions())).toEqual(new Map([['1#global[]', 0n]]));
|
|
105
137
|
|
|
106
138
|
// This similuates the bucket data being sent
|
|
107
139
|
line.advance();
|
|
@@ -132,7 +164,7 @@ bucket_definitions:
|
|
|
132
164
|
write_checkpoint: undefined
|
|
133
165
|
}
|
|
134
166
|
});
|
|
135
|
-
expect(line2.getFilteredBucketPositions()).toEqual(new Map([['1#global[]', 1n]]));
|
|
167
|
+
expect(bucketStarts(line2.getFilteredBucketPositions())).toEqual(new Map([['1#global[]', 1n]]));
|
|
136
168
|
});
|
|
137
169
|
|
|
138
170
|
test('global bucket with initial state', async () => {
|
|
@@ -166,14 +198,14 @@ bucket_definitions:
|
|
|
166
198
|
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
167
199
|
}
|
|
168
200
|
});
|
|
169
|
-
expect(line.bucketsToFetch).
|
|
201
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
170
202
|
{
|
|
171
203
|
bucket: '1#global[]',
|
|
172
204
|
priority: 3
|
|
173
205
|
}
|
|
174
206
|
]);
|
|
175
207
|
// This is the main difference between this and the previous test
|
|
176
|
-
expect(line.getFilteredBucketPositions()).toEqual(new Map([['1#global[]', 1n]]));
|
|
208
|
+
expect(bucketStarts(line.getFilteredBucketPositions())).toEqual(new Map([['1#global[]', 1n]]));
|
|
177
209
|
});
|
|
178
210
|
|
|
179
211
|
test('multiple static buckets', async () => {
|
|
@@ -206,7 +238,7 @@ bucket_definitions:
|
|
|
206
238
|
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
207
239
|
}
|
|
208
240
|
});
|
|
209
|
-
expect(line.bucketsToFetch).
|
|
241
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
210
242
|
{
|
|
211
243
|
bucket: '2#global[1]',
|
|
212
244
|
priority: 3
|
|
@@ -274,13 +306,13 @@ bucket_definitions:
|
|
|
274
306
|
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
275
307
|
}
|
|
276
308
|
});
|
|
277
|
-
expect(line.bucketsToFetch).
|
|
309
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
278
310
|
{
|
|
279
311
|
bucket: '1#global[]',
|
|
280
312
|
priority: 3
|
|
281
313
|
}
|
|
282
314
|
]);
|
|
283
|
-
expect(line.getFilteredBucketPositions()).toEqual(new Map([['1#global[]', 0n]]));
|
|
315
|
+
expect(bucketStarts(line.getFilteredBucketPositions())).toEqual(new Map([['1#global[]', 0n]]));
|
|
284
316
|
});
|
|
285
317
|
|
|
286
318
|
test('invalidating individual bucket', async () => {
|
|
@@ -337,7 +369,7 @@ bucket_definitions:
|
|
|
337
369
|
write_checkpoint: undefined
|
|
338
370
|
}
|
|
339
371
|
});
|
|
340
|
-
expect(line2.bucketsToFetch).
|
|
372
|
+
expect(line2.bucketsToFetch).toMatchObject([{ bucket: '2#global[1]', priority: 3 }]);
|
|
341
373
|
});
|
|
342
374
|
|
|
343
375
|
test('invalidating all buckets', async () => {
|
|
@@ -387,7 +419,7 @@ bucket_definitions:
|
|
|
387
419
|
write_checkpoint: undefined
|
|
388
420
|
}
|
|
389
421
|
});
|
|
390
|
-
expect(line2.bucketsToFetch).
|
|
422
|
+
expect(line2.bucketsToFetch).toMatchObject([
|
|
391
423
|
{ bucket: '2#global[1]', priority: 3 },
|
|
392
424
|
{ bucket: '2#global[2]', priority: 3 }
|
|
393
425
|
]);
|
|
@@ -424,7 +456,7 @@ bucket_definitions:
|
|
|
424
456
|
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
425
457
|
}
|
|
426
458
|
});
|
|
427
|
-
expect(line.bucketsToFetch).
|
|
459
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
428
460
|
{
|
|
429
461
|
bucket: '2#global[1]',
|
|
430
462
|
priority: 3
|
|
@@ -436,7 +468,7 @@ bucket_definitions:
|
|
|
436
468
|
]);
|
|
437
469
|
|
|
438
470
|
// This is the bucket data to be fetched
|
|
439
|
-
expect(line.getFilteredBucketPositions()).toEqual(
|
|
471
|
+
expect(bucketStarts(line.getFilteredBucketPositions())).toEqual(
|
|
440
472
|
new Map([
|
|
441
473
|
['2#global[1]', 0n],
|
|
442
474
|
['2#global[2]', 0n]
|
|
@@ -477,7 +509,7 @@ bucket_definitions:
|
|
|
477
509
|
}
|
|
478
510
|
});
|
|
479
511
|
// This should contain both buckets, even though only one changed.
|
|
480
|
-
expect(line2.bucketsToFetch).
|
|
512
|
+
expect(line2.bucketsToFetch).toMatchObject([
|
|
481
513
|
{
|
|
482
514
|
bucket: '2#global[1]',
|
|
483
515
|
priority: 3
|
|
@@ -488,7 +520,7 @@ bucket_definitions:
|
|
|
488
520
|
}
|
|
489
521
|
]);
|
|
490
522
|
|
|
491
|
-
expect(line2.getFilteredBucketPositions()).toEqual(
|
|
523
|
+
expect(bucketStarts(line2.getFilteredBucketPositions())).toEqual(
|
|
492
524
|
new Map([
|
|
493
525
|
['2#global[1]', 3n],
|
|
494
526
|
['2#global[2]', 1n]
|
|
@@ -513,7 +545,7 @@ bucket_definitions:
|
|
|
513
545
|
|
|
514
546
|
const line = (await state.buildNextCheckpointLine({
|
|
515
547
|
base: storage.makeCheckpoint(1n, (lookups) => {
|
|
516
|
-
expect(lookups).toEqual([ScopedParameterLookup.direct(
|
|
548
|
+
expect(lookups).toEqual([ScopedParameterLookup.direct(lookupScope('by_project', '1'), ['u1'])]);
|
|
517
549
|
return [{ id: 1 }, { id: 2 }];
|
|
518
550
|
}),
|
|
519
551
|
writeCheckpoint: null,
|
|
@@ -548,7 +580,7 @@ bucket_definitions:
|
|
|
548
580
|
write_checkpoint: undefined
|
|
549
581
|
}
|
|
550
582
|
});
|
|
551
|
-
expect(line.bucketsToFetch).
|
|
583
|
+
expect(line.bucketsToFetch).toMatchObject([
|
|
552
584
|
{
|
|
553
585
|
bucket: '3#by_project[1]',
|
|
554
586
|
priority: 3
|
|
@@ -560,7 +592,7 @@ bucket_definitions:
|
|
|
560
592
|
]);
|
|
561
593
|
line.advance();
|
|
562
594
|
// This is the bucket data to be fetched
|
|
563
|
-
expect(line.getFilteredBucketPositions()).toEqual(
|
|
595
|
+
expect(bucketStarts(line.getFilteredBucketPositions())).toEqual(
|
|
564
596
|
new Map([
|
|
565
597
|
['3#by_project[1]', 0n],
|
|
566
598
|
['3#by_project[2]', 0n]
|
|
@@ -574,7 +606,7 @@ bucket_definitions:
|
|
|
574
606
|
// Now we get a new line
|
|
575
607
|
const line2 = (await state.buildNextCheckpointLine({
|
|
576
608
|
base: storage.makeCheckpoint(2n, (lookups) => {
|
|
577
|
-
expect(lookups).toEqual([ScopedParameterLookup.direct(
|
|
609
|
+
expect(lookups).toEqual([ScopedParameterLookup.direct(lookupScope('by_project', '1'), ['u1'])]);
|
|
578
610
|
return [{ id: 1 }, { id: 2 }, { id: 3 }];
|
|
579
611
|
}),
|
|
580
612
|
writeCheckpoint: null,
|
|
@@ -602,7 +634,7 @@ bucket_definitions:
|
|
|
602
634
|
write_checkpoint: undefined
|
|
603
635
|
}
|
|
604
636
|
});
|
|
605
|
-
expect(line2.getFilteredBucketPositions()).toEqual(new Map([['3#by_project[3]', 0n]]));
|
|
637
|
+
expect(bucketStarts(line2.getFilteredBucketPositions())).toEqual(new Map([['3#by_project[3]', 0n]]));
|
|
606
638
|
});
|
|
607
639
|
|
|
608
640
|
describe('streams', () => {
|
|
@@ -1044,9 +1076,9 @@ class MockBucketChecksumStateStorage implements BucketChecksumStateStorage {
|
|
|
1044
1076
|
this.filter?.({ invalidate: true });
|
|
1045
1077
|
}
|
|
1046
1078
|
|
|
1047
|
-
async getChecksums(
|
|
1079
|
+
async getChecksums(_checkpoint: InternalOpId, buckets: { bucket: string }[]): Promise<ChecksumMap> {
|
|
1048
1080
|
return new Map<string, BucketChecksum>(
|
|
1049
|
-
buckets.map((bucket) => {
|
|
1081
|
+
buckets.map(({ bucket }) => {
|
|
1050
1082
|
const checksum = this.state.get(bucket);
|
|
1051
1083
|
return [
|
|
1052
1084
|
bucket,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes the source property from an object.
|
|
3
|
+
*
|
|
4
|
+
* This is for tests where we don't care about this value, and it adds a lot of noise in the output.
|
|
5
|
+
*/
|
|
6
|
+
export function removeSource<T extends { source?: any }>(obj: T): Omit<T, 'source'> {
|
|
7
|
+
const { source, ...rest } = obj;
|
|
8
|
+
return rest;
|
|
9
|
+
}
|