Skip to content

Commit 76179d1

Browse files
committed
improve query performance
1 parent 6ec28f1 commit 76179d1

File tree

3 files changed

+29
-101
lines changed

3 files changed

+29
-101
lines changed

dashboard/app/page.tsx

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,18 @@
11
import { BenchmarkDashboard } from "@/components/benchmark-dashboard"
2-
import { getAllDatabases, getAllFunctions, getLast30DaysStats, getStatsWithDetails } from "@/lib/db"
2+
import { getAllDatabases, getAllFunctions, getLast30DaysAvgLatency } from "@/lib/db"
33

44
// Revalidate the page every 15 minutes
55
export const revalidate = 900; // 15 minutes in seconds
66

77
export default async function Home() {
8-
// Fetch all functions first
9-
const functions = await getAllFunctions();
10-
11-
// For each function, fetch its associated databases
12-
const databasesByFunction = await Promise.all(
13-
functions.map(async (fn) => {
14-
const databases = await getAllDatabases(fn.id);
15-
return { function: fn, databases };
16-
})
17-
);
18-
19-
// Flatten all databases into a single array
20-
const allDatabases = databasesByFunction.flatMap(({ databases }) => databases);
21-
22-
// Fetch stats for all databases
23-
const allStats = await Promise.all(
24-
allDatabases.map(db => getLast30DaysStats(db.id))
25-
);
26-
27-
// Combine all stats into a single array
28-
const combinedStats = allStats.flat();
29-
30-
// Get stats with database and function details
31-
const statsWithDetails = await getStatsWithDetails(combinedStats);
8+
const [functions, databases, stats] = await Promise.all([getAllFunctions(), getAllDatabases(), getLast30DaysAvgLatency()]);
329

3310
return (
3411
<main>
3512
<BenchmarkDashboard
36-
initialDatabases={allDatabases}
13+
initialDatabases={databases}
3714
initialFunctions={functions}
38-
initialStats={statsWithDetails}
15+
initialStats={stats}
3916
/>
4017
</main>
4118
);

dashboard/components/benchmark-dashboard.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Database, Function, Stat } from "@/lib/schema"
1212
// Import Neon logos
1313
import logoLight from "../assets/logo.svg"
1414
import logoDark from "../assets/logo-dark.svg"
15+
import { AvgStat } from "@/lib/db"
1516

