playbook/antigravity-awesome-skills/plugins/antigravity-awesome-skills-.../skills/vercel-optimize/lib/gates/route-errors.mjs

81 lines
3.0 KiB
JavaScript

// Errored function invocations still bill at full duration, so high-volume 5xx is a cost issue, not just reliability.
import { withRouteShapeWarnings } from '../route-normalize.mjs';
const MIN_VOLUME_FOR_RATE_EMISSION = 1000;
export const metadata = {
id: 'route_errors',
threshold: `count > 250 OR (totalRequests >= ${MIN_VOLUME_FOR_RATE_EMISSION} AND errorRate > 0.01)`,
billingDimension: 'function-duration',
scope: 'route',
sourceCitation: 'vercel-optimize gate threshold',
description:
'Routes producing > 250 5xx errors over the window, or with > 1% error rate on at least 1,000 total requests. Errored function invocations still bill at full duration; high error rates also poison user experience.',
};
export function gate(signals) {
const errors = extractErrors(signals);
return errors
.filter((e) => e.count > 250 || (e.total >= MIN_VOLUME_FOR_RATE_EMISSION && (e.errorRate ?? 0) > 0.01))
.map((e) => withRouteShapeWarnings({
kind: metadata.id,
scope: 'route',
route: e.route,
files: [],
priority: e.count,
confidence: 0.93,
o11ySignal: e.errorRate != null
? `errs=${e.count},rate=${(e.errorRate * 100).toFixed(1)}%`
: `errs=${e.count}`,
reason: 'concentrated 5xx errors',
question: `Why does ${e.route} produce ${e.count} 5xx errors over the window, and what code path is failing?`,
evidence: { metric: e.metric, route: e.route, count: e.count, totalRequests: e.total, errorRate: e.errorRate },
}, signals));
}
function extractErrors(signals) {
const fnStatus = signals.metrics?.fnStatusByRoute;
if (Array.isArray(fnStatus?.rows)) return extractFromStatusRows(fnStatus.rows, 'fnStatusByRoute');
const m = signals.metrics?.requestsByRouteStatus;
const cache = signals.metrics?.requestsByRouteCache;
if (!m?.ok && !Array.isArray(m?.rows)) return [];
const errors = extractFromStatusRows(m?.rows ?? [], 'requestsByRouteStatus');
// cache rollup is summed across cache_result, giving per-route total request count.
const totalByRoute = new Map();
for (const row of (cache?.rows ?? [])) {
if (!row.route) continue;
totalByRoute.set(row.route, (totalByRoute.get(row.route) ?? 0) + (row.value ?? 0));
}
return errors.map((e) => {
const total = totalByRoute.get(e.route) ?? 0;
return {
...e,
total,
errorRate: total > 0 ? e.count / total : null,
};
});
}
function extractFromStatusRows(rows, metric) {
const errByRoute = new Map();
const totalByRoute = new Map();
for (const row of rows) {
const route = row.route;
if (!route) continue;
const v = row.value ?? 0;
const status = String(row.http_status ?? '');
if (/^5\d\d$/.test(status)) errByRoute.set(route, (errByRoute.get(route) ?? 0) + v);
totalByRoute.set(route, (totalByRoute.get(route) ?? 0) + v);
}
return [...errByRoute.entries()].map(([route, count]) => {
const total = totalByRoute.get(route) ?? 0;
const errorRate = total > 0 ? count / total : null;
return { route, count, total, errorRate, metric };
});
}