fix: backfill sentinel request-rate detail

This commit is contained in:
Codex
2026-07-02 05:10:47 +00:00
parent d874bf1fe0
commit a295d82e7e
2 changed files with 110 additions and 2 deletions
@@ -800,6 +800,7 @@ function compactQuickVerifyRecordAnalysis(value: unknown): Record<string, unknow
findings: Array.isArray(item.findings) ? item.findings.slice(0, 16).map(compactQuickVerifyRecordFinding) : [],
pagePerformanceSlowApi: Array.isArray(item.pagePerformanceSlowApi) ? item.pagePerformanceSlowApi.slice(0, 6).map(record) : [],
browserProcess: compactQuickVerifyRecordBrowserProcess(record(item.browserProcess)),
requestRate: compactQuickVerifyRecordRequestRate(record(item.requestRate), record(item.requestRateCurve)),
valuesRedacted: true,
};
}
@@ -831,6 +832,106 @@ function compactQuickVerifyRecordBrowserProcess(value: Record<string, unknown>):
};
}
function compactQuickVerifyRecordRequestRate(summaryValue: Record<string, unknown>, curveValue: Record<string, unknown>): Record<string, unknown> | null {
const source = Object.keys(curveValue).length > 0 ? curveValue : summaryValue;
const summary = record(curveValue.summary);
const effectiveSummary = Object.keys(summary).length > 0 ? summary : summaryValue;
const pageCurves = Array.isArray(curveValue.pageCurves) ? curveValue.pageCurves.map((item) => compactQuickVerifyRecordRequestRateCurve(item, "page")).filter((item) => item.buckets.length > 0).slice(0, 12) : [];
const apiPathCurves = Array.isArray(curveValue.apiPathCurves) ? curveValue.apiPathCurves.map((item) => compactQuickVerifyRecordRequestRateCurve(item, "apiPath")).filter((item) => item.buckets.length > 0).slice(0, 16) : [];
const buckets = Array.isArray(curveValue.buckets) ? curveValue.buckets.map(compactQuickVerifyRecordRequestRateBucket).filter((item) => item.startAt !== null && item.requestPerMinute !== null).slice(-120) : [];
if (Object.keys(source).length === 0 && pageCurves.length === 0 && apiPathCurves.length === 0 && buckets.length === 0) return null;
return {
summary: compactQuickVerifyRecordRequestRateSummary(effectiveSummary),
buckets,
pageCurves,
apiPathCurves,
peaks: Array.isArray(source.peaks) ? source.peaks.slice(0, 12).map(compactQuickVerifyRecordRequestRatePeak) : [],
valuesRedacted: true,
};
}
function compactQuickVerifyRecordRequestRateSummary(value: Record<string, unknown>): Record<string, unknown> {
return {
bucketMs: numberAtNullable(value, "bucketMs"),
bucketSeconds: numberAtNullable(value, "bucketSeconds"),
requestCount: numberAtNullable(value, "requestCount"),
bucketCount: numberAtNullable(value, "bucketCount"),
pageCount: numberAtNullable(value, "pageCount"),
apiPathCount: numberAtNullable(value, "apiPathCount"),
firstAt: stringAtNullable(value, "firstAt"),
lastAt: stringAtNullable(value, "lastAt"),
totalRedPerMinute: numberAtNullable(value, "totalRedPerMinute"),
pageRedPerMinute: numberAtNullable(value, "pageRedPerMinute"),
apiPathRedPerMinute: numberAtNullable(value, "apiPathRedPerMinute"),
totalPeakPerMinute: numberAtNullable(value, "totalPeakPerMinute"),
totalPeakCount: numberAtNullable(value, "totalPeakCount"),
totalPeakAt: stringAtNullable(value, "totalPeakAt"),
pagePeakPerMinute: numberAtNullable(value, "pagePeakPerMinute"),
pagePeakKey: stringAtNullable(value, "pagePeakKey"),
pagePeakPath: stringAtNullable(value, "pagePeakPath"),
apiPathPeakPerMinute: numberAtNullable(value, "apiPathPeakPerMinute"),
apiPathPeakKey: stringAtNullable(value, "apiPathPeakKey"),
overThresholdPeakCount: numberAtNullable(value, "overThresholdPeakCount"),
valuesRedacted: true,
};
}
function compactQuickVerifyRecordRequestRateCurve(value: unknown, scope: string): Record<string, unknown> & { buckets: Record<string, unknown>[] } {
const item = record(value);
const buckets = Array.isArray(item.buckets) ? item.buckets.map(compactQuickVerifyRecordRequestRateBucket).filter((bucket) => bucket.startAt !== null && bucket.requestPerMinute !== null).slice(-120) : [];
return {
pageKey: stringAtNullable(item, "pageKey"),
pageRole: stringAtNullable(item, "pageRole"),
pageId: stringAtNullable(item, "pageId"),
pageEpoch: numberAtNullable(item, "pageEpoch"),
apiKey: stringAtNullable(item, "apiKey"),
method: stringAtNullable(item, "method"),
path: stringAtNullable(item, "path"),
count: numberAtNullable(item, "count"),
bucketCount: numberAtNullable(item, "bucketCount") ?? buckets.length,
peakRequestPerMinute: numberAtNullable(item, "peakRequestPerMinute"),
peakBucket: compactQuickVerifyRecordRequestRateBucket(record(item.peakBucket)),
buckets,
scope,
valuesRedacted: true,
};
}
function compactQuickVerifyRecordRequestRateBucket(value: unknown): Record<string, unknown> {
const item = record(value);
return {
bucketStartMs: numberAtNullable(item, "bucketStartMs"),
bucketEndMs: numberAtNullable(item, "bucketEndMs"),
startAt: stringAtNullable(item, "startAt"),
endAt: stringAtNullable(item, "endAt"),
count: numberAtNullable(item, "count"),
requestPerMinute: numberAtNullable(item, "requestPerMinute"),
valuesRedacted: true,
};
}
function compactQuickVerifyRecordRequestRatePeak(value: unknown): Record<string, unknown> {
const item = record(value);
return {
scope: stringAtNullable(item, "scope"),
thresholdPerMinute: numberAtNullable(item, "thresholdPerMinute"),
overThreshold: item.overThreshold === true,
bucketMs: numberAtNullable(item, "bucketMs"),
startAt: stringAtNullable(item, "startAt"),
endAt: stringAtNullable(item, "endAt"),
count: numberAtNullable(item, "count"),
requestPerMinute: numberAtNullable(item, "requestPerMinute"),
method: stringAtNullable(item, "method"),
path: stringAtNullable(item, "path"),
apiKey: stringAtNullable(item, "apiKey"),
pageKey: stringAtNullable(item, "pageKey"),
pageRole: stringAtNullable(item, "pageRole"),
pageId: stringAtNullable(item, "pageId"),
pageEpoch: numberAtNullable(item, "pageEpoch"),
valuesRedacted: true,
};
}
function compactQuickVerifyRecordMemorySeries(value: unknown): Record<string, unknown> & { points: Record<string, unknown>[] } {
const item = record(value);
const points = Array.isArray(item.points)
@@ -1021,6 +1122,9 @@ function analysisSummaryFromAnalyzeResult(analysis: ChildCliResult, fallbackStat
analysisWindow: record(source.analysisWindow),
pagePerformanceSlowApi: Array.isArray(source.pagePerformanceSlowApi) ? source.pagePerformanceSlowApi.slice(0, 8).map(record) : [],
browserProcess: compactAnalyzeBrowserProcess(source.browserProcess),
requestRate: record(source.requestRate),
requestRateCurve: record(source.requestRateCurve),
requestRatePeaks: Array.isArray(source.requestRatePeaks) ? source.requestRatePeaks.slice(0, 12).map(record) : [],
result: record(payload.result),
valuesRedacted: true,
};
@@ -1102,11 +1102,15 @@ function dashboardRunMemoryDetail(config: WebProbeSentinelServiceConfig, row: Re
function dashboardRunRequestRateDetail(config: WebProbeSentinelServiceConfig, row: Record<string, unknown>, stored: Record<string, unknown>): Record<string, unknown> {
const options = dashboardDetailRequestRateOptions(config);
const storedRequestRate = compactDashboardRequestRate(record(record(record(stored.summary).analysis).requestRate), options, "recorded-analysis-summary");
const storedAnalysis = record(record(stored.summary).analysis);
const storedRequestRate = compactDashboardRequestRate(record(storedAnalysis.requestRate), options, "recorded-analysis-summary");
if (storedRequestRate.ok === true && hasDashboardRequestRateSeries(storedRequestRate)) return storedRequestRate;
const storedRequestRateCurve = compactDashboardRequestRate(record(storedAnalysis.requestRateCurve), options, "recorded-analysis-request-rate-curve");
if (storedRequestRateCurve.ok === true && hasDashboardRequestRateSeries(storedRequestRateCurve)) return storedRequestRateCurve;
const fromArtifacts = readDashboardRequestRateFromAnalysisReport(config, row, options);
if (fromArtifacts.ok === true) return fromArtifacts;
if (storedRequestRate.ok === true) return storedRequestRate;
if (storedRequestRateCurve.ok === true) return storedRequestRateCurve;
return {
ok: false,
source: "unavailable",
@@ -1120,7 +1124,7 @@ function dashboardRunRequestRateDetail(config: WebProbeSentinelServiceConfig, ro
pageSeries: [],
apiPathSeries: [],
peaks: [],
reason: fromArtifacts.reason ?? storedRequestRate.reason ?? "request-rate-samples-missing",
reason: fromArtifacts.reason ?? storedRequestRate.reason ?? storedRequestRateCurve.reason ?? "request-rate-samples-missing",
valuesRedacted: true,
};
}