Skip to content

Commit d4aaa7c

Browse files
authored
Merge pull request #1850 from kleros/refactor/graphql-batching-and-optimisations
Refactor/graphql batching and optimisations
2 parents ec16bb7 + 11c8057 commit d4aaa7c

13 files changed

+105
-20
lines changed

web/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878
},
7979
"dependencies": {
8080
"@cyntler/react-doc-viewer": "^1.17.0",
81+
"@graphql-tools/batch-execute": "^9.0.11",
82+
"@graphql-tools/utils": "^10.7.2",
8183
"@kleros/kleros-app": "workspace:^",
8284
"@kleros/kleros-sdk": "workspace:^",
8385
"@kleros/kleros-v2-contracts": "workspace:^",

web/src/consts/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export { ArbitratorTypes };
77
export const ONE_BASIS_POINT = 10000n;
88

99
export const REFETCH_INTERVAL = 5000;
10+
export const STALE_TIME = 1000;
1011

1112
export const IPFS_GATEWAY = import.meta.env.REACT_APP_IPFS_GATEWAY || "https://cdn.kleros.link";
1213
export const HERMES_TELEGRAM_BOT_URL =
@@ -20,7 +21,7 @@ export const GIT_URL = `https://github.com/kleros/kleros-v2/tree/${gitCommitHash
2021
export const RELEASE_VERSION = version;
2122

2223
// https://www.w3.org/TR/2012/WD-html-markup-20120329/input.email.html#input.email.attrs.value.single
23-
// eslint-disable-next-line security/detect-unsafe-regex
24+
2425
export const EMAIL_REGEX =
2526
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2627
export const TELEGRAM_REGEX = /^@\w{5,32}$/;

web/src/context/GraphqlBatcher.tsx

+30-13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React, { useMemo, createContext, useContext } from "react";
22

3+
import { createBatchingExecutor } from "@graphql-tools/batch-execute";
4+
import { AsyncExecutor, ExecutionResult } from "@graphql-tools/utils";
35
import { TypedDocumentNode } from "@graphql-typed-document-node/core";
46
import { create, windowedFiniteBatchScheduler, Batcher } from "@yornaath/batshit";
57
import { request } from "graphql-request";
68

79
import { debounceErrorToast } from "utils/debounceErrorToast";
810
import { getGraphqlUrl } from "utils/getGraphqlUrl";
9-
1011
interface IGraphqlBatcher {
1112
graphqlBatcher: Batcher<any, IQuery>;
1213
}
@@ -21,19 +22,35 @@ interface IQuery {
2122

2223
const Context = createContext<IGraphqlBatcher | undefined>(undefined);
2324

25+
const executor: AsyncExecutor = async ({ document, variables, extensions }) => {
26+
try {
27+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
28+
//@ts-ignore
29+
const result = request(extensions.url, document, variables).then((res) => ({
30+
data: res,
31+
})) as Promise<ExecutionResult>;
32+
33+
return result;
34+
} catch (error) {
35+
console.error("Graph error: ", { error });
36+
debounceErrorToast("Graph query error: failed to fetch data.");
37+
return { data: {} };
38+
}
39+
};
40+
41+
const batchExec = createBatchingExecutor(executor);
42+
2443
const fetcher = async (queries: IQuery[]) => {
25-
const promises = queries.map(async ({ id, document, variables, isDisputeTemplate, chainId }) => {
26-
const url = getGraphqlUrl(isDisputeTemplate ?? false, chainId);
27-
try {
28-
return request(url, document, variables).then((result) => ({ id, result }));
29-
} catch (error) {
30-
console.error("Graph error: ", { error });
31-
debounceErrorToast("Graph query error: failed to fetch data.");
32-
return { id, result: {} };
33-
}
34-
});
35-
const data = await Promise.all(promises);
36-
return data;
44+
const batchdata = await Promise.all(
45+
queries.map(({ document, variables, isDisputeTemplate, chainId }) =>
46+
batchExec({ document, variables, extensions: { url: getGraphqlUrl(isDisputeTemplate ?? false, chainId) } })
47+
)
48+
);
49+
50+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
51+
//@ts-ignore
52+
const processedData = batchdata.map((data, index) => ({ id: queries[index].id, result: data.data }));
53+
return processedData;
3754
};
3855

3956
const GraphqlBatcherProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => {

web/src/hooks/queries/useAllCasesQuery.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useQuery } from "@tanstack/react-query";
22

33
import { useGraphqlBatcher } from "context/GraphqlBatcher";
44

5+
import { STALE_TIME } from "src/consts";
56
import { graphql } from "src/graphql";
67
import { AllCasesQuery } from "src/graphql/graphql";
78

@@ -20,6 +21,7 @@ export const useAllCasesQuery = () => {
2021
const { graphqlBatcher } = useGraphqlBatcher();
2122
return useQuery({
2223
queryKey: [`allCasesQuery`],
24+
staleTime: STALE_TIME,
2325
queryFn: async () =>
2426
await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: allCasesQuery, variables: {} }),
2527
});

web/src/hooks/queries/useClassicAppealQuery.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useQuery } from "@tanstack/react-query";
22

3-
import { REFETCH_INTERVAL } from "consts/index";
3+
import { REFETCH_INTERVAL, STALE_TIME } from "consts/index";
44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

66
import { graphql } from "src/graphql";
@@ -44,6 +44,7 @@ export const useClassicAppealQuery = (id?: string | number) => {
4444
queryKey: [`classicAppealQuery${id}`],
4545
enabled: isEnabled,
4646
refetchInterval: REFETCH_INTERVAL,
47+
staleTime: STALE_TIME,
4748
queryFn: async () =>
4849
isEnabled
4950
? await graphqlBatcher.fetch({

web/src/hooks/queries/useCourtDetails.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useQuery } from "@tanstack/react-query";
22

3-
import { REFETCH_INTERVAL } from "consts/index";
3+
import { REFETCH_INTERVAL, STALE_TIME } from "consts/index";
44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

66
import { graphql } from "src/graphql";
@@ -36,6 +36,7 @@ export const useCourtDetails = (id?: string) => {
3636
queryKey: [`courtDetails${id}`],
3737
enabled: isEnabled,
3838
refetchInterval: REFETCH_INTERVAL,
39+
staleTime: STALE_TIME,
3940
queryFn: async () =>
4041
await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: courtDetailsQuery, variables: { id } }),
4142
});

web/src/hooks/queries/useCourtTree.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useQuery } from "@tanstack/react-query";
22

33
import { useGraphqlBatcher } from "context/GraphqlBatcher";
44

5+
import { STALE_TIME } from "src/consts";
56
import { graphql } from "src/graphql";
67
import { CourtTreeQuery } from "src/graphql/graphql";
78
export type { CourtTreeQuery };
@@ -39,6 +40,7 @@ export const useCourtTree = () => {
3940
const { graphqlBatcher } = useGraphqlBatcher();
4041
return useQuery<CourtTreeQuery>({
4142
queryKey: ["courtTreeQuery"],
43+
staleTime: STALE_TIME,
4244
queryFn: async () =>
4345
await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: courtTreeQuery, variables: {} }),
4446
});

web/src/hooks/queries/useDisputeDetailsQuery.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useQuery } from "@tanstack/react-query";
22

3-
import { REFETCH_INTERVAL } from "consts/index";
3+
import { REFETCH_INTERVAL, STALE_TIME } from "consts/index";
44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

66
import { graphql } from "src/graphql";
@@ -48,6 +48,7 @@ export const useDisputeDetailsQuery = (id?: string | number) => {
4848
queryKey: [`disputeDetailsQuery${id}`],
4949
enabled: isEnabled,
5050
refetchInterval: REFETCH_INTERVAL,
51+
staleTime: STALE_TIME,
5152
queryFn: async () =>
5253
await graphqlBatcher.fetch({
5354
id: crypto.randomUUID(),

web/src/hooks/queries/useDisputeMaintenanceQuery.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useQuery } from "@tanstack/react-query";
22

33
import { useGraphqlBatcher } from "context/GraphqlBatcher";
44

5+
import { STALE_TIME } from "src/consts";
56
import { graphql } from "src/graphql";
67
import { DisputeMaintenanceQuery } from "src/graphql/graphql";
78
import { isUndefined } from "src/utils";
@@ -40,6 +41,7 @@ const useDisputeMaintenanceQuery = (id?: string) => {
4041
return useQuery<DisputeMaintenanceQuery>({
4142
queryKey: [`disputeMaintenanceQuery-${id}`],
4243
enabled: isEnabled,
44+
staleTime: STALE_TIME,
4345
queryFn: async () =>
4446
await graphqlBatcher.fetch({
4547
id: crypto.randomUUID(),

web/src/hooks/queries/useJurorStakeDetailsQuery.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useQuery } from "@tanstack/react-query";
22

3-
import { REFETCH_INTERVAL } from "consts/index";
3+
import { REFETCH_INTERVAL, STALE_TIME } from "consts/index";
44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

66
import { graphql } from "src/graphql";
@@ -29,6 +29,7 @@ export const useJurorStakeDetailsQuery = (userId?: string) => {
2929
queryKey: [`jurorStakeDetails${userId}`],
3030
enabled: isEnabled,
3131
refetchInterval: REFETCH_INTERVAL,
32+
staleTime: STALE_TIME,
3233
queryFn: async () =>
3334
await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: jurorStakeDetailsQuery, variables: { userId } }),
3435
});

web/src/hooks/queries/useUser.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Address } from "viem";
33

44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

6+
import { STALE_TIME } from "src/consts";
67
import { graphql } from "src/graphql";
78
import { UserQuery, Dispute_Filter, UserDisputeFilterQuery, UserDetailsFragment } from "src/graphql/graphql";
89
export type { UserQuery, UserDetailsFragment };
@@ -58,6 +59,7 @@ export const useUserQuery = (address?: Address, where?: Dispute_Filter) => {
5859
return useQuery<UserQuery | UserDisputeFilterQuery>({
5960
queryKey: [`userQuery${address?.toLowerCase()}`],
6061
enabled: isEnabled,
62+
staleTime: STALE_TIME,
6163
queryFn: async () =>
6264
await graphqlBatcher.fetch({
6365
id: crypto.randomUUID(),

web/src/hooks/queries/useVotingHistory.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useQuery } from "@tanstack/react-query";
22

3-
import { REFETCH_INTERVAL } from "consts/index";
3+
import { REFETCH_INTERVAL, STALE_TIME } from "consts/index";
44
import { useGraphqlBatcher } from "context/GraphqlBatcher";
55

66
import { graphql } from "src/graphql";
@@ -59,6 +59,7 @@ export const useVotingHistory = (disputeID?: string) => {
5959
queryKey: [`VotingHistory${disputeID}`],
6060
enabled: isEnabled,
6161
refetchInterval: REFETCH_INTERVAL,
62+
staleTime: STALE_TIME,
6263
queryFn: async () =>
6364
await graphqlBatcher.fetch({ id: crypto.randomUUID(), document: votingHistoryQuery, variables: { disputeID } }),
6465
});

yarn.lock

+53-1
Original file line numberDiff line numberDiff line change
@@ -4510,6 +4510,19 @@ __metadata:
45104510
languageName: node
45114511
linkType: hard
45124512

4513+
"@graphql-tools/batch-execute@npm:^9.0.11":
4514+
version: 9.0.11
4515+
resolution: "@graphql-tools/batch-execute@npm:9.0.11"
4516+
dependencies:
4517+
"@graphql-tools/utils": "npm:^10.7.0"
4518+
dataloader: "npm:^2.2.3"
4519+
tslib: "npm:^2.8.1"
4520+
peerDependencies:
4521+
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
4522+
checksum: 10/6180424a5fa36a446baa665a92cff0332a566b1bd7481e2641c9d0aa2a7a47a24d21a9b90bb3d7f4c0d5a7331fc9e623fe43746f07e5eb0654419a29d860a940
4523+
languageName: node
4524+
linkType: hard
4525+
45134526
"@graphql-tools/code-file-loader@npm:^8.0.0":
45144527
version: 8.0.1
45154528
resolution: "@graphql-tools/code-file-loader@npm:8.0.1"
@@ -4837,6 +4850,20 @@ __metadata:
48374850
languageName: node
48384851
linkType: hard
48394852

4853+
"@graphql-tools/utils@npm:^10.7.0, @graphql-tools/utils@npm:^10.7.2":
4854+
version: 10.7.2
4855+
resolution: "@graphql-tools/utils@npm:10.7.2"
4856+
dependencies:
4857+
"@graphql-typed-document-node/core": "npm:^3.1.1"
4858+
cross-inspect: "npm:1.0.1"
4859+
dset: "npm:^3.1.4"
4860+
tslib: "npm:^2.4.0"
4861+
peerDependencies:
4862+
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
4863+
checksum: 10/b4725b081e5ff5c1441036db76ce907a6fe9b4c94aa9ceb070f75541b2297c3cccaa182f91d214f9abe6d89df33d8df51e055afbc4e382b01e8d8fb7c2f6edf6
4864+
languageName: node
4865+
linkType: hard
4866+
48404867
"@graphql-tools/wrap@npm:^10.0.0":
48414868
version: 10.0.0
48424869
resolution: "@graphql-tools/wrap@npm:10.0.0"
@@ -5609,6 +5636,8 @@ __metadata:
56095636
"@eslint/js": "npm:^9.15.0"
56105637
"@graphql-codegen/cli": "npm:^5.0.3"
56115638
"@graphql-codegen/client-preset": "npm:^4.5.1"
5639+
"@graphql-tools/batch-execute": "npm:^9.0.11"
5640+
"@graphql-tools/utils": "npm:^10.7.2"
56125641
"@kleros/kleros-app": "workspace:^"
56135642
"@kleros/kleros-sdk": "workspace:^"
56145643
"@kleros/kleros-v2-contracts": "workspace:^"
@@ -15796,6 +15825,15 @@ __metadata:
1579615825
languageName: node
1579715826
linkType: hard
1579815827

15828+
"cross-inspect@npm:1.0.1":
15829+
version: 1.0.1
15830+
resolution: "cross-inspect@npm:1.0.1"
15831+
dependencies:
15832+
tslib: "npm:^2.4.0"
15833+
checksum: 10/7c1e02e0a9670b62416a3ea1df7ae880fdad3aa0a857de8932c4e5f8acd71298c7e3db9da8e9da603f5692cd1879938f5e72e34a9f5d1345987bef656d117fc1
15834+
languageName: node
15835+
linkType: hard
15836+
1579915837
"cross-spawn@npm:7.0.3":
1580015838
version: 7.0.3
1580115839
resolution: "cross-spawn@npm:7.0.3"
@@ -16314,6 +16352,13 @@ __metadata:
1631416352
languageName: node
1631516353
linkType: hard
1631616354

16355+
"dataloader@npm:^2.2.3":
16356+
version: 2.2.3
16357+
resolution: "dataloader@npm:2.2.3"
16358+
checksum: 10/83fe6259abe00ae64c5f48252ef59d8e5fcabda9fd4d26685f14a76eeca596bf6f9500d9f22a0094c50c3ea782a0977728f9367e232dfa0fdb5c9d646de279b2
16359+
languageName: node
16360+
linkType: hard
16361+
1631716362
"date-fns@npm:^1.27.2":
1631816363
version: 1.30.1
1631916364
resolution: "date-fns@npm:1.30.1"
@@ -17170,6 +17215,13 @@ __metadata:
1717017215
languageName: node
1717117216
linkType: hard
1717217217

17218+
"dset@npm:^3.1.4":
17219+
version: 3.1.4
17220+
resolution: "dset@npm:3.1.4"
17221+
checksum: 10/6268c9e2049c8effe6e5a1952f02826e8e32468b5ced781f15f8f3b1c290da37626246fec014fbdd1503413f981dff6abd8a4c718ec9952fd45fccb6ac9de43f
17222+
languageName: node
17223+
linkType: hard
17224+
1717317225
"duplexer3@npm:^0.1.4":
1717417226
version: 0.1.5
1717517227
resolution: "duplexer3@npm:0.1.5"
@@ -34460,7 +34512,7 @@ __metadata:
3446034512
languageName: node
3446134513
linkType: hard
3446234514

34463-
"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.6.3":
34515+
"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2, tslib@npm:^2.6.3, tslib@npm:^2.8.1":
3446434516
version: 2.8.1
3446534517
resolution: "tslib@npm:2.8.1"
3446634518
checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7

0 commit comments

Comments
 (0)