Skip to content

Commit

Permalink
feat: add top tables
Browse files Browse the repository at this point in the history
  • Loading branch information
duyet committed Nov 19, 2023
1 parent 931a993 commit 92cf3c7
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 15 deletions.
16 changes: 8 additions & 8 deletions app/[name]/clickhouse-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ export const queries: Array<QueryConfig> = [
query_id: ColumnFormat.Action,
},
relatedCharts: [
[
'query-count',
{
title: 'Total Running Queries over last 12 hours (query / 5 minutes)',
interval: 'toStartOfFiveMinutes',
lastHours: 12,
},
],
[
'query-count-by-user',
{
Expand All @@ -40,14 +48,6 @@ export const queries: Array<QueryConfig> = [
showLegend: false,
},
],
[
'query-count',
{
title: 'Total Running Queries over last 12 hours (query / 5 minutes)',
interval: 'toStartOfFiveMinutes',
lastHours: 12,
},
],
],
},
{
Expand Down
8 changes: 5 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ChartAvgMemory } from '@/components/charts/avg-memory'
import ChartMergeCount from '@/components/charts/merge-count'
import { ChartQueryCountByUser } from '@/components/charts/query-count-by-user'
import ChartTopTableSize from '@/components/charts/top-table-size'
import { IntervalSelect } from '@/components/interval-select'

