From f5c13cb02f1cbe760ebcb7a0c3c120eff649ded8 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Wed, 24 Apr 2024 00:33:30 +0800 Subject: [PATCH] perf: improve monitor query performance --- src/client/api/trpc.ts | 35 +++++++++--- .../components/monitor/MonitorDataMetrics.tsx | 17 ++++-- src/client/components/monitor/MonitorInfo.tsx | 6 +++ src/client/components/server/ServerList.tsx | 2 +- src/client/routes/server.tsx | 1 - src/server/model/monitor/index.bench.ts | 53 +++++++++++++++++++ src/server/model/monitor/index.ts | 29 +++++----- .../migration.sql | 8 +++ src/server/prisma/schema.prisma | 4 ++ src/server/trpc/routers/monitor.ts | 13 +++-- 10 files changed, 136 insertions(+), 32 deletions(-) create mode 100644 src/server/model/monitor/index.bench.ts create mode 100644 src/server/prisma/migrations/20240423074833_add_indexes_for_monitor/migration.sql diff --git a/src/client/api/trpc.ts b/src/client/api/trpc.ts index e91111e..42fd5e0 100644 --- a/src/client/api/trpc.ts +++ b/src/client/api/trpc.ts @@ -1,7 +1,13 @@ import { createTRPCReact, getQueryKey } from '@trpc/react-query'; import type { inferRouterInputs, inferRouterOutputs } from '../../server/trpc'; import type { AppRouter } from '../../server/trpc/routers'; -import { httpBatchLink, loggerLink, TRPCClientErrorLike } from '@trpc/client'; +import { + httpBatchLink, + httpLink, + loggerLink, + splitLink, + TRPCClientErrorLike, +} from '@trpc/client'; import { getJWT } from './auth'; import { message } from 'antd'; import { isDev } from '../utils/env'; @@ -13,6 +19,14 @@ export const trpc = createTRPCReact(); export type AppRouterInput = inferRouterInputs; export type AppRouterOutput = inferRouterOutputs; +const url = '/trpc'; + +function headers() { + return { + Authorization: `Bearer ${getJWT()}`, + }; +} + export const trpcClient = trpc.createClient({ links: [ loggerLink({ @@ -20,13 +34,20 @@ export const trpcClient = trpc.createClient({ (isDev && typeof window !== 'undefined') || (opts.direction === 'down' && opts.result instanceof Error), }), - httpBatchLink({ - url: '/trpc', - async headers() { - return { - Authorization: `Bearer ${getJWT()}`, - }; + splitLink({ + condition(op) { + // check for context property `skipBatch` + return op.context.skipBatch === true; }, + true: httpLink({ + url, + headers, + }), + // when condition is false, use batching + false: httpBatchLink({ + url, + headers, + }), }), ], }); diff --git a/src/client/components/monitor/MonitorDataMetrics.tsx b/src/client/components/monitor/MonitorDataMetrics.tsx index e80e915..257af5c 100644 --- a/src/client/components/monitor/MonitorDataMetrics.tsx +++ b/src/client/components/monitor/MonitorDataMetrics.tsx @@ -15,10 +15,19 @@ export const MonitorDataMetrics: React.FC<{ const { t } = useTranslation(); const workspaceId = useCurrentWorkspaceId(); const { monitorId, monitorType, currectResponse } = props; - const { data, isLoading } = trpc.monitor.dataMetrics.useQuery({ - workspaceId, - monitorId, - }); + const { data, isLoading } = trpc.monitor.dataMetrics.useQuery( + { + workspaceId, + monitorId, + }, + { + trpc: { + context: { + skipBatch: true, + }, + }, + } + ); const provider = useMemo( () => getMonitorProvider(monitorType), diff --git a/src/client/components/monitor/MonitorInfo.tsx b/src/client/components/monitor/MonitorInfo.tsx index 072fa73..737e51b 100644 --- a/src/client/components/monitor/MonitorInfo.tsx +++ b/src/client/components/monitor/MonitorInfo.tsx @@ -70,6 +70,9 @@ export const MonitorInfo: React.FC = React.memo((props) => { active: true, }); + trpcUtils.monitor.all.refetch({ + workspaceId, + }); trpcUtils.monitor.get.refetch({ workspaceId, monitorId, @@ -87,6 +90,9 @@ export const MonitorInfo: React.FC = React.memo((props) => { active: false, }); + trpcUtils.monitor.all.refetch({ + workspaceId, + }); trpcUtils.monitor.get.refetch({ workspaceId, monitorId, diff --git a/src/client/components/server/ServerList.tsx b/src/client/components/server/ServerList.tsx index c6eba68..194310e 100644 --- a/src/client/components/server/ServerList.tsx +++ b/src/client/components/server/ServerList.tsx @@ -139,7 +139,7 @@ export const ServerList: React.FC = React.memo((props) => { cell: (props) => dayjs(props.getValue()).format('MMM D HH:mm:ss'), }), ]; - }, []); + }, [t]); return (
diff --git a/src/client/routes/server.tsx b/src/client/routes/server.tsx index 3d49b76..04fcf54 100644 --- a/src/client/routes/server.tsx +++ b/src/client/routes/server.tsx @@ -10,7 +10,6 @@ import { AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; -import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { Switch } from '@/components/ui/switch'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; diff --git a/src/server/model/monitor/index.bench.ts b/src/server/model/monitor/index.bench.ts new file mode 100644 index 0000000..5c121eb --- /dev/null +++ b/src/server/model/monitor/index.bench.ts @@ -0,0 +1,53 @@ +import { describe, bench } from 'vitest'; +import { prisma } from '../_client'; + +const workspaceId = process.env.BENCH_MONITOR_WORKSPACEID; +const monitorId = process.env.BENCH_MONITOR_ID; + +describe.runIf(workspaceId && monitorId)('getMonitorRecentData', () => { + bench('find with join', async () => { + await prisma.monitorData + .findMany({ + where: { + monitor: { + id: monitorId, + workspaceId, + }, + }, + take: 20, + orderBy: { + createdAt: 'desc', + }, + select: { + value: true, + createdAt: true, + }, + }) + .then((arr) => arr.reverse()); + }); + + bench('find with double check', async () => { + await prisma.monitor.findFirstOrThrow({ + where: { + workspaceId, + id: monitorId, + }, + }); + + await prisma.monitorData + .findMany({ + where: { + monitorId, + }, + take: 20, + orderBy: { + createdAt: 'desc', + }, + select: { + value: true, + createdAt: true, + }, + }) + .then((arr) => arr.reverse()); + }); +}); diff --git a/src/server/model/monitor/index.ts b/src/server/model/monitor/index.ts index c81c67f..b0b2e6b 100644 --- a/src/server/model/monitor/index.ts +++ b/src/server/model/monitor/index.ts @@ -48,17 +48,22 @@ export function getMonitorRecentData( monitorId: string, take: number ) { - return prisma.monitorData.findMany({ - where: { - monitor: { - id: monitorId, - workspaceId, + return prisma.monitorData + .findMany({ + where: { + monitor: { + id: monitorId, + workspaceId, + }, }, - }, - take: -take, - select: { - value: true, - createdAt: true, - }, - }); + take, + orderBy: { + createdAt: 'desc', + }, + select: { + value: true, + createdAt: true, + }, + }) + .then((arr) => arr.reverse()); } diff --git a/src/server/prisma/migrations/20240423074833_add_indexes_for_monitor/migration.sql b/src/server/prisma/migrations/20240423074833_add_indexes_for_monitor/migration.sql new file mode 100644 index 0000000..f254784 --- /dev/null +++ b/src/server/prisma/migrations/20240423074833_add_indexes_for_monitor/migration.sql @@ -0,0 +1,8 @@ +-- CreateIndex +CREATE INDEX "MonitorData_monitorId_createdAt_idx" ON "MonitorData"("monitorId", "createdAt" DESC); + +-- CreateIndex +CREATE INDEX "MonitorData_monitorId_createdAt_value_idx" ON "MonitorData"("monitorId", "createdAt", "value"); + +-- CreateIndex +CREATE INDEX "MonitorEvent_monitorId_idx" ON "MonitorEvent"("monitorId"); diff --git a/src/server/prisma/schema.prisma b/src/server/prisma/schema.prisma index 4973e6a..15db4df 100644 --- a/src/server/prisma/schema.prisma +++ b/src/server/prisma/schema.prisma @@ -318,6 +318,8 @@ model MonitorEvent { createdAt DateTime @default(now()) @db.Timestamptz(6) monitor Monitor @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade) + + @@index([monitorId]) } model MonitorData { @@ -329,6 +331,8 @@ model MonitorData { monitor Monitor @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade) @@index([createdAt]) + @@index([monitorId, createdAt(sort: Desc)]) + @@index([monitorId, createdAt, value]) } // Use for record latest monitor status, for example tls status diff --git a/src/server/trpc/routers/monitor.ts b/src/server/trpc/routers/monitor.ts index d6dcc66..c2a4f71 100644 --- a/src/server/trpc/routers/monitor.ts +++ b/src/server/trpc/routers/monitor.ts @@ -300,14 +300,13 @@ export const monitorRouter = router({ return monitor; }), recentData: publicProcedure - .meta({ - openapi: { - tags: [OPENAPI_TAG.MONITOR], - protect: false, + .meta( + buildMonitorOpenapi({ method: 'GET', - path: `/monitor/{monitorId}/recentData`, - }, - }) + protect: false, + path: '/{monitorId}/recentData', + }) + ) .input( z.object({ workspaceId: z.string().cuid2(),