feat: performance tracker

This commit is contained in:
max_richter 2023-08-13 00:34:03 +02:00
parent 1cf0d91af5
commit 59ddcb64a6
7 changed files with 269 additions and 72 deletions

View File

@ -5,38 +5,40 @@
import * as $0 from "./routes/_404.tsx";
import * as $1 from "./routes/_app.tsx";
import * as $2 from "./routes/_middleware.ts";
import * as $3 from "./routes/api/articles/[name].ts";
import * as $4 from "./routes/api/articles/create/index.ts";
import * as $5 from "./routes/api/articles/index.ts";
import * as $6 from "./routes/api/auth/callback.ts";
import * as $7 from "./routes/api/auth/login.ts";
import * as $8 from "./routes/api/auth/logout.ts";
import * as $9 from "./routes/api/cache/index.ts";
import * as $10 from "./routes/api/images/index.ts";
import * as $11 from "./routes/api/index.ts";
import * as $12 from "./routes/api/movies/[name].ts";
import * as $13 from "./routes/api/movies/enhance/[name].ts";
import * as $14 from "./routes/api/movies/index.ts";
import * as $15 from "./routes/api/query/index.ts";
import * as $16 from "./routes/api/query/sync.ts";
import * as $17 from "./routes/api/recipes/[name].ts";
import * as $18 from "./routes/api/recipes/index.ts";
import * as $19 from "./routes/api/resources.ts";
import * as $20 from "./routes/api/series/[name].ts";
import * as $21 from "./routes/api/series/enhance/[name].ts";
import * as $22 from "./routes/api/series/index.ts";
import * as $23 from "./routes/api/tmdb/[id].ts";
import * as $24 from "./routes/api/tmdb/credits/[id].ts";
import * as $25 from "./routes/api/tmdb/query.ts";
import * as $26 from "./routes/articles/[name].tsx";
import * as $27 from "./routes/articles/index.tsx";
import * as $28 from "./routes/index.tsx";
import * as $29 from "./routes/movies/[name].tsx";
import * as $30 from "./routes/movies/index.tsx";
import * as $31 from "./routes/recipes/[name].tsx";
import * as $32 from "./routes/recipes/index.tsx";
import * as $33 from "./routes/series/[name].tsx";
import * as $34 from "./routes/series/index.tsx";
import * as $3 from "./routes/admin/performance/api.ts";
import * as $4 from "./routes/admin/performance/index.tsx";
import * as $5 from "./routes/api/articles/[name].ts";
import * as $6 from "./routes/api/articles/create/index.ts";
import * as $7 from "./routes/api/articles/index.ts";
import * as $8 from "./routes/api/auth/callback.ts";
import * as $9 from "./routes/api/auth/login.ts";
import * as $10 from "./routes/api/auth/logout.ts";
import * as $11 from "./routes/api/cache/index.ts";
import * as $12 from "./routes/api/images/index.ts";
import * as $13 from "./routes/api/index.ts";
import * as $14 from "./routes/api/movies/[name].ts";
import * as $15 from "./routes/api/movies/enhance/[name].ts";
import * as $16 from "./routes/api/movies/index.ts";
import * as $17 from "./routes/api/query/index.ts";
import * as $18 from "./routes/api/query/sync.ts";
import * as $19 from "./routes/api/recipes/[name].ts";
import * as $20 from "./routes/api/recipes/index.ts";
import * as $21 from "./routes/api/resources.ts";
import * as $22 from "./routes/api/series/[name].ts";
import * as $23 from "./routes/api/series/enhance/[name].ts";
import * as $24 from "./routes/api/series/index.ts";
import * as $25 from "./routes/api/tmdb/[id].ts";
import * as $26 from "./routes/api/tmdb/credits/[id].ts";
import * as $27 from "./routes/api/tmdb/query.ts";
import * as $28 from "./routes/articles/[name].tsx";
import * as $29 from "./routes/articles/index.tsx";
import * as $30 from "./routes/index.tsx";
import * as $31 from "./routes/movies/[name].tsx";
import * as $32 from "./routes/movies/index.tsx";
import * as $33 from "./routes/recipes/[name].tsx";
import * as $34 from "./routes/recipes/index.tsx";
import * as $35 from "./routes/series/[name].tsx";
import * as $36 from "./routes/series/index.tsx";
import * as $$0 from "./islands/Counter.tsx";
import * as $$1 from "./islands/IngredientsList.tsx";
import * as $$2 from "./islands/KMenu.tsx";
@ -54,38 +56,40 @@ const manifest = {
"./routes/_404.tsx": $0,
"./routes/_app.tsx": $1,
"./routes/_middleware.ts": $2,
"./routes/api/articles/[name].ts": $3,
"./routes/api/articles/create/index.ts": $4,
"./routes/api/articles/index.ts": $5,
"./routes/api/auth/callback.ts": $6,
"./routes/api/auth/login.ts": $7,
"./routes/api/auth/logout.ts": $8,
"./routes/api/cache/index.ts": $9,
"./routes/api/images/index.ts": $10,
"./routes/api/index.ts": $11,
"./routes/api/movies/[name].ts": $12,
"./routes/api/movies/enhance/[name].ts": $13,
"./routes/api/movies/index.ts": $14,
"./routes/api/query/index.ts": $15,
"./routes/api/query/sync.ts": $16,
"./routes/api/recipes/[name].ts": $17,
"./routes/api/recipes/index.ts": $18,
"./routes/api/resources.ts": $19,
"./routes/api/series/[name].ts": $20,
"./routes/api/series/enhance/[name].ts": $21,
"./routes/api/series/index.ts": $22,
"./routes/api/tmdb/[id].ts": $23,
"./routes/api/tmdb/credits/[id].ts": $24,
"./routes/api/tmdb/query.ts": $25,
"./routes/articles/[name].tsx": $26,
"./routes/articles/index.tsx": $27,
"./routes/index.tsx": $28,
"./routes/movies/[name].tsx": $29,
"./routes/movies/index.tsx": $30,
"./routes/recipes/[name].tsx": $31,
"./routes/recipes/index.tsx": $32,
"./routes/series/[name].tsx": $33,
"./routes/series/index.tsx": $34,
"./routes/admin/performance/api.ts": $3,
"./routes/admin/performance/index.tsx": $4,
"./routes/api/articles/[name].ts": $5,
"./routes/api/articles/create/index.ts": $6,
"./routes/api/articles/index.ts": $7,
"./routes/api/auth/callback.ts": $8,
"./routes/api/auth/login.ts": $9,
"./routes/api/auth/logout.ts": $10,
"./routes/api/cache/index.ts": $11,
"./routes/api/images/index.ts": $12,
"./routes/api/index.ts": $13,
"./routes/api/movies/[name].ts": $14,
"./routes/api/movies/enhance/[name].ts": $15,
"./routes/api/movies/index.ts": $16,
"./routes/api/query/index.ts": $17,
"./routes/api/query/sync.ts": $18,
"./routes/api/recipes/[name].ts": $19,
"./routes/api/recipes/index.ts": $20,
"./routes/api/resources.ts": $21,
"./routes/api/series/[name].ts": $22,
"./routes/api/series/enhance/[name].ts": $23,
"./routes/api/series/index.ts": $24,
"./routes/api/tmdb/[id].ts": $25,
"./routes/api/tmdb/credits/[id].ts": $26,
"./routes/api/tmdb/query.ts": $27,
"./routes/articles/[name].tsx": $28,
"./routes/articles/index.tsx": $29,
"./routes/index.tsx": $30,
"./routes/movies/[name].tsx": $31,
"./routes/movies/index.tsx": $32,
"./routes/recipes/[name].tsx": $33,
"./routes/recipes/index.tsx": $34,
"./routes/series/[name].tsx": $35,
"./routes/series/index.tsx": $36,
},
islands: {
"./islands/Counter.tsx": $$0,

86
lib/cache/performance.ts vendored Normal file
View File

@ -0,0 +1,86 @@
import * as cache from "@lib/cache/cache.ts";
export type PerformancePoint = {
path: string;
search: string;
time: number;
date: Date;
};
export type PerformanceRes = {
max: number;
res: {
url: string;
data: readonly [number, number, number, number];
}[];
};
export const savePerformance = (url: string, milliseconds: number) => {
const d = new Date();
const year = d.getFullYear();
const month = d.getMonth().toString().padStart(2, "0");
const day = d.getDay().toString().padStart(2, "0");
const hour = d.getHours().toString().padStart(2, "0");
const minute = d.getMinutes().toString().padStart(2, "0");
const seconds = d.getSeconds().toString().padStart(2, "0");
const u = new URL(url);
if (u.pathname.includes("_frsh/")) return;
u.searchParams.delete("__frsh_c");
cache.set(
`performance:${year}:${month}:${day}:${hour}:${minute}:${seconds}`,
JSON.stringify({
path: decodeURIComponent(u.pathname),
search: u.search,
time: Math.floor(milliseconds * 1000),
}),
);
};
export async function getPerformances(): Promise<PerformanceRes> {
const d = new Date();
const year = d.getFullYear();
const month = d.getMonth().toString().padStart(2, "0");
const day = d.getDay().toString().padStart(2, "0");
const keys = await cache.keys(
`performance:${year}:${month}:${day}:*`,
);
const performances = await Promise.all(
keys.map(async (key) =>
JSON.parse(await cache.get<string>(key)) as PerformancePoint
),
);
let maximum = 0;
const res = new Map<string, number[]>();
for (const p of performances) {
const id = p.path;
const timings = res.get(id) || [];
if (p.time > maximum) maximum = p.time;
timings.push(p.time);
res.set(id, timings);
}
return {
max: maximum,
res: [...res.entries()].map(([url, timings]) => {
let total = 0;
let min = Infinity;
let max = -Infinity;
for (const t of timings) {
total += t;
if (t < min) min = t;
if (t > max) max = t;
}
return {
url,
data: [timings.length, min, total / timings.length, max] as const,
};
}).sort((a, b) => a.data[2] < b.data[2] ? 1 : -1),
};
}

View File

@ -71,8 +71,6 @@ export async function searchResource(
}
}
console.log({ q, query_by, filter_by });
return await typesenseClient.collections("resources")
.documents().search({
q,

View File

@ -36,6 +36,7 @@ export default function App({ Component }: AppProps) {
:root {
--background: rgb(43, 41, 48);
--foreground: rgb(129, 129, 129);
--light: #2B2930;
}
.custom-grid {
grid-template-columns: repeat(auto-fit, minmax(37vw, 1fr)) ;

View File

@ -2,17 +2,17 @@
import { MiddlewareHandlerContext } from "$fresh/server.ts";
import { DomainError } from "@lib/errors.ts";
import { getCookies } from "https://deno.land/std@0.197.0/http/cookie.ts";
import { decode, verify } from "https://deno.land/x/djwt@v2.2/mod.ts";
import { sessionDB, userDB } from "@lib/db.ts";
import { verify } from "https://deno.land/x/djwt@v2.2/mod.ts";
import * as cache from "@lib/cache/performance.ts";
import { JWT_SECRET } from "@lib/env.ts";
export async function handler(
_req: Request,
req: Request,
ctx: MiddlewareHandlerContext,
) {
try {
ctx.state.flag = true;
const allCookies = getCookies(_req.headers);
performance.mark("a");
const allCookies = getCookies(req.headers);
const sessionCookie = allCookies["session_cookie"];
if (!ctx.state.session && sessionCookie && JWT_SECRET) {
try {
@ -22,9 +22,15 @@ export async function handler(
}
} catch (_err) {
//
console.log({ _err });
}
}
return await ctx.next();
const resp = await ctx.next();
performance.mark("b");
const b = performance.measure("a->b", "a", "b");
cache.savePerformance(req.url, b.duration);
return resp;
} catch (error) {
console.error("Error", error);

View File

@ -0,0 +1,9 @@
import { Handlers } from "$fresh/server.ts";
import { getPerformances } from "@lib/cache/performance.ts";
import { json } from "@lib/helpers.ts";
export const handler: Handlers = {
async GET() {
return json(await getPerformances());
},
};

View File

@ -0,0 +1,93 @@
import { MainLayout } from "@components/layouts/main.tsx";
import { Handlers, PageProps } from "$fresh/server.ts";
import {
getPerformances,
PerformancePoint,
PerformanceRes,
} from "@lib/cache/performance.ts";
import { AccessDeniedError } from "@lib/errors.ts";
export const handler: Handlers = {
async GET(_, ctx) {
const performances = await getPerformances();
if (!("session" in ctx.state)) {
throw new AccessDeniedError();
}
return ctx.render({ performances });
},
};
function PerformanceLine(
{ maximum, data: [amount, min, average, max], url }: {
maximum: number;
url: string;
data: readonly [number, number, number, number];
},
) {
return (
<div
class="mt-10 flex flex-col gap-2 bg-gray-900 px-4 py-2 rounded-2xl"
style={{ background: "var(--light)" }}
>
<div style={{ color: "var(--foreground)" }}>
{url}
</div>
<div class="flex gap-2">
<span class="bg-gray-600 p-1 text-xs rounded-xl text-white">
{Math.floor(average / 1000)}ms
</span>
<span class="bg-gray-600 p-1 text-xs rounded-xl text-white">
{amount}x
</span>
</div>
<div class="" style={{ maxWidth: "75vw" }}>
<div
class="h-2 bg-red-200 rounded"
style={{
width: `${Math.floor((max / maximum) * 100)}%`,
borderRadius: "1rem 1rem 1rem 0px",
minWidth: "5px",
}}
/>
<div
class="h-2 bg-green-200 rounded"
style={{
width: `${Math.floor((average / maximum) * 100)}%`,
borderStartEndRadius: "0px",
borderRadius: "0px 0px 1rem 0px",
minWidth: "5px",
}}
/>
<div
class="h-2 bg-yellow-200 rounded"
style={{
width: `${Math.floor((min / maximum) * 100)}%`,
borderRadius: "0px 0px 1rem 1rem",
minWidth: "5px",
}}
/>
</div>
</div>
);
}
export default function Greet(
{ data: { performances }, url }: PageProps<{ performances: PerformanceRes }>,
) {
return (
<MainLayout url={url}>
<h1 class="text-white text-4xl ">Performance</h1>
{performances.res.map((r) => {
return (
<PerformanceLine
maximum={performances.max}
url={r.url}
data={r.data}
/>
);
})}
</MainLayout>
);
}