1617
interface LatencyData {
1718
cold: Record<string, Record<string, number>>
@@ -29,7 +30,7 @@ interface HistoricalData {
2930
interface BenchmarkDashboardProps {
3031
initialDatabases: Database[]
3132
initialFunctions: Function[]
32-
initialStats: Stat[]
33+
initialStats: AvgStat[]
3334
}
3435

3536
export function BenchmarkDashboard(props: BenchmarkDashboardProps) {
@@ -160,13 +161,6 @@ function BenchmarkDashboardClient({
160161
});
161162

162163
const latencyData = processLatencyData(initialStats, initialFunctions, initialDatabases)
163-
const historicalData = processHistoricalData(initialStats, initialFunctions, initialDatabases)
164-
165-
const today = new Date().toLocaleDateString("en-US", {
166-
year: "numeric",
167-
month: "long",
168-
day: "numeric",
169-
})
170164

171165
return (
172166
<div className="flex min-h-screen bg-background">
@@ -244,7 +238,7 @@ function BenchmarkDashboardClient({
244238
}
245239

246240
// Helper functions to process the database stats into the required formats
247-
function processLatencyData(stats: Stat[], functions: Function[], databases: Database[]): LatencyData {
241+
function processLatencyData(stats: AvgStat[], functions: Function[], databases: Database[]): LatencyData {
248242
const result: LatencyData = {
249243
cold: {},
250244
hot: {}
@@ -272,16 +266,16 @@ function processLatencyData(stats: Stat[], functions: Function[], databases: Dat
272266
acc[key].hot.push(stat)
273267
}
274268
return acc
275-
}, {} as Record<string, { cold: Stat[]; hot: Stat[] }>)
269+
}, {} as Record<string, { cold: AvgStat[]; hot: AvgStat[] }>)
276270

277271
// Calculate averages
278272
Object.entries(groupedStats).forEach(([key, { cold, hot }]) => {
279273
const [functionId, databaseId] = key.split('-').map(Number)
280274
if (cold.length > 0) {
281-
result.cold[functionId][databaseId] = cold.reduce((sum, s) => sum + Number(s.latencyMs), 0) / cold.length
275+
result.cold[functionId][databaseId] = cold.reduce((sum, s) => sum + Number(s.avgLatencyMs), 0) / cold.length
282276
}
283277
if (hot.length > 0) {
284-
result.hot[functionId][databaseId] = hot.reduce((sum, s) => sum + Number(s.latencyMs), 0) / hot.length
278+
result.hot[functionId][databaseId] = hot.reduce((sum, s) => sum + Number(s.avgLatencyMs), 0) / hot.length
285279
}
286280
})
287281

dashboard/lib/db.ts

Lines changed: 19 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,41 @@
11
import { drizzle } from "drizzle-orm/neon-http";
22
import { neon } from "@neondatabase/serverless";
33
import { config } from "dotenv";
4-
import { and, desc, eq, gte } from "drizzle-orm";
4+
import { avg, desc, gte } from "drizzle-orm";
55
import { databases, functions, stats, type Database, type Function, type Stat } from "./schema";
66

77
config({ path: ".env" });
88

99
const sql = neon(process.env.DATABASE_URL!);
1010
export const db = drizzle(sql);
1111

12-
/**
13-
* Get all databases for a specific function
14-
*/
15-
export async function getAllDatabases(functionId: number): Promise<Database[]> {
16-
return await db.select().from(databases).where(eq(databases.functionId, functionId));
12+
export async function getAllDatabases(): Promise<Database[]> {
13+
return await db.select().from(databases);
1714
}
1815

19-
/**
20-
* Get all functions
21-
*/
2216
export async function getAllFunctions(): Promise<Function[]> {
2317
return await db.select().from(functions);
2418
}
2519

26-
/**
27-
* Get stats for the last 24 hours for a specific database
28-
*/
29-
export async function getLastDayStats(databaseId: number): Promise<Stat[]> {
30-
const oneDayAgo = new Date();
31-
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
20+
export type AvgStat = {
21+
functionId: number;
22+
databaseId: number;
23+
queryType: 'cold' | 'hot';
24+
avgLatencyMs: string | null; // `decimal` columns come back as strings
25+
};
3226

33-
return await db
34-
.select()
35-
.from(stats)
36-
.where(
37-
and(
38-
eq(stats.databaseId, databaseId),
39-
gte(stats.dateTime, oneDayAgo)
40-
)
41-
)
42-
.orderBy(desc(stats.dateTime));
43-
}
44-
45-
/**
46-
* Get stats for the last 30 days for a specific database
47-
*/
48-
export async function getLast30DaysStats(databaseId: number): Promise<Stat[]> {
27+
export async function getLast30DaysAvgLatency(): Promise<AvgStat[]> {
4928
const thirtyDaysAgo = new Date();
5029
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
5130

52-
return await db
53-
.select()
31+
return db
32+
.select({
33+
functionId: stats.functionId,
34+
databaseId: stats.databaseId,
35+
queryType: stats.queryType,
36+
avgLatencyMs: avg(stats.latencyMs).as('avg_latency_ms'),
37+
})
5438
.from(stats)
55-
.where(
56-
and(
57-
eq(stats.databaseId, databaseId),
58-
gte(stats.dateTime, thirtyDaysAgo)
59-
)
60-
)
61-
.orderBy(desc(stats.dateTime));
39+
.where(gte(stats.dateTime, thirtyDaysAgo))
40+
.groupBy(stats.functionId, stats.databaseId, stats.queryType);
6241
}
63-
64-
/**
65-
* Get stats with database and function details
66-
*/
67-
export async function getStatsWithDetails(stats: Stat[]): Promise<(Stat & { database: Database; function: Function })[]> {
68-
const databaseIds = new Set(stats.map(stat => stat.databaseId));
69-
const functionIds = new Set(stats.map(stat => stat.functionId));
70-
71-
const [dbDetails, functionDetails] = await Promise.all([
72-
db.select().from(databases).where(eq(databases.id, Array.from(databaseIds)[0])),
73-
db.select().from(functions).where(eq(functions.id, Array.from(functionIds)[0]))
74-
]);
75-
76-
const dbMap = new Map(dbDetails.map(db => [db.id, db]));
77-
const functionMap = new Map(functionDetails.map(fn => [fn.id, fn]));
78-
79-
return stats.map(stat => ({
80-
...stat,
81-
database: dbMap.get(stat.databaseId)!,
82-
function: functionMap.get(stat.functionId)!
83-
}));
84-
}

0 commit comments

Comments
 (0)