From 053a4fb2953e372242a8faea1cc94e08f6328b17 Mon Sep 17 00:00:00 2001 From: saml33 Date: Fri, 6 Oct 2023 20:13:07 +1100 Subject: [PATCH 1/2] offchain services health hook --- components/StatusBar.tsx | 16 +++++++++- hooks/useOffchainServicesHealth.ts | 47 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 hooks/useOffchainServicesHealth.ts diff --git a/components/StatusBar.tsx b/components/StatusBar.tsx index e433c074..37215953 100644 --- a/components/StatusBar.tsx +++ b/components/StatusBar.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next' -import Tps from './Tps' +import Tps, { StatusDot } from './Tps' import DiscordIcon from './icons/DiscordIcon' import { TwitterIcon } from './icons/TwitterIcon' import { DocumentTextIcon } from '@heroicons/react/20/solid' @@ -8,6 +8,7 @@ import { IDL } from '@blockworks-foundation/mango-v4' import RpcPing from './RpcPing' import Tooltip from './shared/Tooltip' import { useRouter } from 'next/router' +import useOffchainServicesHealth from 'hooks/useOffchainServicesHealth' const DEFAULT_LATEST_COMMIT = { sha: '', url: '' } @@ -36,6 +37,10 @@ const StatusBar = ({ collapsed }: { collapsed: boolean }) => { const { t } = useTranslation('common') const [latestCommit, setLatestCommit] = useState(DEFAULT_LATEST_COMMIT) const router = useRouter() + const { offchainHealth, isLoading: loadingOffchainHealth } = + useOffchainServicesHealth() + + console.log(offchainHealth) useEffect(() => { const { sha } = latestCommit @@ -54,6 +59,15 @@ const StatusBar = ({ collapsed }: { collapsed: boolean }) => { | + {!loadingOffchainHealth ? ( + <> + | +
+ + Offchain Services +
+ + ) : null}
diff --git a/hooks/useOffchainServicesHealth.ts b/hooks/useOffchainServicesHealth.ts new file mode 100644 index 00000000..5f5034c5 --- /dev/null +++ b/hooks/useOffchainServicesHealth.ts @@ -0,0 +1,47 @@ +import { useQuery } from '@tanstack/react-query' +import { useMemo } from 'react' + +const fetchOffchainHealth = async () => { + try { + const [dbHealthResponse, redisHealthResponse, serverHealthResponse] = + await Promise.all([ + fetch('https://api.mngo.cloud/data/health/db'), + fetch('https://api.mngo.cloud/data/health/redis'), + fetch('https://api.mngo.cloud/data/health/server'), + ]) + const [dbHealth, redisHealth, serverHealth] = await Promise.all([ + dbHealthResponse.json(), + redisHealthResponse.json(), + serverHealthResponse.json(), + ]) + return { dbHealth, redisHealth, serverHealth } + } catch (e) { + console.log('Failed to check offchain services health', e) + return { dbHealth: 500, redisHealth: 500, serverHealth: 500 } + } +} + +export default function useOffchainServicesHealth() { + const { data: offchainHealthData, isLoading } = useQuery( + ['offchain-health'], + () => fetchOffchainHealth(), + { + cacheTime: 1000 * 60 * 10, + staleTime: 1000 * 60, + retry: 3, + }, + ) + + const offchainHealth = useMemo(() => { + if (!offchainHealthData) return 500 + const somethingWrong = Object.values(offchainHealthData).filter( + (v) => v !== 200, + ) + if (!somethingWrong.length) { + return 200 + } else if (somethingWrong.length < 3) { + return 300 + } else return 500 + }, [offchainHealthData]) + return { offchainHealth, isLoading } +} From 86664fb3eb1af7355d1352b567c62df52a2b3d76 Mon Sep 17 00:00:00 2001 From: saml33 Date: Mon, 9 Oct 2023 13:05:52 +1100 Subject: [PATCH 2/2] add overall platform status --- components/RpcPing.tsx | 51 ++-------- components/StatusBar.tsx | 143 ++++++++++++++++++++++++++--- components/Tps.tsx | 52 ++--------- hooks/useOffchainServicesHealth.ts | 23 +++-- public/locales/en/common.json | 7 +- public/locales/es/common.json | 7 +- public/locales/ru/common.json | 7 +- public/locales/zh/common.json | 5 +- public/locales/zh_tw/common.json | 5 +- 9 files changed, 178 insertions(+), 122 deletions(-) diff --git a/components/RpcPing.tsx b/components/RpcPing.tsx index c4b7622c..9182ffbc 100644 --- a/components/RpcPing.tsx +++ b/components/RpcPing.tsx @@ -1,45 +1,8 @@ -import { Connection } from '@solana/web3.js' -import mangoStore from '@store/mangoStore' -import { useEffect, useState } from 'react' -import useInterval from './shared/useInterval' import { formatNumericValue } from 'utils/numbers' -import Tooltip from './shared/Tooltip' -import { useTranslation } from 'react-i18next' import { StatusDot } from './Tps' +import { rpcAlertThreshold, rpcWarningThreshold } from './StatusBar' -const rpcAlertThreshold = 250 -const rpcWarningThreshold = 500 - -const getPingTime = async ( - connection: Connection, - setRpcPing: (x: number) => void, -) => { - const startTime = Date.now() - try { - await connection.getSlot() - - const endTime = Date.now() - const pingTime = endTime - startTime - setRpcPing(pingTime) - } catch (error) { - console.error('Error pinging the RPC:', error) - return null - } -} - -const RpcPing = () => { - const { t } = useTranslation('common') - const connection = mangoStore((s) => s.connection) - const [rpcPing, setRpcPing] = useState(0) - - useEffect(() => { - getPingTime(connection, setRpcPing) - }, []) - - useInterval(() => { - getPingTime(connection, setRpcPing) - }, 30 * 1000) - +const RpcPing = ({ rpcPing }: { rpcPing: number }) => { return (
@@ -48,12 +11,10 @@ const RpcPing = () => { alert={rpcAlertThreshold} warning={rpcWarningThreshold} /> - - - {formatNumericValue(rpcPing, 0)} - MS - - + + {formatNumericValue(rpcPing, 0)} + MS +
) diff --git a/components/StatusBar.tsx b/components/StatusBar.tsx index 37215953..4908f762 100644 --- a/components/StatusBar.tsx +++ b/components/StatusBar.tsx @@ -3,14 +3,22 @@ import Tps, { StatusDot } from './Tps' import DiscordIcon from './icons/DiscordIcon' import { TwitterIcon } from './icons/TwitterIcon' import { DocumentTextIcon } from '@heroicons/react/20/solid' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { IDL } from '@blockworks-foundation/mango-v4' import RpcPing from './RpcPing' import Tooltip from './shared/Tooltip' import { useRouter } from 'next/router' import useOffchainServicesHealth from 'hooks/useOffchainServicesHealth' +import mangoStore from '@store/mangoStore' +import { Connection } from '@solana/web3.js' +import { sumBy } from 'lodash' +import useInterval from './shared/useInterval' const DEFAULT_LATEST_COMMIT = { sha: '', url: '' } +export const tpsAlertThreshold = 1300 +export const tpsWarningThreshold = 1000 +export const rpcAlertThreshold = 250 +export const rpcWarningThreshold = 500 const getLatestCommit = async () => { try { @@ -33,14 +41,81 @@ const getLatestCommit = async () => { } } +const getRecentPerformance = async ( + connection: Connection, + setTps: (x: number) => void, +) => { + try { + const samples = 2 + const response = await connection.getRecentPerformanceSamples(samples) + const totalSecs = sumBy(response, 'samplePeriodSecs') + const totalTransactions = sumBy(response, 'numTransactions') + const tps = totalTransactions / totalSecs + + setTps(tps) + } catch { + console.warn('Unable to fetch TPS') + } +} + +const getPingTime = async ( + connection: Connection, + setRpcPing: (x: number) => void, +) => { + const startTime = Date.now() + try { + await connection.getSlot() + + const endTime = Date.now() + const pingTime = endTime - startTime + setRpcPing(pingTime) + } catch (error) { + console.error('Error pinging the RPC:', error) + return null + } +} + +const getOverallStatus = ( + tps: number, + rpcPing: number, + offchainHealth: number, +) => { + if (tps < tpsWarningThreshold) { + return 'severly-degraded' + } else if ( + tps < tpsAlertThreshold || + rpcPing > rpcWarningThreshold || + offchainHealth !== 200 + ) { + return 'degraded' + } else return 'operational' +} + const StatusBar = ({ collapsed }: { collapsed: boolean }) => { const { t } = useTranslation('common') const [latestCommit, setLatestCommit] = useState(DEFAULT_LATEST_COMMIT) const router = useRouter() const { offchainHealth, isLoading: loadingOffchainHealth } = useOffchainServicesHealth() + const connection = mangoStore((s) => s.connection) + const [tps, setTps] = useState(0) + const [rpcPing, setRpcPing] = useState(0) - console.log(offchainHealth) + useEffect(() => { + getPingTime(connection, setRpcPing) + }, []) + + useInterval(() => { + getPingTime(connection, setRpcPing) + }, 30 * 1000) + + useEffect(() => { + getRecentPerformance(connection, setTps) + }, []) + + useInterval(() => { + getRecentPerformance(connection, setTps) + }, 60 * 1000) useEffect(() => { const { sha } = latestCommit @@ -49,25 +124,65 @@ const StatusBar = ({ collapsed }: { collapsed: boolean }) => { } }, [latestCommit]) + const platformHealth = useMemo(() => { + return getOverallStatus(tps, rpcPing, offchainHealth) + }, [tps, rpcPing, offchainHealth]) + + const dotColor = + platformHealth === 'severly-degraded' + ? 'bg-th-error' + : platformHealth === 'degraded' + ? 'bg-th-warning' + : 'bg-th-success' + return (