diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx
index 9effb991c..9565d6268 100644
--- a/apps/trading/pages/_app.page.tsx
+++ b/apps/trading/pages/_app.page.tsx
@@ -18,13 +18,12 @@ import {
} from '@vegaprotocol/web3';
import {
clients,
- EnvironmentProvider,
envTriggerMapping,
Networks,
- NodeSwitcherDialog,
useEnvironment,
useEnvironment2,
useInitializeEnv,
+ useNodeHealth,
useStatisticsQuery,
} from '@vegaprotocol/environment';
import { AppLoader, Web3Provider } from '../components/app-loader';
@@ -41,8 +40,10 @@ import { ViewingBanner } from '../components/viewing-banner';
import { Banner } from '../components/banner';
import classNames from 'classnames';
import { Dialog } from '@vegaprotocol/ui-toolkit';
+import type { InMemoryCacheConfig } from '@apollo/client';
import { ApolloProvider } from '@apollo/client';
-import { useHeaderStore } from '@vegaprotocol/apollo-client';
+import { createClient, useHeaderStore } from '@vegaprotocol/apollo-client';
+import classNames from 'classnames';
const DEFAULT_TITLE = t('Welcome to Vega trading!');
@@ -126,19 +127,34 @@ const DynamicLoader = dynamic(
function VegaTradingApp(props: AppProps) {
const [open, setOpen] = useState(false);
- const status = useEnvironment2((store) => store.status);
+ const { status, url } = useEnvironment2((store) => ({
+ status: store.status,
+ url: store.url,
+ }));
+
useInitializeEnv();
- if (status === 'default' || status === 'pending') {
+ const client = useMemo(() => {
+ if (url) {
+ return createClient({
+ url,
+ cacheConfig,
+ });
+ }
+ return undefined;
+ }, [url]);
+
+ if (status === 'default' || status === 'pending' || !client) {
return ;
}
return (
- {/* */}
-
-
-
+
+
+
+
+
);
}
@@ -151,7 +167,7 @@ const NodeSwitcher = ({
setOpen: (x: boolean) => void;
}) => {
const [customUrl, setCustomUrl] = useState('');
- const { status, nodes, setUrl } = useEnvironment2((store) => ({
+ const { nodes, setUrl } = useEnvironment2((store) => ({
status: store.status,
nodes: store.nodes,
setUrl: store.setUrl,
@@ -164,7 +180,8 @@ const NodeSwitcher = ({
| node |
response time |
- block height |
+ core block height |
+ datanode block height |
@@ -199,7 +216,10 @@ const Row = ({ url }: { url: string }) => {
const [time, setTime] = useState();
const { data } = useStatisticsQuery({
pollInterval: 3000,
+ fetchPolicy: 'no-cache',
});
+ const headerStore = useHeaderStore();
+ const headers = headerStore[url];
useEffect(() => {
const requestUrl = new URL(url);
@@ -209,11 +229,19 @@ const Row = ({ url }: { url: string }) => {
setTime(duration);
}, [url]);
+ const headerBlockHeightClass = classNames('text-right', {
+ 'text-vega-pink':
+ headers &&
+ data &&
+ headers.blockHeight < Number(data.statistics.blockHeight) - 3,
+ });
+
return (
<>
{url} |
{time ? time.toFixed(2) + 'ms' : 'n/a'} |
{data?.statistics.blockHeight || '-'} |
+ {headers?.blockHeight || '-'} |
>
);
};
@@ -226,10 +254,20 @@ const Test = () => {
status: store.status,
}));
const headers = useHeaderStore();
+ const {
+ coreBlockHeight,
+ coreVegaTime,
+ datanodeBlockHeight,
+ datanodeVegaTime,
+ } = useNodeHealth();
return (
{JSON.stringify(env, null, 2)}
{JSON.stringify(headers, null, 2)}
+
Core BH {coreBlockHeight}
+
Core time {coreVegaTime.toISOString()}
+
Datanode BH {datanodeBlockHeight}
+
Datanode time {datanodeVegaTime?.toISOString()}
);
};
@@ -248,3 +286,51 @@ const MaybeConnectEagerly = () => {
}
return null;
};
+
+const cacheConfig: InMemoryCacheConfig = {
+ typePolicies: {
+ Account: {
+ keyFields: false,
+ fields: {
+ balanceFormatted: {},
+ },
+ },
+ Instrument: {
+ keyFields: false,
+ },
+ TradableInstrument: {
+ keyFields: ['instrument'],
+ },
+ Product: {
+ keyFields: ['settlementAsset', ['id']],
+ },
+ MarketData: {
+ keyFields: ['market', ['id']],
+ },
+ Node: {
+ keyFields: false,
+ },
+ Withdrawal: {
+ fields: {
+ pendingOnForeignChain: {
+ read: (isPending = false) => isPending,
+ },
+ },
+ },
+ ERC20: {
+ keyFields: ['contractAddress'],
+ },
+ PositionUpdate: {
+ keyFields: false,
+ },
+ AccountUpdate: {
+ keyFields: false,
+ },
+ Party: {
+ keyFields: false,
+ },
+ Fees: {
+ keyFields: false,
+ },
+ },
+};
diff --git a/libs/environment/src/hooks/use-node-health.ts b/libs/environment/src/hooks/use-node-health.ts
index e54cddfaa..a4eee921f 100644
--- a/libs/environment/src/hooks/use-node-health.ts
+++ b/libs/environment/src/hooks/use-node-health.ts
@@ -1,88 +1,36 @@
-import compact from 'lodash/compact';
-import shuffle from 'lodash/shuffle';
-import type { createClient } from '@vegaprotocol/apollo-client';
-import { useEffect, useState } from 'react';
-import type { StatisticsQuery } from '../utils/__generated__/Node';
-import { StatisticsDocument } from '../utils/__generated__/Node';
-import type { ClientCollection } from './use-nodes';
+import { useMemo } from 'react';
+import { useStatisticsQuery } from '../utils/__generated__/Node';
+import { useHeaderStore } from '@vegaprotocol/apollo-client';
+import { useEnvironment } from './use-environment';
+import { fromNanoSeconds } from '@vegaprotocol/react-helpers';
+import { useEnvironment2 } from './use-environment-2';
-// How often to query other nodes
-export const INTERVAL_TIME = 30 * 1000;
-// Number of nodes to query against
-export const NODE_SUBSET_COUNT = 5;
+export const useNodeHealth = () => {
+ const url = useEnvironment2((store) => store.url);
+ const headerStore = useHeaderStore();
+ const headers = url ? headerStore[url] : undefined;
+ const { data } = useStatisticsQuery({
+ pollInterval: 1000,
+ fetchPolicy: 'no-cache',
+ });
-// Queries all nodes from the environment provider via an interval
-// to calculate and return the difference between the most advanced block
-// and the block height of the current node
-export const useNodeHealth = (clients: ClientCollection, vegaUrl?: string) => {
- const [blockDiff, setBlockDiff] = useState(0);
+ const blockDiff = useMemo(() => {
+ if (!data?.statistics.blockHeight) {
+ return null;
+ }
- useEffect(() => {
- if (!clients || !vegaUrl) return;
+ if (!headers) {
+ return 0;
+ }
- const fetchBlockHeight = async (
- client?: ReturnType
- ) => {
- try {
- const result = await client?.query({
- query: StatisticsDocument,
- fetchPolicy: 'no-cache', // always fetch and never cache
- });
+ return Number(data.statistics.blockHeight) - headers.blockHeight;
+ }, [data, headers]);
- if (!result) return null;
- if (result.error) return null;
- return result;
- } catch {
- return null;
- }
- };
-
- const getBlockHeights = async () => {
- const nodes = Object.keys(clients).filter((key) => key !== vegaUrl);
- // make sure that your current vega url is always included
- // so we can compare later
- const testNodes = [vegaUrl, ...randomSubset(nodes, NODE_SUBSET_COUNT)];
- const result = await Promise.all(
- testNodes.map((node) => fetchBlockHeight(clients[node]))
- );
- const blockHeights: { [node: string]: number | null } = {};
- testNodes.forEach((node, i) => {
- const data = result[i];
- const blockHeight = data
- ? Number(data?.data.statistics.blockHeight)
- : null;
- blockHeights[node] = blockHeight;
- });
- return blockHeights;
- };
-
- // Every INTERVAL_TIME get block heights of a random subset
- // of nodes and determine if your current node is falling behind
- const interval = setInterval(async () => {
- const blockHeights = await getBlockHeights();
- const highestBlock = Math.max.apply(
- null,
- compact(Object.values(blockHeights))
- );
- const currNodeBlock = blockHeights[vegaUrl];
-
- if (!currNodeBlock) {
- // Block height query failed and null was returned
- setBlockDiff(-1);
- } else {
- setBlockDiff(highestBlock - currNodeBlock);
- }
- }, INTERVAL_TIME);
-
- return () => {
- clearInterval(interval);
- };
- }, [clients, vegaUrl]);
-
- return blockDiff;
-};
-
-const randomSubset = (arr: string[], size: number) => {
- const shuffled = shuffle(arr);
- return shuffled.slice(0, size);
+ return {
+ coreBlockHeight: Number(data?.statistics.blockHeight || 0),
+ coreVegaTime: fromNanoSeconds(data?.statistics.vegaTime),
+ datanodeBlockHeight: headers?.blockHeight,
+ datanodeVegaTime: headers?.timestamp,
+ blockDiff,
+ };
};
diff --git a/libs/environment/src/utils/Node.graphql b/libs/environment/src/utils/Node.graphql
index 6cb58464e..64ab73970 100644
--- a/libs/environment/src/utils/Node.graphql
+++ b/libs/environment/src/utils/Node.graphql
@@ -2,6 +2,7 @@ query Statistics {
statistics {
chainId
blockHeight
+ vegaTime
}
}
diff --git a/libs/environment/src/utils/__generated__/Node.ts b/libs/environment/src/utils/__generated__/Node.ts
index 4be4f15b6..2f256176c 100644
--- a/libs/environment/src/utils/__generated__/Node.ts
+++ b/libs/environment/src/utils/__generated__/Node.ts
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>;
-export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string } };
+export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
@@ -19,6 +19,7 @@ export const StatisticsDocument = gql`
statistics {
chainId
blockHeight
+ vegaTime
}
}
`;