Skip to content

Commit

Permalink
Abstract out widgets, add Commit Rate
Browse files Browse the repository at this point in the history
  • Loading branch information
insmac committed Dec 3, 2024
1 parent afdc72c commit ce8db48
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 116 deletions.
64 changes: 64 additions & 0 deletions packages/web-console/assets/metric-commit-rate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import {
Button,
Overlay,
} from "@questdb/react-components"
import { Undo } from "@styled-icons/boxicons-regular"
import { Text } from "../../../components/Text"
import styled from "styled-components"
import { AddChart } from "@styled-icons/material"
import { MetricType, metricTypeLabel } from "./utils"
import { MetricType } from "./utils"
import { useEditor } from "../../../providers"
import merge from "lodash.merge"
import { defaultColor, getColorForNewMetric } from "./color-palette"
import { widgets } from "./widgets"

const StyledDescription = styled(Dialog.Description)`
display: grid;
Expand Down Expand Up @@ -116,37 +116,21 @@ export const AddMetricDialog = ({ open, onOpenChange }: Props) => {

<StyledDescription>
<Metrics>
{[
{
label: metricTypeLabel[MetricType.LATENCY],
value: MetricType.LATENCY,
iconUrl: "/assets/metric-read-latency.svg",
},
{
label: metricTypeLabel[MetricType.WRITE_AMPLIFICATION],
value: MetricType.WRITE_AMPLIFICATION,
iconUrl: "/assets/metric-write-amplification.svg",
},
{
label: metricTypeLabel[MetricType.ROWS_APPLIED],
value: MetricType.ROWS_APPLIED,
iconUrl: "/assets/metric-rows-applied.svg",
},
].map((metric) => (
{Object.entries(widgets).map(([metricType, widget]) => (
<Metric
key={metric.value}
onClick={() => handleSelectMetric(metric.value)}
key={metricType}
onClick={() => handleSelectMetric(metricType as MetricType)}
>
<Image>
<img
src={metric.iconUrl}
alt={`${metric.label} icon`}
src={widget.iconUrl}
alt={`${widget.label} icon`}
width="64"
height="64"
/>
</Image>
<Text color="foreground" weight={600}>
{metric.label}
{widget.label}
</Text>
</Metric>
))}
Expand Down
75 changes: 20 additions & 55 deletions packages/web-console/src/scenes/Editor/Metrics/metric.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,13 @@ import React, {
import { Metric as MetricItem } from "../../../store/buffers"
import {
durationInMinutes,
graphDataConfigs,
MetricDuration,
MetricType,
Latency,
RowsApplied,
metricTypeLabel,
LastNotNull,
ResultType,
} from "./utils"
import { widgets } from "./widgets"
import { QuestContext } from "../../../providers"
import {
latency as latencySQL,
rowsApplied as rowsAppliedSQL,
latencyLastNotNull as latencyLasNotNullSQL,
rowsAppliedLastNotNull as rowsAppliedLastNotNullSQL,
} from "./queries"
import * as QuestDB from "../../../utils/questdb"
import { Graph } from "./graph"
import uPlot from "uplot"
Expand Down Expand Up @@ -79,64 +71,37 @@ export const Metric = ({

const { autoRefreshTables } = useLocalStorage()

const widgetConfig = widgets[metric.metricType]

const minuteDurations: [MetricDuration, number][] = Object.entries(
durationInMinutes,
) as [MetricDuration, number][]

const fetchLatency = async () => {
if (!metric.tableId) return Promise.reject()
return quest.query<Latency>(
latencySQL(metric.tableId, metricDurationRef.current),
)
}

const fetchLatencyLastNotNull = async () => {
if (!metric.tableId) return Promise.reject()
return quest.query<LastNotNull>(latencyLasNotNullSQL(metric.tableId))
}

const fetchRowsApplied = async () => {
if (!metric.tableId) return Promise.reject()
return quest.query<RowsApplied>(
rowsAppliedSQL(metric.tableId, metricDurationRef.current),
)
}

const fetchRowsAppliedLastNotNull = async () => {
if (!metric.tableId) return Promise.reject()
return quest.query<LastNotNull>(rowsAppliedLastNotNullSQL(metric.tableId))
}

const fetchers = {
[MetricType.LATENCY]: fetchLatency,
[MetricType.ROWS_APPLIED]: fetchRowsApplied,
[MetricType.WRITE_AMPLIFICATION]: fetchRowsApplied,
}

const fetchersLastNotNull = {
[MetricType.LATENCY]: fetchLatencyLastNotNull,
[MetricType.ROWS_APPLIED]: fetchRowsAppliedLastNotNull,
[MetricType.WRITE_AMPLIFICATION]: fetchRowsAppliedLastNotNull,
}

const fetchMetric = async () => {
setLoading(true)
try {
const responses = await Promise.all<
| QuestDB.QueryResult<RowsApplied>
| QuestDB.QueryResult<Latency>
| QuestDB.QueryResult<ResultType[MetricType]>
| QuestDB.QueryResult<LastNotNull>
>([
fetchers[metric.metricType](),
fetchersLastNotNull[metric.metricType](),
quest.query<any>(
widgetConfig.getQuery({
tableId: metric.tableId,
metricDuration: metricDurationRef.current,
}),
),
quest.query<LastNotNull>(
widgetConfig.getQueryLastNotNull(metric.tableId),
),
])

if (responses[0] && responses[0].type === QuestDB.Type.DQL) {
const alignedData = graphDataConfigs[metric.metricType].alignData(
responses[0].data as any,
const alignedData = widgetConfig.alignData(
responses[0].data as unknown as ResultType[MetricType],
)
setData(alignedData)
}

if (responses[1] && responses[1].type === QuestDB.Type.DQL) {
const lastNotNull = responses[1].data[0] as LastNotNull
if (lastNotNull) {
Expand Down Expand Up @@ -202,7 +167,7 @@ export const Metric = ({
return (
<MetricInfoRoot>
<Error size="18px" />
Cannot load metric: {metricTypeLabel[metric.metricType]}
Cannot load metric: {widgetConfig.label}
</MetricInfoRoot>
)

Expand All @@ -224,8 +189,8 @@ export const Metric = ({
colors={[metric.color]}
loading={loading}
duration={metricDuration}
label={metricTypeLabel[metric.metricType]}
yValue={graphDataConfigs[metric.metricType].mapYValue}
label={widgetConfig.label}
yValue={widgetConfig.mapYValue}
beforeLabel={
<TableSelector
loading={loading}
Expand Down
74 changes: 37 additions & 37 deletions packages/web-console/src/scenes/Editor/Metrics/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ import { utcToLocal } from "../../../utils/dateTime"
import uPlot from "uplot"

export enum MetricType {
ROWS_APPLIED = "Rows applied",
COMMIT_RATE = "Commit rate",
WRITE_THROUGHPUT = "Rows applied",
LATENCY = "Latency",
WRITE_AMPLIFICATION = "Write amplification",
}

export const metricTypeLabel: Record<MetricType, string> = {
[MetricType.ROWS_APPLIED]: "Write throughput",
[MetricType.LATENCY]: "WAL apply latency in ms",
[MetricType.WRITE_AMPLIFICATION]: "Write amplification",
export type Widget = {
label: string
iconUrl: string
getQuery: ({
tableId,
metricDuration,
sampleBy,
}: {
tableId?: number
metricDuration: MetricDuration
sampleBy?: SampleBy
}) => string
getQueryLastNotNull: (id?: number) => string
alignData: (data: any) => uPlot.AlignedData
mapYValue: (rawValue: number) => string
}

export enum MetricDuration {
Expand Down Expand Up @@ -50,6 +62,17 @@ export const mappedSampleBy: Record<MetricDuration, SampleBy> = {
[MetricDuration.SEVEN_DAYS]: SampleBy.ONE_MINUTE,
}

export type CommitRate = {
created: string
commit_rate: string
commit_rate_smooth: string
}

export type WriteAmplification = {
created: string
writeAmplification: string
}

export type RowsApplied = {
time: string
numOfWalApplies: string
Expand All @@ -68,6 +91,13 @@ export type LastNotNull = {
created: string
}

export type ResultType = {
[MetricType.COMMIT_RATE]: CommitRate
[MetricType.LATENCY]: Latency
[MetricType.WRITE_THROUGHPUT]: RowsApplied
[MetricType.WRITE_AMPLIFICATION]: RowsApplied
}

export const minutesToDays = (durationInMinutes: number) =>
durationInMinutes / 60 / 24

Expand All @@ -94,46 +124,16 @@ export const xAxisFormat = {
utcToLocal(rawValue, "dd/MM"),
}

const sqlValueToFixed = (value: string, decimals: number = 2) => {
export const sqlValueToFixed = (value: string, decimals: number = 2) => {
const parsed = parseFloat(value)
return Number(parsed.toFixed(decimals)) as unknown as number
}

const formatNumbers = (value: number) => {
export const formatNumbers = (value: number) => {
if (value >= 1e6) {
return (value / 1e6).toFixed(1).replace(/\.0$/, "") + " M"
} else if (value >= 1e3) {
return (value / 1e3).toFixed(1).replace(/\.0$/, "") + " k"
}
return value.toString()
}

export const graphDataConfigs = {
[MetricType.LATENCY]: {
alignData: (latency: Latency[]): uPlot.AlignedData => [
latency.map((l) => new Date(l.time).getTime()),
latency.map((l) => sqlValueToFixed(l.avg_latency)),
],
mapYValue: (rawValue: number) => {
if (rawValue >= 1000) {
const seconds = rawValue / 1000
return `${seconds.toFixed(2)} s`
}
return `${rawValue} ms`
},
},
[MetricType.ROWS_APPLIED]: {
alignData: (rowsApplied: RowsApplied[]): uPlot.AlignedData => [
rowsApplied.map((l) => new Date(l.time).getTime()),
rowsApplied.map((l) => sqlValueToFixed(l.numOfRowsApplied)),
],
mapYValue: (rawValue: number) => formatNumbers(rawValue),
},
[MetricType.WRITE_AMPLIFICATION]: {
alignData: (rowsApplied: RowsApplied[]): uPlot.AlignedData => [
rowsApplied.map((l) => new Date(l.time).getTime()),
rowsApplied.map((l) => sqlValueToFixed(l.avgWalAmplification)),
],
mapYValue: (rawValue: number) => formatNumbers(rawValue),
},
}
Loading

0 comments on commit ce8db48

Please sign in to comment.