export default function Home() {
Expand All @@ -18,18 +19,19 @@ export default function Home() {
</div>
</div>
<TabsContent value="overview" className="space-y-4">
<div className="grid grid-cols-2 items-center gap-5">
<div className="grid grid-cols-2 items-stretch gap-5">
<ChartAvgMemory
title="Avg Memory"
className="w-full"
chartClassName="h-72"
/>
<ChartQueryCountByUser title="Query Count" className="w-full" />
<ChartQueryCountByUser title="Query Count" className="w-full p-5" />
<ChartMergeCount
title="Merge and PartMutation (Avg)"
className="w-full"
className="w-full p-5"
chartClassName="h-72"
/>
<ChartTopTableSize className="w-full p-5" />
</div>
</TabsContent>
</Tabs>
Expand Down
3 changes: 2 additions & 1 deletion components/charts/avg-memory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ChartCard } from '../chart-card'
export async function ChartAvgMemory({
title,
interval = 'toStartOfTenMinutes',
lastHours = 24,
className,
chartClassName,
}: ChartProps) {
Expand All @@ -16,7 +17,7 @@ export async function ChartAvgMemory({
avg(CurrentMetric_MemoryTracking) AS avg_memory,
formatReadableSize(avg_memory) AS readable_avg_memory
FROM system.metric_log
WHERE event_time >= (now() - INTERVAL 1 DAY)
WHERE event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1
ORDER BY 1 ASC
`)
Expand Down
4 changes: 2 additions & 2 deletions components/charts/merge-count.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function ChartMergeCountSpark({
const data = await fetchData(`
SELECT ${interval}(event_time) AS event_time,
avg(CurrentMetric_Merge) AS avg_CurrentMetric_Merge,
avg(ProfileEvent_MergedRows) AS avg_ProfileEvent_MergedRows
avg(CurrentMetric_PartMutation) AS avg_CurrentMetric_PartMutation
FROM system.metric_log
WHERE event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1
Expand All @@ -29,7 +29,7 @@ export async function ChartMergeCountSpark({
index="event_time"
categories={[
'avg_CurrentMetric_Merge',
'avg_ProfileEvent_MergedRows',
'avg_CurrentMetric_PartMutation',
]}
readable="quantity"
stack
Expand Down
92 changes: 92 additions & 0 deletions components/charts/top-table-size.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { fetchData } from '@/lib/clickhouse'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ChartCard } from '@/components/chart-card'
import type { ChartProps } from '@/components/charts/chart-props'
import { BarList } from '@/components/tremor'

export async function ChartTopTableSize({
title,
className,
...props
}: ChartProps) {
const limit = 7
const topBySizeQuery = fetchData(`
SELECT
(database || '.' || table) as table,
sum(data_compressed_bytes) as compressed_bytes,
sum(data_uncompressed_bytes) AS uncompressed_bytes,
formatReadableSize(compressed_bytes) AS compressed,
formatReadableSize(uncompressed_bytes) AS uncompressed,
round(uncompressed_bytes / compressed_bytes, 2) AS compr_rate,
sum(rows) AS total_rows,
formatReadableQuantity(sum(rows)) AS readable_total_rows,
count() AS part_count
FROM system.parts
WHERE (active = 1) AND (database != 'system') AND (table LIKE '%')
GROUP BY 1
ORDER BY compressed_bytes DESC
LIMIT ${limit}
`)

const topByRowCountQuery = fetchData(`
SELECT
(database || '.' || table) as table,
sum(data_compressed_bytes) as compressed_bytes,
sum(data_uncompressed_bytes) AS uncompressed_bytes,
formatReadableSize(compressed_bytes) AS compressed,
formatReadableSize(uncompressed_bytes) AS uncompressed,
round(uncompressed_bytes / compressed_bytes, 2) AS compr_rate,
sum(rows) AS total_rows,
formatReadableQuantity(sum(rows)) AS readable_total_rows,
count() AS part_count
FROM system.parts
WHERE (active = 1) AND (database != 'system') AND (table LIKE '%')
GROUP BY 1
ORDER BY total_rows DESC
LIMIT ${limit}
`)

const [topBySize, topByRowCount] = await Promise.all([
topBySizeQuery,
topByRowCountQuery,
])

const dataTopBySize = topBySize.map((row) => ({
name: row.table,
value: row.compressed_bytes,
compressed: row.compressed,
}))

const dataTopByCount = topByRowCount.map((row) => ({
name: row.table,
value: row.total_rows,
readable_total_rows: row.readable_total_rows,
}))

return (
<ChartCard title={title} className={className}>
<Tabs defaultValue="by-size">
<TabsList>
<TabsTrigger value="by-size">Top tables by Size</TabsTrigger>
<TabsTrigger value="by-count">Top tables by Row Count</TabsTrigger>
</TabsList>
<TabsContent value="by-size">
<BarList
data={dataTopBySize}
formatedColumn="compressed"
{...props}
/>
</TabsContent>
<TabsContent value="by-count">
<BarList
data={dataTopByCount}
formatedColumn="readable_total_rows"
{...props}
/>
</TabsContent>
</Tabs>
</ChartCard>
)
}

export default ChartTopTableSize
6 changes: 6 additions & 0 deletions components/data-table/cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ import {
} from '@/components/ui/dropdown-menu'
import { ColumnFormat } from '@/components/data-table/columns'

const CODE_TRUNCATE_LENGTH = 50

export const formatCell = (row: any, value: any, format: ColumnFormat) => {
switch (format) {
case ColumnFormat.Code:
return <code>{value}</code>

case ColumnFormat.CodeToggle:
if (value.length < CODE_TRUNCATE_LENGTH) {
return <code>{value}</code>
}

return (
<Accordion type="single" collapsible={row.getIsExpanded()}>
<AccordionItem value="code" className="border-0">
Expand Down
2 changes: 1 addition & 1 deletion components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function Header() {
return (
<div className="flex items-center justify-between space-y-2">
<div>
<h2 className="flex flex-row items-center text-2xl font-bold tracking-tight min-w-32">
<h2 className="min-w-32 flex flex-row items-center text-2xl font-bold tracking-tight">
<Image
src="/logo.svg"
width={40}
Expand Down
25 changes: 25 additions & 0 deletions components/tremor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import React from 'react'
import {
AreaChart as TremorAreaChart,
BarChart as TremorBarChart,
BarList as TremorBarList,
} from '@tremor/react'
import type {
AreaChartProps as TremorAreaChartProps,
BarChartProps as TremorBarChartProps,
BarListProps as TremorBarListProps,
} from '@tremor/react'

import {
Expand Down Expand Up @@ -78,3 +80,26 @@ export function BarChart({
/>
)
}

export interface BarListProps extends TremorBarListProps {
data: (TremorBarListProps['data'][0] & { [key: string]: any })[]
formatedColumn?: string
}

export function BarList({ data, formatedColumn, ...props }: BarListProps) {
let valueFormatter

if (formatedColumn) {
valueFormatter = (value: number) => {
const formated = data.find((d) => d.value === value)?.[
formatedColumn
] as string

return formated ? formated : value
}
}

return (
<TremorBarList data={data} valueFormatter={valueFormatter} {...props} />
)
}

0 comments on commit 92cf3c7

Please sign in to comment.