From 4c71d5f51eb499d6fdbbe86715a346f94356397e Mon Sep 17 00:00:00 2001 From: Maciej Bodek Date: Wed, 4 Dec 2024 13:11:59 +0100 Subject: [PATCH] User configurable refresh rates --- .../src/scenes/Editor/Metrics/index.tsx | 44 ++++++++++++++---- .../src/scenes/Editor/Metrics/metric.tsx | 18 +++++++- .../src/scenes/Editor/Metrics/utils.ts | 45 +++++++++++++++++++ .../scenes/Editor/Metrics/widgets/utils.ts | 10 +++-- .../web-console/src/scenes/Schema/index.tsx | 3 +- packages/web-console/src/store/buffers.ts | 2 + 6 files changed, 108 insertions(+), 14 deletions(-) diff --git a/packages/web-console/src/scenes/Editor/Metrics/index.tsx b/packages/web-console/src/scenes/Editor/Metrics/index.tsx index 4c03bbb0a..dda18e3a8 100644 --- a/packages/web-console/src/scenes/Editor/Metrics/index.tsx +++ b/packages/web-console/src/scenes/Editor/Metrics/index.tsx @@ -3,8 +3,8 @@ import styled from "styled-components" import { Box, Button, Select } from "@questdb/react-components" import { Text, Link } from "../../../components" import { useEditor } from "../../../providers" -import { MetricDuration } from "./utils" -import { Time } from "@styled-icons/boxicons-regular" +import { MetricDuration, RefreshRate, autoRefreshRates } from "./utils" +import { Time, Refresh } from "@styled-icons/boxicons-regular" import { AddMetricDialog } from "./add-metric-dialog" import type { Metric } from "../../../store/buffers" import { Metric as MetricComponent } from "./metric" @@ -74,11 +74,22 @@ const GlobalInfo = styled(Box).attrs({ const formatDurationLabel = (duration: MetricDuration) => `Last ${duration}` +const formatRefreshRateLabel = ( + rate: RefreshRate, + duration: MetricDuration, +) => { + if (rate === RefreshRate.AUTO) { + return `${RefreshRate.AUTO} (${autoRefreshRates[duration]})` + } + return rate +} + export const Metrics = () => { const { activeBuffer, updateBuffer, buffers } = useEditor() const [metricDuration, setMetricDuration] = useState( MetricDuration.ONE_HOUR, ) + const [refreshRate, setRefreshRate] = useState(RefreshRate.AUTO) const [dialogOpen, setDialogOpen] = useState(false) const [metrics, setMetrics] = useState([]) const telemetryConfig = useSelector(selectors.telemetry.getConfig) @@ -130,28 +141,34 @@ export const Metrics = () => { if (buffer) { const metrics = buffer?.metricsViewState?.metrics const metricDuration = buffer?.metricsViewState?.metricDuration + const refreshRate = buffer?.metricsViewState?.refreshRate if (metrics) { setMetrics(metrics) } if (metricDuration) { setMetricDuration(metricDuration) } + if (refreshRate) { + setRefreshRate(refreshRate) + } } }, [buffer]) useEffect(() => { - if ( - buffer?.id && - metricDuration !== buffer?.metricsViewState?.metricDuration - ) { + if (buffer?.id) { const merged = merge(buffer, { metricsViewState: { - metricDuration, + ...(metricDuration !== buffer?.metricsViewState?.metricDuration && { + metricDuration, + }), + ...(refreshRate !== buffer?.metricsViewState?.refreshRate && { + refreshRate, + }), }, }) updateBuffer(buffer.id, merged) } - }, [metricDuration]) + }, [metricDuration, refreshRate]) if (telemetryConfig && !telemetryConfig.enabled) { return ( @@ -195,6 +212,16 @@ export const Metrics = () => { {getLocalTimeZone()} + { key={index} metric={metric} metricDuration={metricDuration} + refreshRate={refreshRate} onRemove={handleRemoveMetric} onTableChange={handleTableChange} onColorChange={handleColorChange} diff --git a/packages/web-console/src/scenes/Editor/Metrics/metric.tsx b/packages/web-console/src/scenes/Editor/Metrics/metric.tsx index 57984996a..728630b10 100644 --- a/packages/web-console/src/scenes/Editor/Metrics/metric.tsx +++ b/packages/web-console/src/scenes/Editor/Metrics/metric.tsx @@ -12,6 +12,9 @@ import { MetricType, LastNotNull, ResultType, + RefreshRate, + refreshRates, + autoRefreshRates, } from "./utils" import { widgets } from "./widgets" import { QuestContext } from "../../../providers" @@ -45,6 +48,7 @@ const ActionButton = styled(Button)` export const Metric = ({ metric, metricDuration, + refreshRate, onRemove, onTableChange, onColorChange, @@ -52,6 +56,7 @@ export const Metric = ({ }: { metric: MetricItem metricDuration: MetricDuration + refreshRate: RefreshRate onRemove: (metric: MetricItem) => void onTableChange: (metric: MetricItem, tableId: number) => void onColorChange: (metric: MetricItem, color: string) => void @@ -128,7 +133,16 @@ export const Metric = ({ const setupListeners = () => { if (autoRefreshTables) { - intervalRef.current = setInterval(() => fetchMetric(), 30000) + if (refreshRate === RefreshRate.OFF) { + clearInterval(intervalRef.current) + } else { + intervalRef.current = setInterval( + () => fetchMetric(), + refreshRate === RefreshRate.AUTO + ? refreshRates[autoRefreshRates[metricDuration]] + : refreshRates[refreshRate], + ) + } window.addEventListener("focus", focusListener) focusListenerRef.current = true } else { @@ -155,7 +169,7 @@ export const Metric = ({ focusListenerRef.current = false } } - }, [autoRefreshTables, metricDuration, metric.tableId]) + }, [autoRefreshTables, metricDuration, metric.tableId, refreshRate]) const focusListener = useCallback(() => { if (focusListenerRef.current) { diff --git a/packages/web-console/src/scenes/Editor/Metrics/utils.ts b/packages/web-console/src/scenes/Editor/Metrics/utils.ts index d18739cb2..89cbb3474 100644 --- a/packages/web-console/src/scenes/Editor/Metrics/utils.ts +++ b/packages/web-console/src/scenes/Editor/Metrics/utils.ts @@ -27,6 +27,8 @@ export type Widget = { } export enum MetricDuration { + FIVE_MINUTES = "5m", + FIFTEEN_MINUTES = "15m", ONE_HOUR = "1h", THREE_HOURS = "3h", SIX_HOURS = "6h", @@ -43,7 +45,44 @@ export enum SampleBy { ONE_HOUR = "1h", } +export enum RefreshRate { + AUTO = "Auto", + ONE_SECOND = "1s", + FIVE_SECONDS = "5s", + TEN_SECONDS = "10s", + THIRTY_SECONDS = "30s", + ONE_MINUTE = "1m", + OFF = "Off", +} + +export const refreshRates: Record = { + [RefreshRate.AUTO]: 0, + [RefreshRate.OFF]: 0, + [RefreshRate.ONE_SECOND]: 1 * 1000, + [RefreshRate.FIVE_SECONDS]: 5 * 1000, + [RefreshRate.TEN_SECONDS]: 10 * 1000, + [RefreshRate.THIRTY_SECONDS]: 30 * 1000, + [RefreshRate.ONE_MINUTE]: 60 * 1000, +} + +export const autoRefreshRates: Record< + MetricDuration, + Exclude +> = { + [MetricDuration.FIVE_MINUTES]: RefreshRate.ONE_SECOND, + [MetricDuration.FIFTEEN_MINUTES]: RefreshRate.FIVE_SECONDS, + [MetricDuration.ONE_HOUR]: RefreshRate.TEN_SECONDS, + [MetricDuration.THREE_HOURS]: RefreshRate.THIRTY_SECONDS, + [MetricDuration.SIX_HOURS]: RefreshRate.THIRTY_SECONDS, + [MetricDuration.TWELVE_HOURS]: RefreshRate.THIRTY_SECONDS, + [MetricDuration.TWENTY_FOUR_HOURS]: RefreshRate.THIRTY_SECONDS, + [MetricDuration.THREE_DAYS]: RefreshRate.ONE_MINUTE, + [MetricDuration.SEVEN_DAYS]: RefreshRate.ONE_MINUTE, +} + export const durationInMinutes: Record = { + [MetricDuration.FIVE_MINUTES]: 5, + [MetricDuration.FIFTEEN_MINUTES]: 15, [MetricDuration.ONE_HOUR]: 60, [MetricDuration.THREE_HOURS]: 60 * 3, [MetricDuration.SIX_HOURS]: 60 * 6, @@ -54,6 +93,8 @@ export const durationInMinutes: Record = { } export const defaultSampleByForDuration: Record = { + [MetricDuration.FIVE_MINUTES]: SampleBy.ONE_SECOND, + [MetricDuration.FIFTEEN_MINUTES]: SampleBy.ONE_SECOND, [MetricDuration.ONE_HOUR]: SampleBy.ONE_SECOND, [MetricDuration.THREE_HOURS]: SampleBy.ONE_SECOND, [MetricDuration.SIX_HOURS]: SampleBy.ONE_SECOND, @@ -108,6 +149,10 @@ export const minutesToSeconds = (durationInMinutes: number) => durationInMinutes * 60 export const xAxisFormat = { + [MetricDuration.FIVE_MINUTES]: (rawValue: number) => + utcToLocal(rawValue, "HH:mm:ss"), + [MetricDuration.FIFTEEN_MINUTES]: (rawValue: number) => + utcToLocal(rawValue, "HH:mm"), [MetricDuration.ONE_HOUR]: (rawValue: number) => utcToLocal(rawValue, "HH:mm"), [MetricDuration.THREE_HOURS]: (rawValue: number) => diff --git a/packages/web-console/src/scenes/Editor/Metrics/widgets/utils.ts b/packages/web-console/src/scenes/Editor/Metrics/widgets/utils.ts index 0829f0b11..248e82894 100644 --- a/packages/web-console/src/scenes/Editor/Metrics/widgets/utils.ts +++ b/packages/web-console/src/scenes/Editor/Metrics/widgets/utils.ts @@ -24,8 +24,12 @@ export const minutesToSeconds = (durationInMinutes: number) => export const getTimeFilter = ( minutes: number, ) => `created > date_trunc('minute', dateadd('${ - minutes >= 1440 ? "d" : "h" + minutes >= 1440 ? "d" : minutes >= 60 ? "h" : "s" }', -${ - minutes >= 1440 ? minutesToDays(minutes) : minutesToHours(minutes) + minutes >= 1440 + ? minutesToDays(minutes) + : minutes >= 60 + ? minutesToHours(minutes) + : minutesToSeconds(minutes) }, now())) -and created < date_trunc('minute', now())` +and created < date_trunc('${minutes >= 60 ? "minute" : "second"}', now())` diff --git a/packages/web-console/src/scenes/Schema/index.tsx b/packages/web-console/src/scenes/Schema/index.tsx index d40c7d318..8dff1d3fb 100644 --- a/packages/web-console/src/scenes/Schema/index.tsx +++ b/packages/web-console/src/scenes/Schema/index.tsx @@ -71,7 +71,7 @@ import { NotificationType } from "../../types" import { Checkbox } from "./checkbox" import { AddChart } from "@styled-icons/material" import { useEditor } from "../../providers/EditorProvider" -import { MetricDuration } from "../../scenes/Editor/Metrics/utils" +import { MetricDuration, RefreshRate } from "../../scenes/Editor/Metrics/utils" type Props = Readonly<{ hideMenu?: boolean @@ -470,6 +470,7 @@ const Schema = ({ metricsViewState: { metrics: [], metricDuration: MetricDuration.ONE_HOUR, + refreshRate: RefreshRate.AUTO, }, }) }} diff --git a/packages/web-console/src/store/buffers.ts b/packages/web-console/src/store/buffers.ts index 7efc6cd20..c36b2ffda 100644 --- a/packages/web-console/src/store/buffers.ts +++ b/packages/web-console/src/store/buffers.ts @@ -1,3 +1,4 @@ +import { RefreshRate } from "./../scenes/Editor/Metrics/utils" /******************************************************************************* * ___ _ ____ ____ * / _ \ _ _ ___ ___| |_| _ \| __ ) @@ -41,6 +42,7 @@ export type Metric = { export type MetricsViewState = { metricDuration?: MetricDuration + refreshRate?: RefreshRate sampleBy?: string metrics?: Metric[] }