Skip to content

Commit

Permalink
feat: show chart sql dialog, add new-parts-created chart
Browse files Browse the repository at this point in the history
  • Loading branch information
duyet committed Nov 30, 2023
1 parent 1edab95 commit aa0afed
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 65 deletions.
28 changes: 28 additions & 0 deletions app/[query]/clickhouse-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@ import type { QueryConfig } from '@/lib/types/query-config'
import { ColumnFormat } from '@/components/data-table/columns'

export const queries: Array<QueryConfig> = [
{
name: 'global-table-overview',
sql: `
SELECT
table,
sum(rows) AS rows,
max(modification_time) AS latest_modification,
formatReadableSize(sum(bytes)) AS data_size,
formatReadableSize(sum(primary_key_bytes_in_memory)) AS primary_keys_size,
any(engine) AS engine,
sum(bytes) AS bytes_size
FROM clusterAllReplicas(default, system.parts)
WHERE active
GROUP BY
database,
table
ORDER BY bytes_size DESC
`,
columns: [
'table',
'rows',
'data_size',
'latest_modification',
'primary_keys_size',
'engine',
'bytes_size',
],
},
{
name: 'running-queries',
sql: `
Expand Down
32 changes: 32 additions & 0 deletions app/charts/[chart]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { notFound } from 'next/navigation'

import { RelatedCharts } from '@/components/related-charts'

interface PageProps {
params: {
chart: string
}
}

export const dynamic = 'force-dynamic'
export const revalidate = 30

export default async function Page({ params: { chart } }: PageProps) {
const title = chart.replace(/-/g, ' ')
let Chart
let props = {}

try {
Chart = (await import(`@/components/charts/${chart}`)).default
} catch (e) {
return notFound()
}

return (
<Chart
className="w-full p-0 shadow-none"
chartClassName="h-96"
{...props}
/>
)
}
2 changes: 1 addition & 1 deletion app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function Error({
return (
<ErrorAlert
title="Something went wrong"
message="Checking console for more details."
message={error.message}
reset={reset}
/>
)
Expand Down
11 changes: 9 additions & 2 deletions app/overview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ChartAvgMemory } from '@/components/charts/avg-memory'
import { ChartDisksUsage } from '@/components/charts/disks-usage'
import { ChartMergeCount } from '@/components/charts/merge-count'
import ChartNewPartCreated from '@/components/charts/new-parts-created'
import { ChartQueryCountByUser } from '@/components/charts/query-count-by-user'
import { ChartTopTableSize } from '@/components/charts/top-table-size'
import { IntervalSelect } from '@/components/interval-select'
Expand Down Expand Up @@ -47,10 +48,16 @@ export default async function Overview() {
/>
<ChartTopTableSize className="w-full p-5" />
<ChartDisksUsage
title="Disks Usage over last 14 days"
className="w-full p-5"
title="Disks Usage over last 30 days"
interval="toStartOfDay"
lastHours={24 * 30}
/>
<ChartNewPartCreated
className="w-full p-5"
title="New Parts Created over last 24 hours"
interval="toStartOfHour"
lastHours={24 * 14}
lastHours={24}
/>
</div>
</TabsContent>
Expand Down
26 changes: 23 additions & 3 deletions components/chart-card.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
import { CodeIcon } from '@radix-ui/react-icons'

import { Button } from '@/components/ui/button'
import {
Card,
CardContent,
CardDescription,
CardHeader,
} from '@/components/ui/card'
import { DialogSQL } from '@/components/dialog-sql'

interface ChartCardProps {
title?: string | React.ReactNode
children: string | React.ReactNode
className?: string
sql?: string
children: string | React.ReactNode
}

export function ChartCard({ title, className, children }: ChartCardProps) {
export function ChartCard({ title, className, sql, children }: ChartCardProps) {
return (
<Card className={className}>
{title ? (
<CardHeader className="p-2">
<CardDescription>{title}</CardDescription>
<CardDescription className="group flex flex-row items-center justify-between">
{title}
<DialogSQL
button={
<Button
className="group-hover:border-1 border-0"
size="sm"
variant="ghost"
>
<CodeIcon className="h-3 w-3" />
</Button>
}
sql={sql}
description="SQL Query for this chart"
/>
</CardDescription>
</CardHeader>
) : null}

Expand Down
8 changes: 4 additions & 4 deletions components/charts/avg-memory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ export async function ChartAvgMemory({
lastHours = 24,
className,
}: ChartProps) {
const data = await fetchData(`
const sql = `
SELECT ${interval}(event_time) as event_time,
avg(CurrentMetric_MemoryTracking) AS avg_memory,
formatReadableSize(avg_memory) AS readable_avg_memory
FROM system.metric_log
WHERE event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1
ORDER BY 1 ASC
`)
ORDER BY 1 ASC`
const data = await fetchData(sql)

return (
<ChartCard title={title} className={className}>
<ChartCard title={title} className={className} sql={sql}>
<AreaChart
data={data}
index="event_time"
Expand Down
28 changes: 18 additions & 10 deletions components/charts/disks-usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,41 @@ import type { ChartProps } from '@/components/charts/chart-props'
import { AreaChart } from '@/components/tremor'

export async function ChartDisksUsage({
title = 'Disks Usage over last 7 days',
interval = 'toStartOfHour',
title = 'Disks Usage over last 30 days',
interval = 'toStartOfDay',
className,
chartClassName,
lastHours = 24 * 7,
lastHours = 24 * 30,
...props
}: ChartProps) {
const data = await fetchData(`
const sql = `
WITH CAST(sumMap(map(metric, value)), 'Map(LowCardinality(String), UInt32)') AS map
SELECT
${interval}(event_time) as event_time,
map['DiskAvailable_default'] as DiskAvailable_default,
formatReadableSize(DiskAvailable_default) as readable_DiskAvailable_default
map['DiskUsed_default'] as DiskUsed_default,
formatReadableSize(DiskAvailable_default) as readable_DiskAvailable_default,
formatReadableSize(DiskUsed_default) as readable_DiskUsed_default
FROM system.asynchronous_metric_log
WHERE event_time >= (now() - INTERVAL ${lastHours} HOUR)
WHERE event_time >= (now() - toIntervalHour(${lastHours}))
GROUP BY 1
ORDER BY 1 ASC
`)
`

const data = await fetchData(sql)

return (
<ChartCard title={title} className={className}>
<ChartCard title={title} className={className} sql={sql}>
<AreaChart
className={cn('h-52', chartClassName)}
data={data}
index="event_time"
categories={['DiskAvailable_default']}
readableColumns={['readable_DiskAvailable_default']}
categories={['DiskAvailable_default', 'DiskUsed_default']}
readable={true}
readableColumns={[
'readable_DiskAvailable_default',
'readable_DiskUsed_default',
]}
{...props}
/>
</ChartCard>
Expand Down
8 changes: 5 additions & 3 deletions components/charts/merge-count.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ export async function ChartMergeCount({
className,
chartClassName,
}: ChartProps) {
const data = await fetchData(`
const sql = `
SELECT ${interval}(event_time) AS event_time,
avg(CurrentMetric_Merge) AS avg_CurrentMetric_Merge,
avg(CurrentMetric_PartMutation) AS avg_CurrentMetric_PartMutation
FROM system.metric_log
WHERE event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1
ORDER BY 1
`)
`

const data = await fetchData(sql)

return (
<ChartCard title={title} className={className}>
<ChartCard title={title} className={className} sql={sql}>
<AreaChart
className={cn('h-52', chartClassName)}
data={data}
Expand Down
68 changes: 68 additions & 0 deletions components/charts/new-parts-created.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { fetchData } from '@/lib/clickhouse'
import { ChartCard } from '@/components/chart-card'
import type { ChartProps } from '@/components/charts/chart-props'
import { BarChart } from '@/components/tremor'

export async function ChartNewPartCreated({
title = 'New Parts Created over last 24 hours (part counts / 15 minutes)',
interval = 'toStartOfFifteenMinutes',
lastHours = 24,
className,
chartClassName,
...props
}: ChartProps) {
const sql = `
SELECT
count() AS new_parts,
${interval}(event_time) AS event_time,
table,
sum(rows) AS total_written_rows,
formatReadableQuantity(total_written_rows) AS readable_total_written_rows,
sum(size_in_bytes) AS total_bytes_on_disk,
formatReadableSize(total_bytes_on_disk) AS readable_total_bytes_on_disk
FROM clusterAllReplicas(default, system.part_log)
WHERE (event_type = 'NewPart') AND (event_time > (now() - toIntervalHour(${lastHours})))
GROUP BY
event_time,
table
ORDER BY
event_time ASC,
table DESC
`

const raw = await fetchData(sql)

const data = raw.reduce((acc, cur) => {
const { event_time, table, new_parts } = cur
if (acc[event_time] === undefined) {
acc[event_time] = {}
}
acc[event_time][table] = new_parts
return acc
}, {}) as Record<string, Record<string, number>>

const barData = Object.entries(data).map(([event_time, obj]) => {
return { event_time, ...obj }
})

// All users
const tables = Object.values(data).reduce((acc, cur) => {
return Array.from(new Set([...acc, ...Object.keys(cur)]))
}, [] as string[])

return (
<ChartCard title={title} className={className} sql={sql}>
<BarChart
className={chartClassName}
data={barData}
index="event_time"
categories={tables}
readable="quantity"
stack
{...props}
/>
</ChartCard>
)
}

export default ChartNewPartCreated
7 changes: 4 additions & 3 deletions components/charts/query-count-by-user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function ChartQueryCountByUser({
chartClassName,
...props
}: ChartProps) {
const raw = await fetchData(`
const sql = `
SELECT ${interval}(event_time) AS event_time,
user,
COUNT(*) AS count
Expand All @@ -20,7 +20,8 @@ export async function ChartQueryCountByUser({
AND event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1, 2
ORDER BY 1 ASC, 3 DESC
`)
`
const raw = await fetchData(sql)

const data = raw.reduce((acc, cur) => {
const { event_time, user, count } = cur
Expand All @@ -41,7 +42,7 @@ export async function ChartQueryCountByUser({
}, [] as string[])

return (
<ChartCard title={title} className={className}>
<ChartCard title={title} className={className} sql={sql}>
<BarChart
className={chartClassName}
data={barData}
Expand Down
7 changes: 4 additions & 3 deletions components/charts/query-count.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ export async function ChartQueryCount({
lastHours = 24,
...props
}: ChartProps) {
const data = await fetchData(`
const sql = `
SELECT ${interval}(event_time) AS event_time,
COUNT() AS query_count
FROM system.query_log
WHERE type = 'QueryFinish'
AND event_time >= (now() - INTERVAL ${lastHours} HOUR)
GROUP BY 1
ORDER BY 1
`)
`
const data = await fetchData(sql)

return (
<ChartCard title={title} className={className}>
<ChartCard title={title} className={className} sql={sql}>
<AreaChart
className={cn('h-52', chartClassName)}
data={data}
Expand Down
2 changes: 1 addition & 1 deletion components/charts/top-table-size.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export async function ChartTopTableSize({
return (
<ChartCard title={title} className={className}>
<Tabs defaultValue="by-size">
<TabsList>
<TabsList className="mb-5">
<TabsTrigger value="by-size">Top tables by Size</TabsTrigger>
<TabsTrigger value="by-count">Top tables by Row Count</TabsTrigger>
</TabsList>
Expand Down
Loading

0 comments on commit aa0afed

Please sign in to comment.