feat(devops): separate public page server timing diagnostics
This commit is contained in:
1 parent
d5091d1a73
commit
baeb54aeb5
15 files changed
+315
-29
No files matched your search
@@ -11,6 +11,8 @@ const measurement = z.object({
|
||||
errors: z.number().nonnegative(),
|
||||
});
|
||||
const schema = z.object({
|
||||
kind: z.enum(["admin-api", "public-page"]).optional(),
|
||||
outcome: z.enum(["completed", "failed", "redirect", "not-found"]).optional(),
|
||||
operationId: z.string().max(100),
|
||||
route: z.string().max(300),
|
||||
method: z.string().max(10),
|
||||
@@ -24,6 +26,8 @@ const schema = z.object({
|
||||
}),
|
||||
});
|
||||
export interface PerformanceSample {
|
||||
kind?: "admin-api" | "public-page";
|
||||
outcome?: "completed" | "failed" | "redirect" | "not-found";
|
||||
operationId: string;
|
||||
route: string;
|
||||
method: string;
|
||||
@@ -33,23 +37,33 @@ export interface PerformanceSample {
|
||||
metrics: PerformanceMetrics;
|
||||
}
|
||||
const state = globalThis as typeof globalThis & {
|
||||
cmsPerformance?: { samples: PerformanceSample[]; pending: number };
|
||||
cmsPerformance?: {
|
||||
samples: PerformanceSample[];
|
||||
publicSamples?: PerformanceSample[];
|
||||
pending: number;
|
||||
};
|
||||
};
|
||||
const buffer = state.cmsPerformance ?? { samples: [], pending: 0 };
|
||||
state.cmsPerformance = buffer;
|
||||
|
||||
/** Best effort: no connection wait, no unbounded offline queue, no request failure. */
|
||||
export function recordPerformance(sample: PerformanceSample): void {
|
||||
buffer.samples.unshift(sample);
|
||||
buffer.samples.length = Math.min(buffer.samples.length, LIMIT);
|
||||
const samples =
|
||||
sample.kind === "public-page"
|
||||
? (buffer.publicSamples ?? [])
|
||||
: buffer.samples;
|
||||
if (sample.kind === "public-page") buffer.publicSamples = samples;
|
||||
samples.unshift(sample);
|
||||
samples.length = Math.min(samples.length, LIMIT);
|
||||
const key = sample.kind === "public-page" ? `${KEY}:public` : KEY;
|
||||
if (redis?.status !== "ready" || buffer.pending >= 4) return;
|
||||
buffer.pending++;
|
||||
try {
|
||||
void redis
|
||||
.multi()
|
||||
.lpush(KEY, JSON.stringify(sample))
|
||||
.ltrim(KEY, 0, LIMIT - 1)
|
||||
.expire(KEY, 3600)
|
||||
.lpush(key, JSON.stringify(sample))
|
||||
.ltrim(key, 0, LIMIT - 1)
|
||||
.expire(key, 3600)
|
||||
.exec()
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
@@ -60,16 +74,21 @@ export function recordPerformance(sample: PerformanceSample): void {
|
||||
}
|
||||
}
|
||||
|
||||
export async function readPerformance(): Promise<{
|
||||
export async function readPerformance(
|
||||
kind: "admin-api" | "public-page" = "admin-api",
|
||||
): Promise<{
|
||||
samples: PerformanceSample[];
|
||||
shared: boolean;
|
||||
}> {
|
||||
const cutoff = Date.now() - RETENTION_MS;
|
||||
const key = kind === "public-page" ? `${KEY}:public` : KEY;
|
||||
const local =
|
||||
kind === "public-page" ? (buffer.publicSamples ?? []) : buffer.samples;
|
||||
if (redis?.status === "ready") {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
try {
|
||||
const rows = await Promise.race([
|
||||
redis.lrange(KEY, 0, LIMIT - 1),
|
||||
redis.lrange(key, 0, LIMIT - 1),
|
||||
new Promise<never>((_, reject) => {
|
||||
timer = setTimeout(
|
||||
() => reject(new Error("Diagnostic storage timeout")),
|
||||
@@ -95,7 +114,7 @@ export async function readPerformance(): Promise<{
|
||||
}
|
||||
}
|
||||
return {
|
||||
samples: buffer.samples.filter((sample) => sample.at >= cutoff),
|
||||
samples: local.filter((sample) => sample.at >= cutoff),
|
||||
shared: false,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user