Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/nodes stats 1160 #1169

Merged
merged 20 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8550d14
feat(hapi): avoid requesting duplicate endpoints
Torresmorah Mar 7, 2023
4e82091
chore(webapp): use endpoint updated time instead of local time
Torresmorah Mar 7, 2023
9bc804f
perf(hapi): improve endpoints health check
Torresmorah Mar 8, 2023
000430c
feat(hasura): add health_check_history table
Torresmorah Mar 8, 2023
5771ae5
feat(hasura): add endpoints check history table
Torresmorah Mar 10, 2023
e90c089
feat(hapi): add service to manage health check history
Torresmorah Mar 10, 2023
233e5c5
feat(hasura): add check history by endpoint view
Torresmorah Mar 15, 2023
432b9c9
feat(hasura): add select permissions to guest role
Torresmorah Mar 15, 2023
bff30f6
feat(webapp): add endpoints stats view
Torresmorah Mar 20, 2023
b61ba39
feat(webapp): add internationalization for endpoints stats view
Torresmorah Mar 20, 2023
d18cde4
fix(hasura): remove check_history_by_endpoint view
Torresmorah Mar 22, 2023
f479235
fix(hasura): change sum_total_time data type
Torresmorah Mar 22, 2023
239777a
feat(webapp): add link to endpoints stats in the endpoints list
Torresmorah Mar 22, 2023
b6b0535
refactor(webapp): fix sintax error and refactor code
Torresmorah Mar 22, 2023
7ec3603
refactor(hapi): use flatMap and remove unneeded order by
Torresmorah Mar 22, 2023
97e9747
feat(hapi): fix flatMap
Torresmorah Mar 23, 2023
be5aa73
feat(webapp): use Autocomplete component
Torresmorah Mar 23, 2023
a9057f1
[CodeFactor] Apply fixes
code-factor Mar 23, 2023
5757924
fix(hapi): apply fixes in not defined
Torresmorah Mar 23, 2023
b74a61f
fix(hapi): apply fixes in wrong format
Torresmorah Mar 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(webapp): add endpoints stats view
  • Loading branch information
Torresmorah committed Mar 20, 2023
commit bff30f604a2c1783d631784055b808821d8d5e97
19 changes: 19 additions & 0 deletions webapp/src/gql/producer.gql.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,22 @@ export const EOSRATE_STATS_QUERY = gql`
}
}
`

export const FASTEST_ENDPOINTS_QUERY = gql`query($today: date){
endpoints: check_history_by_endpoint(limit: 5, order_by: {avg_time: asc, availability: desc}, where: {date: {_eq: $today}}) {
value
avg_time
availability
}
}`

export const HISTORY_ENDPOINTS_BY_PRODUCER_QUERY = gql`query($id: Int){
endpoints: check_history_by_endpoint(order_by: {value: asc, date: asc}, where: {producer_id: {_eq: $id}}) {
value
date
avg_time
availability
}
}`


84 changes: 84 additions & 0 deletions webapp/src/hooks/customHooks/useHealthCheckHistoryState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState, useEffect } from 'react'
import { useLazyQuery } from '@apollo/client'

import {
FASTEST_ENDPOINTS_QUERY,
HISTORY_ENDPOINTS_BY_PRODUCER_QUERY,
PRODUCERS_QUERY,
} from '../../gql'

const useHealthCheckState = () => {
const [
loadEndpoints,
{ loading = true, data: { endpoints: fastestEndpoints } = {} },
] = useLazyQuery(FASTEST_ENDPOINTS_QUERY)
const [
loadProducers,
{ loading: loadingProducers = true, data: { producers } = {} },
] = useLazyQuery(PRODUCERS_QUERY)
const [
loadHistory,
{ loading: loadingHistory = true, data: { endpoints: history } = {} },
] = useLazyQuery(HISTORY_ENDPOINTS_BY_PRODUCER_QUERY)
const [producersNames, setProducersNames] = useState()
const [selected, setSelected] = useState()
const [historyData, setHistoryData] = useState()
const [statsAverage, setStatsAverage] = useState()

useEffect(() => {
const endpointFilter = {
_and: [{ type: { _in: ['ssl', 'api'] } }, { value: { _gt: '' } }],
}
loadProducers({
variables: {
where: { nodes: { endpoints: endpointFilter } },
endpointFilter,
limit: null,
},
})
loadEndpoints({ variables: { today: new Date() } })
}, [])

useEffect(() => {
if (!producers?.length) return

setProducersNames(
producers.map((producer) => ({
id: producer.id,
name: producer?.bp_json?.org?.candidate_name,
})),
)
setSelected(producers[0]?.id)
}, [producers])

useEffect(() => {
loadHistory({ variables: { id: selected } })
}, [selected])

useEffect(() => {
if (!history) return

const data = history.reduce((aux, curr) => {
const index = aux.findIndex((x) => x.name === curr.value)
if (index < 0) {
aux.push({ name: curr.value, data: [curr.avg_time],avg_time:curr.avg_time, availability: curr.availability})
} else {
aux[index].data.push(curr.avg_time)
aux[index].availability = aux[index].availability + curr.availability
aux[index].avg_time = aux[index].avg_time + curr.avg_time
}

return aux
}, [])
setHistoryData(data)
setStatsAverage(data.map(x=>({value:x.name,avg_time:x.avg_time/x.data.length,availability:x.availability/x.data.length})))

}, [history])

return [
{ fastestEndpoints, producersNames, historyData, statsAverage, selected, loading },
{ setSelected },
]
}

