@powersync/service-core 1.20.5 → 1.21.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 +35 -0
- package/dist/api/RouteAPI.d.ts +3 -3
- package/dist/api/diagnostics.d.ts +1 -1
- package/dist/api/diagnostics.js +18 -2
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/entry/commands/teardown-action.js +1 -1
- package/dist/entry/commands/teardown-action.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/AbstractModule.d.ts +1 -1
- package/dist/replication/AbstractReplicationJob.js +1 -1
- package/dist/replication/AbstractReplicationJob.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +6 -6
- package/dist/replication/AbstractReplicator.js +21 -21
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/routes/endpoints/admin.js +7 -3
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.js +1 -1
- package/dist/routes/endpoints/checkpointing.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +1 -1
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +7 -7
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +2 -2
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/runner/teardown.js +4 -4
- package/dist/runner/teardown.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +9 -9
- package/dist/storage/BucketStorage.js +9 -9
- package/dist/storage/BucketStorageFactory.d.ts +23 -18
- package/dist/storage/BucketStorageFactory.js +12 -11
- package/dist/storage/BucketStorageFactory.js.map +1 -1
- package/dist/storage/PersistedSyncRulesContent.d.ts +3 -1
- package/dist/storage/PersistedSyncRulesContent.js +10 -3
- package/dist/storage/PersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/SourceTable.d.ts +3 -3
- package/dist/storage/SourceTable.js +3 -3
- package/dist/storage/StorageVersionConfig.d.ts +1 -1
- package/dist/storage/StorageVersionConfig.js +1 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +38 -6
- package/dist/storage/SyncRulesBucketStorage.js +14 -0
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/storage/WriteCheckpointAPI.d.ts +6 -6
- package/dist/storage/WriteCheckpointAPI.js +1 -1
- package/dist/storage/bson.d.ts +0 -1
- package/dist/storage/bson.js +0 -4
- package/dist/storage/bson.js.map +1 -1
- package/dist/sync/BucketChecksumState.d.ts +2 -5
- package/dist/sync/BucketChecksumState.js +116 -57
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/tracing/PerformanceTracer.d.ts +44 -0
- package/dist/tracing/PerformanceTracer.js +102 -0
- package/dist/tracing/PerformanceTracer.js.map +1 -0
- package/dist/tracing/TraceWriter.d.ts +22 -0
- package/dist/tracing/TraceWriter.js +63 -0
- package/dist/tracing/TraceWriter.js.map +1 -0
- package/dist/util/checkpointing.js +1 -1
- package/dist/util/config/compound-config-collector.d.ts +1 -1
- package/dist/util/config/compound-config-collector.js +2 -2
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -1
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
- package/dist/util/env.js +1 -1
- package/dist/util/protocol-types.d.ts +1 -1
- package/dist/util/protocol-types.js +1 -1
- package/package.json +11 -11
- package/src/api/RouteAPI.ts +3 -3
- package/src/api/diagnostics.ts +26 -3
- package/src/entry/commands/teardown-action.ts +1 -1
- package/src/index.ts +2 -0
- package/src/modules/AbstractModule.ts +1 -1
- package/src/replication/AbstractReplicationJob.ts +1 -1
- package/src/replication/AbstractReplicator.ts +23 -23
- package/src/routes/endpoints/admin.ts +7 -3
- package/src/routes/endpoints/checkpointing.ts +1 -1
- package/src/routes/endpoints/socket-route.ts +1 -1
- package/src/routes/endpoints/sync-rules.ts +7 -7
- package/src/routes/endpoints/sync-stream.ts +2 -2
- package/src/runner/teardown.ts +4 -4
- package/src/storage/BucketStorage.ts +9 -9
- package/src/storage/BucketStorageFactory.ts +29 -22
- package/src/storage/PersistedSyncRulesContent.ts +12 -4
- package/src/storage/SourceTable.ts +3 -3
- package/src/storage/StorageVersionConfig.ts +1 -1
- package/src/storage/SyncRulesBucketStorage.ts +46 -7
- package/src/storage/WriteCheckpointAPI.ts +6 -6
- package/src/storage/bson.ts +0 -5
- package/src/sync/BucketChecksumState.ts +137 -73
- package/src/sync/sync.ts +1 -1
- package/src/tracing/PerformanceTracer.ts +126 -0
- package/src/tracing/TraceWriter.ts +67 -0
- package/src/util/checkpointing.ts +1 -1
- package/src/util/config/compound-config-collector.ts +3 -3
- package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -1
- package/src/util/env.ts +1 -1
- package/src/util/protocol-types.ts +1 -1
- package/test/src/auth.test.ts +109 -1
- package/test/src/diagnostics.test.ts +151 -0
- package/test/src/sync/BucketChecksumState.test.ts +221 -65
- package/test/tsconfig.json +0 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ChecksumMap,
|
|
8
8
|
InternalOpId,
|
|
9
9
|
JwtPayload,
|
|
10
|
+
ParameterSetLimitExceededError,
|
|
10
11
|
ReplicationCheckpoint,
|
|
11
12
|
StreamingSyncRequest,
|
|
12
13
|
SyncContext,
|
|
@@ -15,9 +16,9 @@ import {
|
|
|
15
16
|
import { JSONBig } from '@powersync/service-jsonbig';
|
|
16
17
|
import {
|
|
17
18
|
ParameterIndexLookupCreator,
|
|
19
|
+
ParameterLookupRows,
|
|
18
20
|
ScopedParameterLookup,
|
|
19
21
|
SourceTableInterface,
|
|
20
|
-
SqliteJsonRow,
|
|
21
22
|
SqliteRow,
|
|
22
23
|
SqlSyncRules,
|
|
23
24
|
TablePattern,
|
|
@@ -545,7 +546,7 @@ bucket_definitions:
|
|
|
545
546
|
const line = (await state.buildNextCheckpointLine({
|
|
546
547
|
base: storage.makeCheckpoint(1n, (lookups) => {
|
|
547
548
|
expect(lookups).toEqual([ScopedParameterLookup.direct(lookupScope('by_project', '1'), ['u1'])]);
|
|
548
|
-
return [{ id: 1 }, { id: 2 }];
|
|
549
|
+
return [{ lookup: lookups[0], rows: [{ id: 1 }, { id: 2 }] }];
|
|
549
550
|
}),
|
|
550
551
|
writeCheckpoint: null,
|
|
551
552
|
update: CHECKPOINT_INVALIDATE_ALL
|
|
@@ -606,7 +607,7 @@ bucket_definitions:
|
|
|
606
607
|
const line2 = (await state.buildNextCheckpointLine({
|
|
607
608
|
base: storage.makeCheckpoint(2n, (lookups) => {
|
|
608
609
|
expect(lookups).toEqual([ScopedParameterLookup.direct(lookupScope('by_project', '1'), ['u1'])]);
|
|
609
|
-
return [{ id: 1 }, { id: 2 }, { id: 3 }];
|
|
610
|
+
return [{ lookup: lookups[0], rows: [{ id: 1 }, { id: 2 }, { id: 3 }] }];
|
|
610
611
|
}),
|
|
611
612
|
writeCheckpoint: null,
|
|
612
613
|
update: {
|
|
@@ -889,7 +890,8 @@ config:
|
|
|
889
890
|
|
|
890
891
|
const line = (await state.buildNextCheckpointLine({
|
|
891
892
|
base: storage.makeCheckpoint(1n, (lookups) => {
|
|
892
|
-
|
|
893
|
+
expect(lookups).toHaveLength(1);
|
|
894
|
+
return [{ lookup: lookups[0], rows: [{ id: 1 }, { id: 2 }, { id: 3 }] }];
|
|
893
895
|
}),
|
|
894
896
|
writeCheckpoint: null,
|
|
895
897
|
update: CHECKPOINT_INVALIDATE_ALL
|
|
@@ -900,19 +902,192 @@ config:
|
|
|
900
902
|
expect(logData[0].parameter_query_results).toBe(3);
|
|
901
903
|
});
|
|
902
904
|
|
|
903
|
-
test('throws error with breakdown when
|
|
905
|
+
test('throws error with breakdown when too many parameters were fetched', async () => {
|
|
906
|
+
const syncRules = SqlSyncRules.fromYaml(
|
|
907
|
+
`
|
|
908
|
+
config:
|
|
909
|
+
edition: 3
|
|
910
|
+
|
|
911
|
+
streams:
|
|
912
|
+
a:
|
|
913
|
+
auto_subscribe: true
|
|
914
|
+
query: SELECT * FROM a WHERE id IN (SELECT id FROM magic_sequence WHERE count0 = auth.parameter('a'))
|
|
915
|
+
b:
|
|
916
|
+
auto_subscribe: true
|
|
917
|
+
query: SELECT * FROM a WHERE id IN (SELECT id FROM magic_sequence WHERE count1 = auth.parameter('b'))
|
|
918
|
+
c:
|
|
919
|
+
auto_subscribe: true
|
|
920
|
+
query: SELECT * FROM a WHERE id IN (SELECT id FROM magic_sequence WHERE count1 = auth.parameter('c'))
|
|
921
|
+
`,
|
|
922
|
+
{ defaultSchema: 'public' }
|
|
923
|
+
).config.hydrate({
|
|
924
|
+
hydrationState: versionedHydrationState(1)
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
const storage = new MockBucketChecksumStateStorage();
|
|
928
|
+
|
|
929
|
+
const errorMessages: string[] = [];
|
|
930
|
+
const errorData: any[] = [];
|
|
931
|
+
const mockLogger = {
|
|
932
|
+
info: () => {},
|
|
933
|
+
error: (message: string, data: any) => {
|
|
934
|
+
errorMessages.push(message);
|
|
935
|
+
errorData.push(data);
|
|
936
|
+
},
|
|
937
|
+
warn: () => {},
|
|
938
|
+
debug: () => {}
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
const smallContext = new SyncContext({
|
|
942
|
+
maxBuckets: 100,
|
|
943
|
+
maxParameterQueryResults: 55,
|
|
944
|
+
maxDataFetchConcurrency: 10
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
const state = new BucketChecksumState({
|
|
948
|
+
syncContext: smallContext,
|
|
949
|
+
tokenPayload: new JwtPayload({
|
|
950
|
+
sub: 'u1',
|
|
951
|
+
// Create 60 total results: 30 a + 20 b + 10 c
|
|
952
|
+
a: BigInt(30),
|
|
953
|
+
b: BigInt(20),
|
|
954
|
+
c: BigInt(10)
|
|
955
|
+
}),
|
|
956
|
+
syncRequest,
|
|
957
|
+
syncRules,
|
|
958
|
+
bucketStorage: storage,
|
|
959
|
+
logger: mockLogger as any
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
await expect(
|
|
963
|
+
state.buildNextCheckpointLine({
|
|
964
|
+
base: storage.makeCheckpoint(1n, (lookups, limit) => {
|
|
965
|
+
expect(lookups).toHaveLength(1);
|
|
966
|
+
const [{ values }] = lookups;
|
|
967
|
+
expect(values[0]).toStrictEqual('lookup');
|
|
968
|
+
|
|
969
|
+
const count = Number(values[2]); // The count parameter from streams
|
|
970
|
+
if (count > limit) {
|
|
971
|
+
throw new ParameterSetLimitExceededError(limit);
|
|
972
|
+
}
|
|
973
|
+
return [{ lookup: lookups[0], rows: Array.from({ length: count }, (_, i) => ({ '0': BigInt(i) })) }];
|
|
974
|
+
}),
|
|
975
|
+
writeCheckpoint: null,
|
|
976
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
977
|
+
})
|
|
978
|
+
).rejects.toThrow('Too many parameter query results (limit of 55)');
|
|
979
|
+
|
|
980
|
+
// Verify error log includes breakdown
|
|
981
|
+
expect(errorMessages[0]).toContain('Invoked parameter queries by definition:');
|
|
982
|
+
expect(errorMessages[0]).toContain('Stream a evaluating parameter on magic_sequence: 30 results.');
|
|
983
|
+
expect(errorMessages[0]).toContain('Stream b evaluating parameter on magic_sequence: 20 results.');
|
|
984
|
+
expect(errorMessages[0]).toContain(
|
|
985
|
+
'Stream c evaluating parameter on magic_sequence exceeded the remaining limit of 5 available results.'
|
|
986
|
+
);
|
|
987
|
+
|
|
988
|
+
expect(errorData[0].checkpoint).toEqual(1n);
|
|
989
|
+
expect(errorData[0].parameterResults).toEqual([
|
|
990
|
+
{
|
|
991
|
+
definition: 'Stream a evaluating parameter on magic_sequence',
|
|
992
|
+
didExceedLimit: false,
|
|
993
|
+
resultsOrLimit: 30
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
definition: 'Stream b evaluating parameter on magic_sequence',
|
|
997
|
+
didExceedLimit: false,
|
|
998
|
+
resultsOrLimit: 20
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
definition: 'Stream c evaluating parameter on magic_sequence',
|
|
1002
|
+
didExceedLimit: true,
|
|
1003
|
+
resultsOrLimit: 5
|
|
1004
|
+
}
|
|
1005
|
+
]);
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
test('throws error when too many lookups are requested at once', async () => {
|
|
1009
|
+
const syncRules = SqlSyncRules.fromYaml(
|
|
1010
|
+
`
|
|
1011
|
+
config:
|
|
1012
|
+
edition: 3
|
|
1013
|
+
|
|
1014
|
+
streams:
|
|
1015
|
+
a:
|
|
1016
|
+
auto_subscribe: true
|
|
1017
|
+
query: SELECT * FROM a WHERE x IN (SELECT x FROM b WHERE y IN auth.parameter('p'))
|
|
1018
|
+
`,
|
|
1019
|
+
{ defaultSchema: 'public' }
|
|
1020
|
+
).config.hydrate({
|
|
1021
|
+
hydrationState: versionedHydrationState(1)
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
const storage = new MockBucketChecksumStateStorage();
|
|
1025
|
+
|
|
1026
|
+
const errorData: any[] = [];
|
|
1027
|
+
const mockLogger = {
|
|
1028
|
+
info: () => {},
|
|
1029
|
+
error: (_message: string, data: any) => {
|
|
1030
|
+
errorData.push(data);
|
|
1031
|
+
},
|
|
1032
|
+
warn: () => {},
|
|
1033
|
+
debug: () => {}
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const smallContext = new SyncContext({
|
|
1037
|
+
maxBuckets: 100,
|
|
1038
|
+
maxParameterQueryResults: 10,
|
|
1039
|
+
maxDataFetchConcurrency: 10
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
const state = new BucketChecksumState({
|
|
1043
|
+
syncContext: smallContext,
|
|
1044
|
+
tokenPayload: new JwtPayload({
|
|
1045
|
+
sub: 'u1',
|
|
1046
|
+
p: Array.from({ length: 100 }, (_, i) => i)
|
|
1047
|
+
}),
|
|
1048
|
+
syncRequest,
|
|
1049
|
+
syncRules,
|
|
1050
|
+
bucketStorage: storage,
|
|
1051
|
+
logger: mockLogger as any
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
await expect(
|
|
1055
|
+
state.buildNextCheckpointLine({
|
|
1056
|
+
base: storage.makeCheckpoint(1n, () => {
|
|
1057
|
+
throw new Error('should not get called');
|
|
1058
|
+
}),
|
|
1059
|
+
writeCheckpoint: null,
|
|
1060
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
1061
|
+
})
|
|
1062
|
+
).rejects.toThrow('Attempted to fetch 100 lookups at once, a maximum of 10 lookups are allowed');
|
|
1063
|
+
|
|
1064
|
+
expect(errorData).toStrictEqual([
|
|
1065
|
+
{
|
|
1066
|
+
user_id: 'u1',
|
|
1067
|
+
checkpoint: 1n,
|
|
1068
|
+
cause: 'Stream a evaluating parameter on b'
|
|
1069
|
+
}
|
|
1070
|
+
]);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
test('throws error with breakdown when bucket limit is exceeded', async () => {
|
|
1074
|
+
// These streams are designed to return buckets without consuming too many parameters (as exceeding that limit is
|
|
1075
|
+
// a different error).
|
|
904
1076
|
const SYNC_RULES_MULTI = SqlSyncRules.fromYaml(
|
|
905
1077
|
`
|
|
906
|
-
|
|
1078
|
+
config:
|
|
1079
|
+
edition: 3
|
|
1080
|
+
|
|
1081
|
+
streams:
|
|
907
1082
|
projects:
|
|
908
|
-
|
|
909
|
-
|
|
1083
|
+
auto_subscribe: true
|
|
1084
|
+
query: SELECT id FROM projects WHERE p IN auth.parameter('a')
|
|
910
1085
|
tasks:
|
|
911
|
-
|
|
912
|
-
|
|
1086
|
+
auto_subscribe: true
|
|
1087
|
+
query: SELECT id FROM tasks WHERE p IN auth.parameter('b')
|
|
913
1088
|
comments:
|
|
914
|
-
|
|
915
|
-
|
|
1089
|
+
auto_subscribe: true
|
|
1090
|
+
query: SELECT id FROM comments WHERE p IN auth.parameter('c')
|
|
916
1091
|
`,
|
|
917
1092
|
{ defaultSchema: 'public' }
|
|
918
1093
|
).config.hydrate({ hydrationState: versionedHydrationState(4) });
|
|
@@ -932,75 +1107,61 @@ bucket_definitions:
|
|
|
932
1107
|
};
|
|
933
1108
|
|
|
934
1109
|
const smallContext = new SyncContext({
|
|
935
|
-
maxBuckets:
|
|
936
|
-
maxParameterQueryResults:
|
|
1110
|
+
maxBuckets: 50,
|
|
1111
|
+
maxParameterQueryResults: 10,
|
|
937
1112
|
maxDataFetchConcurrency: 10
|
|
938
1113
|
});
|
|
939
1114
|
|
|
940
1115
|
const state = new BucketChecksumState({
|
|
941
1116
|
syncContext: smallContext,
|
|
942
|
-
tokenPayload: new JwtPayload({
|
|
1117
|
+
tokenPayload: new JwtPayload({
|
|
1118
|
+
sub: 'u1',
|
|
1119
|
+
// Create 60 total results: 30 projects + 20 tasks + 10 comments
|
|
1120
|
+
a: Array.from({ length: 30 }, (_, i) => BigInt(i)),
|
|
1121
|
+
b: Array.from({ length: 20 }, (_, i) => BigInt(i)),
|
|
1122
|
+
c: Array.from({ length: 10 }, (_, i) => BigInt(i))
|
|
1123
|
+
}),
|
|
943
1124
|
syncRequest,
|
|
944
1125
|
syncRules: SYNC_RULES_MULTI,
|
|
945
1126
|
bucketStorage: storage,
|
|
946
1127
|
logger: mockLogger as any
|
|
947
1128
|
});
|
|
948
1129
|
|
|
949
|
-
// Create 60 total results: 30 projects + 20 tasks + 10 comments
|
|
950
|
-
const projectIds = Array.from({ length: 30 }, (_, i) => ({ id: i + 1 }));
|
|
951
|
-
const taskIds = Array.from({ length: 20 }, (_, i) => ({ id: i + 1 }));
|
|
952
|
-
const commentIds = Array.from({ length: 10 }, (_, i) => ({ id: i + 1 }));
|
|
953
|
-
|
|
954
|
-
for (let i = 1; i <= 30; i++) {
|
|
955
|
-
storage.updateTestChecksum({ bucket: `projects[${i}]`, checksum: 1, count: 1 });
|
|
956
|
-
}
|
|
957
|
-
for (let i = 1; i <= 20; i++) {
|
|
958
|
-
storage.updateTestChecksum({ bucket: `tasks[${i}]`, checksum: 1, count: 1 });
|
|
959
|
-
}
|
|
960
|
-
for (let i = 1; i <= 10; i++) {
|
|
961
|
-
storage.updateTestChecksum({ bucket: `comments[${i}]`, checksum: 1, count: 1 });
|
|
962
|
-
}
|
|
963
|
-
|
|
964
1130
|
await expect(
|
|
965
1131
|
state.buildNextCheckpointLine({
|
|
966
|
-
base: storage.makeCheckpoint(1n,
|
|
967
|
-
const lookup = lookups[0];
|
|
968
|
-
const lookupName = lookup.values[0];
|
|
969
|
-
if (lookupName === 'projects') {
|
|
970
|
-
return projectIds;
|
|
971
|
-
} else if (lookupName === 'tasks') {
|
|
972
|
-
return taskIds;
|
|
973
|
-
} else {
|
|
974
|
-
return commentIds;
|
|
975
|
-
}
|
|
976
|
-
}),
|
|
1132
|
+
base: storage.makeCheckpoint(1n),
|
|
977
1133
|
writeCheckpoint: null,
|
|
978
1134
|
update: CHECKPOINT_INVALIDATE_ALL
|
|
979
1135
|
})
|
|
980
|
-
).rejects.toThrow('Too many
|
|
1136
|
+
).rejects.toThrow('Too many buckets: 60 (limit of 50');
|
|
981
1137
|
|
|
982
1138
|
// Verify error log includes breakdown
|
|
983
|
-
expect(errorMessages[0]).toContain('
|
|
1139
|
+
expect(errorMessages[0]).toContain('Buckets by definition:');
|
|
984
1140
|
expect(errorMessages[0]).toContain('projects: 30');
|
|
985
1141
|
expect(errorMessages[0]).toContain('tasks: 20');
|
|
986
1142
|
expect(errorMessages[0]).toContain('comments: 10');
|
|
987
1143
|
|
|
988
1144
|
expect(errorData[0].checkpoint).toEqual(1n);
|
|
989
|
-
expect(errorData[0].
|
|
990
|
-
expect(errorData[0].
|
|
1145
|
+
expect(errorData[0].buckets).toBe(60);
|
|
1146
|
+
expect(errorData[0].buckets_by_definition).toEqual({
|
|
991
1147
|
projects: 30,
|
|
992
1148
|
tasks: 20,
|
|
993
1149
|
comments: 10
|
|
994
1150
|
});
|
|
995
1151
|
});
|
|
996
1152
|
|
|
997
|
-
test('limits breakdown to top 10 definitions', async () => {
|
|
998
|
-
// Create sync
|
|
999
|
-
let yamlDefinitions =
|
|
1153
|
+
test('limits bucket breakdown to top 10 definitions', async () => {
|
|
1154
|
+
// Create sync streams with 15 different definitions with dynamic parameters
|
|
1155
|
+
let yamlDefinitions = `
|
|
1156
|
+
config:
|
|
1157
|
+
edition: 3
|
|
1158
|
+
|
|
1159
|
+
streams:
|
|
1160
|
+
`;
|
|
1000
1161
|
for (let i = 1; i <= 15; i++) {
|
|
1001
1162
|
yamlDefinitions += ` def${i}:\n`;
|
|
1002
|
-
yamlDefinitions += `
|
|
1003
|
-
yamlDefinitions += `
|
|
1163
|
+
yamlDefinitions += ` auto_subscribe: true\n`;
|
|
1164
|
+
yamlDefinitions += ` query: SELECT * FROM tbl WHERE b = auth.parameter('${i}')\n`;
|
|
1004
1165
|
}
|
|
1005
1166
|
|
|
1006
1167
|
const SYNC_RULES_MANY = SqlSyncRules.fromYaml(yamlDefinitions, { defaultSchema: 'public' }).config.hydrate({
|
|
@@ -1020,35 +1181,30 @@ bucket_definitions:
|
|
|
1020
1181
|
};
|
|
1021
1182
|
|
|
1022
1183
|
const smallContext = new SyncContext({
|
|
1023
|
-
maxBuckets:
|
|
1184
|
+
maxBuckets: 10,
|
|
1024
1185
|
maxParameterQueryResults: 10,
|
|
1025
1186
|
maxDataFetchConcurrency: 10
|
|
1026
1187
|
});
|
|
1027
1188
|
|
|
1028
1189
|
const state = new BucketChecksumState({
|
|
1029
1190
|
syncContext: smallContext,
|
|
1030
|
-
tokenPayload: new JwtPayload({
|
|
1191
|
+
tokenPayload: new JwtPayload({
|
|
1192
|
+
sub: 'u1',
|
|
1193
|
+
...Object.fromEntries(Array.from({ length: 30 }, (_, i) => [i.toString(), BigInt(i)]))
|
|
1194
|
+
}),
|
|
1031
1195
|
syncRequest,
|
|
1032
1196
|
syncRules: SYNC_RULES_MANY,
|
|
1033
1197
|
bucketStorage: storage,
|
|
1034
1198
|
logger: mockLogger as any
|
|
1035
1199
|
});
|
|
1036
1200
|
|
|
1037
|
-
// Each definition creates one bucket, total 15 buckets
|
|
1038
|
-
for (let i = 1; i <= 15; i++) {
|
|
1039
|
-
storage.updateTestChecksum({ bucket: `def${i}[${i}]`, checksum: 1, count: 1 });
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
1201
|
await expect(
|
|
1043
1202
|
state.buildNextCheckpointLine({
|
|
1044
|
-
base: storage.makeCheckpoint(1n,
|
|
1045
|
-
// Return one result for each definition
|
|
1046
|
-
return [{ id: 1 }];
|
|
1047
|
-
}),
|
|
1203
|
+
base: storage.makeCheckpoint(1n),
|
|
1048
1204
|
writeCheckpoint: null,
|
|
1049
1205
|
update: CHECKPOINT_INVALIDATE_ALL
|
|
1050
1206
|
})
|
|
1051
|
-
).rejects.toThrow('Too many
|
|
1207
|
+
).rejects.toThrow('Too many buckets: 15 (limit of 10)');
|
|
1052
1208
|
|
|
1053
1209
|
// Verify only top 10 are shown
|
|
1054
1210
|
const errorMessage = errorMessages[0];
|
|
@@ -1094,16 +1250,16 @@ class MockBucketChecksumStateStorage implements BucketChecksumStateStorage {
|
|
|
1094
1250
|
|
|
1095
1251
|
makeCheckpoint(
|
|
1096
1252
|
opId: InternalOpId,
|
|
1097
|
-
parameters?: (lookups: ScopedParameterLookup[]) =>
|
|
1253
|
+
parameters?: (lookups: ScopedParameterLookup[], limit: number) => ParameterLookupRows[]
|
|
1098
1254
|
): ReplicationCheckpoint {
|
|
1099
1255
|
return {
|
|
1100
1256
|
checkpoint: opId,
|
|
1101
1257
|
lsn: String(opId),
|
|
1102
|
-
getParameterSets: async (lookups: ScopedParameterLookup[]) => {
|
|
1258
|
+
getParameterSets: async (lookups: ScopedParameterLookup[], limit) => {
|
|
1103
1259
|
if (parameters == null) {
|
|
1104
1260
|
throw new Error(`getParametersSets not defined for checkpoint ${opId}`);
|
|
1105
1261
|
}
|
|
1106
|
-
return parameters(lookups);
|
|
1262
|
+
return parameters(lookups, limit);
|
|
1107
1263
|
}
|
|
1108
1264
|
};
|
|
1109
1265
|
}
|