diff --git a/frontend/src/pages/DiskUsage/DiskUsage.tsx b/frontend/src/pages/DiskUsage/DiskUsage.tsx index dd98f03..44209f1 100644 --- a/frontend/src/pages/DiskUsage/DiskUsage.tsx +++ b/frontend/src/pages/DiskUsage/DiskUsage.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react' import { Pie } from '@ant-design/plots' import { Card, Spin, Row, Col, notification } from 'antd' +import useSWR from 'swr' + interface NodeData { node: string space_used: number @@ -9,33 +11,35 @@ interface NodeData { } export function DiskUsage(): JSX.Element { - const [clusterOverviewData, setClusterOverviewData] = useState([]) - - const loadData = async () => { + const loadData = async (url: string) => { try { - const res = await fetch('/api/analyze/cluster_overview') + const res = await fetch(url) const resJson = await res.json() - setClusterOverviewData(resJson) + return resJson } catch { notification.error({ message: 'Failed to load data' }) } } - useEffect(() => { - loadData() - }, []) + const { data, error, isLoading } = useSWR('/api/analyze/cluster_overview', loadData) const rows = [] - for (let i = 0; i < clusterOverviewData.length; i += 2) { - rows.push(clusterOverviewData.slice(i, i + 2)) + if (!isLoading) { + for (let i = 0; i < data.length; i += 2) { + rows.push(data.slice(i, i + 2)) + } } - return ( + return isLoading ? ( +
loading...
+ ) : error ? ( +
error
+ ) : (

Disk usage


- {clusterOverviewData.length === 0 ? ( + {data.length === 0 ? ( ) : ( <> @@ -78,7 +82,7 @@ export function DiskUsage(): JSX.Element { }} color={['#FFB816', '#175FFF']} tooltip={{ - formatter: (v) => { + formatter: v => { return { name: v.type, value: `${(v.value / 1000000000).toFixed(2)}GB`, @@ -126,7 +130,7 @@ export function DiskUsage(): JSX.Element { }} color={['#FFB816', '#175FFF']} tooltip={{ - formatter: (v) => { + formatter: v => { return { name: v.type, value: `${(v.value / 1000000000).toFixed(2)}GB`, diff --git a/frontend/src/pages/Errors/Errors.tsx b/frontend/src/pages/Errors/Errors.tsx index 3ac760c..2bccd80 100644 --- a/frontend/src/pages/Errors/Errors.tsx +++ b/frontend/src/pages/Errors/Errors.tsx @@ -3,6 +3,8 @@ import { Table, notification } from 'antd' import { ColumnsType } from 'antd/es/table' import { isoTimestampToHumanReadable } from '../../utils/dateUtils' +import useSWR from 'swr' + interface ErrorData { name: string count: number @@ -10,8 +12,6 @@ interface ErrorData { } export default function CollapsibleTable() { - const [slowQueries, setSlowQueries] = useState([]) - const slowQueriesColumns: ColumnsType = [ { title: 'Error', @@ -26,37 +26,34 @@ export default function CollapsibleTable() { { title: 'Most recent occurence', dataIndex: 'max_last_error_time', - render: (_, item) => isoTimestampToHumanReadable(item.max_last_error_time) + render: (_, item) => isoTimestampToHumanReadable(item.max_last_error_time), }, ] - const loadData = async () => { + const loadData = async (url: string) => { try { - const res = await fetch('/api/analyze/errors') + const res = await fetch(url) const resJson = await res.json() const slowQueriesData = resJson.map((error: ErrorData, idx: number) => ({ key: idx, ...error })) - setSlowQueries(slowQueriesData) + return slowQueriesData } catch { notification.error({ message: 'Failed to load data' }) } } - useEffect(() => { - loadData() - }, []) + const { data, error, isLoading, mutate } = useSWR('/api/analyze/errors', loadData) - return ( + return isLoading ? ( +
loading...
+ ) : error ? ( +
error
+ ) : (

Errors


- +
) diff --git a/frontend/src/pages/Logs/Logs.tsx b/frontend/src/pages/Logs/Logs.tsx index e8cb373..15d6e0a 100644 --- a/frontend/src/pages/Logs/Logs.tsx +++ b/frontend/src/pages/Logs/Logs.tsx @@ -86,7 +86,7 @@ export default function Logs() {

Logs

setLogMessageFilter(e.target.value)} + onChange={e => setLogMessageFilter(e.target.value)} value={logMessageFilter} />
diff --git a/frontend/src/pages/Operations/Operations.tsx b/frontend/src/pages/Operations/Operations.tsx index 6032831..24d9a88 100644 --- a/frontend/src/pages/Operations/Operations.tsx +++ b/frontend/src/pages/Operations/Operations.tsx @@ -11,6 +11,8 @@ import TextArea from 'antd/es/input/TextArea' import { ColumnType } from 'antd/es/table' import { isoTimestampToHumanReadable } from '../../utils/dateUtils' +import useSWR from 'swr' + const OPERATION_STATUS_TO_HUMAN = { 0: 'Not started', 1: 'Running', @@ -74,25 +76,29 @@ export function OperationControls({ } export function OperationsList(): JSX.Element { - const [operations, setOperations] = useState([]) - - const fetchAndUpdateOperationsIfNeeded = async () => { - const response = await fetch('/api/async_migrations') + const fetchAndUpdateOperationsIfNeeded = async (url: string) => { + const response = await fetch(url) const responseJson = await response.json() const results = responseJson.results if (JSON.stringify(results) !== JSON.stringify(operations)) { - setOperations(results) + return results } } - const triggerOperation = async (id) => { + const triggerOperation = async id => { await fetch(`/api/async_migrations/${id}/trigger`, { method: 'POST' }) await fetchAndUpdateOperationsIfNeeded() } + const { data: operations, error, isLoading, mutate } = useSWR( + '/api/async_migrations', + fetchAndUpdateOperationsIfNeeded + ) + useEffect(() => { - fetchAndUpdateOperationsIfNeeded() - const intervalId = setInterval(fetchAndUpdateOperationsIfNeeded, 5000) + const intervalId = setInterval(() => { + mutate('/api/async_migrations') + }, 5000) return () => { try { clearInterval(intervalId) @@ -121,11 +127,11 @@ export function OperationsList(): JSX.Element { }, { title: 'Started at', - render: (_, migration) => migration.started_at ? isoTimestampToHumanReadable(migration.started_at) : '', + render: (_, migration) => (migration.started_at ? isoTimestampToHumanReadable(migration.started_at) : ''), }, { title: 'Finished at', - render: (_, migration) => migration.finished_at ? isoTimestampToHumanReadable(migration.finished_at) : '', + render: (_, migration) => (migration.finished_at ? isoTimestampToHumanReadable(migration.finished_at) : ''), }, { title: '', @@ -140,7 +146,13 @@ export function OperationsList(): JSX.Element { }, ] - return
+ return isLoading ? ( +
loading...
+ ) : error ? ( +
error
+ ) : ( +
+ ) } export function CreateNewOperation(): JSX.Element { @@ -221,8 +233,8 @@ export function CreateNewOperation(): JSX.Element { code[`operation-${i + 1}`] || `CREATE TABLE test_table ( foo String ) Engine=MergeTree() ORDER BY foo` } - onValueChange={(value) => setCode({ ...code, [`operation-${i + 1}`]: value })} - highlight={(code) => highlight(code, languages.sql)} + onValueChange={value => setCode({ ...code, [`operation-${i + 1}`]: value })} + highlight={code => highlight(code, languages.sql)} padding={10} style={{ fontFamily: '"Fira code", "Fira Mono", monospace', @@ -242,8 +254,8 @@ export function CreateNewOperation(): JSX.Element { id={`create-migration-form-rollback-${i + 1}`} name={`rollback-${i + 1}`} value={code[`rollback-${i + 1}`] || `DROP TABLE IF EXISTS test_table`} - onValueChange={(value) => setCode({ ...code, [`rollback-${i + 1}`]: value })} - highlight={(code) => highlight(code, languages.sql)} + onValueChange={value => setCode({ ...code, [`rollback-${i + 1}`]: value })} + highlight={code => highlight(code, languages.sql)} padding={10} style={{ fontFamily: '"Fira code", "Fira Mono", monospace', diff --git a/frontend/src/pages/QueryEditor/Benchmark.tsx b/frontend/src/pages/QueryEditor/Benchmark.tsx index 26a076a..9c6263c 100644 --- a/frontend/src/pages/QueryEditor/Benchmark.tsx +++ b/frontend/src/pages/QueryEditor/Benchmark.tsx @@ -6,6 +6,7 @@ import 'prismjs/components/prism-sql' import 'prismjs/themes/prism.css' import Editor from 'react-simple-code-editor' import { Column } from '@ant-design/charts' +import useSWR from 'swr' export interface BenchmarkingData { benchmarking_result: { @@ -82,8 +83,8 @@ export default function QueryBenchmarking() {

setQuery1(code)} - highlight={(code) => highlight(code, languages.sql)} + onValueChange={code => setQuery1(code)} + highlight={code => highlight(code, languages.sql)} padding={10} style={{ fontFamily: '"Fira code", "Fira Mono", monospace', @@ -102,8 +103,8 @@ export default function QueryBenchmarking() {

setQuery2(code)} - highlight={(code) => highlight(code, languages.sql)} + onValueChange={code => setQuery2(code)} + highlight={code => highlight(code, languages.sql)} padding={10} style={{ fontFamily: '"Fira code", "Fira Mono", monospace',