export default useHealthCheckState
51 changes: 51 additions & 0 deletions webapp/src/routes/EndpointsStats/EndpointStatsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint camelcase: 0 */
import React from 'react'
import { useTranslation } from 'react-i18next'
import Typography from '@mui/material/Typography'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'

import { makeStyles } from '@mui/styles'

import styles from './styles'

const useStyles = makeStyles(styles)

const EndpointsTable = ({endpoints, title}) => {
const { t } = useTranslation('EndpointsStatsRoute')
const classes = useStyles()

return (
<>
<Typography component="p" variant="h6">
{title}
</Typography>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>{t('Endpoint')}</TableCell>
<TableCell align="right">{t('Average Availability')}</TableCell>
<TableCell align="right">{t('Average Response Time')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{endpoints.map((item, index) => (
<TableRow key={index}>
<TableCell>{item.value}</TableCell>
<TableCell align="right">{`${item.availability}%`}</TableCell>
<TableCell align="right">{`${item.avg_time.toFixed(3)} s`}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}

export default EndpointsTable
102 changes: 102 additions & 0 deletions webapp/src/routes/EndpointsStats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* eslint camelcase: 0 */
import React from 'react'
import { useTranslation } from 'react-i18next'
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import LinearProgress from '@mui/material/LinearProgress'
import MenuItem from '@mui/material/MenuItem'
import Highcharts from 'highcharts'
import HighchartsReact from 'highcharts-react-official'
import moment from 'moment'
import Select from '@mui/material/Select'
import { makeStyles } from '@mui/styles'

import useHealthCheckState from '../../hooks/customHooks/useHealthCheckHistoryState'

import styles from './styles'
import EndpointsTable from './EndpointStatsTable'

const useStyles = makeStyles(styles)

const dates = []
for (let i = 29; i >= 0; i--) {
const d = moment().subtract(i, 'days').format('ll')
dates.push(d)
}

const options = {
xAxis: {
categories: dates,
},
credits: {
enabled: false,
},
title: {
text: 'Average Response Time',
},
yAxis: {
title: {
text: 'Time in seconds',
},
labels: {
format: '{text} s',
},
},
tooltip: {
pointFormat: '{series.name}: <b>{point.y} s<b>',
},
}

const EndpointsStats = () => {
const { t } = useTranslation('EndpointsStatsRoute')
const classes = useStyles()
const [{fastestEndpoints,producersNames,historyData,statsAverage,selected,loading},{setSelected}] = useHealthCheckState()

return (
<>
<Card className={classes.cardShadow}>
<CardContent>
{loading && <LinearProgress />}
{!loading && (
<EndpointsTable
title="Top 5 fastest endpoints by querying from Costa Rica"
endpoints={fastestEndpoints || []}
/>
)}
</CardContent>
</Card>
<Card className={`${classes.cardByProducer} ${classes.cardShadow}`}>
<CardContent>
<Typography component="p" variant="h6">
Endpoints stats by producer
</Typography>
<br />
{producersNames?.length && (
<Select value={selected} onChange={(event) => setSelected(event.target.value)}>
{producersNames.map(producer => (
<MenuItem key={producer.id} value={producer.id}>{producer.name}</MenuItem>
))}
</Select>
)}
{historyData && (
<HighchartsReact
highcharts={Highcharts}
options={{ ...options,xAxis: {
categories: dates.slice(dates.length-(historyData[0].data.length+1)),
}, series: historyData }}
/>
)}
{statsAverage && (
<EndpointsTable
title="List of endpoints"
endpoints={statsAverage}
/>
)}
</CardContent>
</Card>
</>
)
}

export default EndpointsStats
8 changes: 8 additions & 0 deletions webapp/src/routes/EndpointsStats/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default (theme) => ({
cardShadow: {
boxShadow: '0px 1px 3px 1px rgba(0, 0, 0, 0.15) !important',
},
cardByProducer: {
marginTop: theme.spacing(8)
}
})
8 changes: 8 additions & 0 deletions webapp/src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

const Home = lazy(() => import('./Home'))
const CPUBenchmark = lazy(() => import('./CPUBenchmark'))
const EndpointsStats = lazy(() => import('./EndpointsStats'))
const BlockProducers = lazy(() => import('./BlockProducers'))
const RewardsDistribution = lazy(() => import('./RewardsDistribution'))
const Nodes = lazy(() => import('./Nodes'))
Expand Down Expand Up @@ -118,6 +119,13 @@ const defaultRoutes = [
path: '/cpu-benchmark',
exact: true,
},
{
name: 'endpointsStats',
icon: <ActivityIcon />,
component: EndpointsStats,
path: '/endpoints-stats',
exact: true,
},
{
name: 'ricardianContract',
icon: <InfoIcon />,
Expand Down