Setting the file. One moment.
Queries · Vercel Optimize · vercel-labs/agent-skills · Skills Docs
ContentsBack to the top of the page lib/queries.mjs
lib/ queries.mjs
JavaScript · 315 lines · 10 KB
12 const HOST_LIMIT = 50 ;
13 const DIM_LIMIT = 50 ;
14
15 // CLI emits value under `<metric_id_with_underscores>_<aggregation>` (e.g. `vercel_request_count_sum`).
16 function defaultNormalize ( metricId , aggregation , groupBy ) {
17 return ( resp ) => ({ rows: normalizeSummary (resp, metricId, aggregation, groupBy) });
18 }
19
20 // Collapse (route × function_start_type) rows into one row per route. Observed values: "cold", "hot", "prewarmed".
21 function normalizeColdStart ( metricId , aggregation ) {
22 return ( resp ) => {
23 const rows = normalizeSummary (resp, metricId, aggregation, [ 'route' , 'function_start_type' ]);
24 const byRoute = new Map ();
25 for ( const r of rows) {
26 if ( ! r.route) continue ;
27 const prior = byRoute. get (r.route) ?? { route: r.route, total: 0 , coldCount: 0 , warmCount: 0 , prewarmedCount: 0 };
28 const v = r.value ?? 0 ;
29 prior.total += v;
30 if (r.function_start_type === 'cold' ) prior.coldCount += v;
31 else if (r.function_start_type === 'hot' ) prior.warmCount += v;
32 else if (r.function_start_type === 'prewarmed' ) prior.prewarmedCount += v;
33 byRoute. set (r.route, prior);
34 }
35 return {
36 rows: [ ... byRoute. values ()]. map (( r ) => ({
37 ... r,
38 coldPct: r.total > 0 ? r.coldCount / r.total : 0 ,
39 })),
40 };
41 };
42 }
43
44 export const QUERIES = [
45 {
46 id: 'requestsByRouteCache' ,
47 metricId: 'vercel.request.count' ,
48 aggregation: 'sum' ,
49 groupBy: [ 'route' , 'cache_result' ],
50 limit: ROUTE_LIMIT ,
51 description: 'Request count per route × cache_result. Source of cache hit rate; total invocations folds across cache_result.' ,
52 },
53 {
54 id: 'fnDurationP95ByRoute' ,
55 metricId: 'vercel.function_invocation.function_duration_ms' ,
56 aggregation: 'p95' ,
57 groupBy: [ 'route' ],
58 limit: ROUTE_LIMIT ,
59 description: 'p95 wall-clock function duration per route. Canonical slow-route signal.' ,
60 },
61 {
62 id: 'requestsByRouteStatus' ,
63 metricId: 'vercel.request.count' ,
64 aggregation: 'sum' ,
65 groupBy: [ 'route' , 'http_status' ],
66 limit: ROUTE_LIMIT ,
67 description: 'Request count per route × http_status. Compatibility fallback for older route_errors fixtures.' ,
68 },
69 {
70 id: 'fnStatusByRoute' ,
71 metricId: 'vercel.function_invocation.count' ,
72 aggregation: 'sum' ,
73 groupBy: [ 'route' , 'http_status' ],
74 limit: ROUTE_LIMIT ,
75 description: 'Function invocation count per route × http_status. Canonical 5xx source for slow_route disqualification and route_errors.' ,
76 },
77 {
78 id: 'requestsByRouteMethod' ,
79 metricId: 'vercel.request.count' ,
80 aggregation: 'sum' ,
81 groupBy: [ 'route' , 'request_method' ],
82 limit: ROUTE_LIMIT ,
83 description: 'Request count per route × request_method. Uncached_route gate uses this to skip mostly-POST routes (Server Actions, mutations) where 0% cache is correct behavior.' ,
84 },
85 {
86 id: 'externalApiP75' ,
87 metricId: 'vercel.external_api_request.request_duration_ms' ,
88 aggregation: 'p75' ,
89 groupBy: [ 'origin_hostname' ],
90 limit: HOST_LIMIT ,
91 description: 'p75 external API duration per origin hostname.' ,
92 },
93
94 {
95 id: 'fnStartTypeByRoute' ,
96 metricId: 'vercel.function_invocation.count' ,
97 aggregation: 'sum' ,
98 groupBy: [ 'route' , 'function_start_type' ],
99 limit: ROUTE_LIMIT ,
100 description: 'Function invocation count split by cold | hot | prewarmed. Feeds cold_start gate.' ,
101 normalizer: normalizeColdStart ( 'vercel.function_invocation.count' , 'sum' ),
102 },
103 {
104 id: 'fnGbHrByRoute' ,
105 metricId: 'vercel.function_invocation.function_duration_gbhr' ,
106 aggregation: 'sum' ,
107 groupBy: [ 'route' ],
108 limit: ROUTE_LIMIT ,
109 description: 'Billed GB-hours per route (function duration in Fluid billing).' ,
110 },
111 {
112 id: 'fnCpuMsByRoute' ,
113 metricId: 'vercel.function_invocation.function_cpu_time_ms' ,
114 aggregation: 'sum' ,
115 groupBy: [ 'route' ],
116 limit: ROUTE_LIMIT ,
117 description: 'Active CPU time per route. Fluid Compute bills on this; high CPU = expensive route.' ,
118 },
119 {
120 id: 'fnPeakMemoryByRoute' ,
121 metricId: 'vercel.function_invocation.peak_memory_mb' ,
122 aggregation: 'max' ,
123 groupBy: [ 'route' ],
124 limit: ROUTE_LIMIT ,
125 description: 'Peak memory observed per route. Compared against provisioned to right-size.' ,
126 },
127 {
128 id: 'fnProvisionedMemoryByRoute' ,
129 metricId: 'vercel.function_invocation.provisioned_memory_mb' ,
130 aggregation: 'max' ,
131 groupBy: [ 'route' ],
132 limit: ROUTE_LIMIT ,
133 description: 'Provisioned memory per route. Feeds oversized_memory gate.' ,
134 },
135 {
136 id: 'fnTtfbP95ByRoute' ,
137 metricId: 'vercel.function_invocation.ttfb_ms' ,
138 aggregation: 'p95' ,
139 groupBy: [ 'route' ],
140 limit: ROUTE_LIMIT ,
141 description: 'Server-measured time-to-first-byte per route. Complements function_duration_ms p95.' ,
142 },
143
144 {
145 id: 'fdtByRoute' ,
146 metricId: 'vercel.request.fdt_total_bytes' ,
147 aggregation: 'sum' ,
148 groupBy: [ 'route' ],
149 limit: ROUTE_LIMIT ,
150 description: 'Fast Data Transfer bytes per route. Bandwidth cost driver.' ,
151 },
152 {
153 id: 'fdtByBot' ,
154 metricId: 'vercel.request.fdt_total_bytes' ,
155 aggregation: 'sum' ,
156 groupBy: [ 'bot_category' ],
157 limit: DIM_LIMIT ,
158 description: 'FDT bytes by bot category. Empty `bot_category` = human traffic; non-empty = bots.' ,
159 },
160 {
161 id: 'fdtByCache' ,
162 metricId: 'vercel.request.fdt_total_bytes' ,
163 aggregation: 'sum' ,
164 groupBy: [ 'cache_result' ],
165 limit: DIM_LIMIT ,
166 description: 'FDT bytes by cache_result. Uncached vs cached bandwidth.' ,
167 },
168
169 {
170 id: 'middlewareCount' ,
171 metricId: 'vercel.middleware_invocation.count' ,
172 aggregation: 'sum' ,
173 groupBy: [ 'request_path' ],
174 limit: ROUTE_LIMIT ,
175 description: 'Middleware invocations per request_path. Heavy middleware traffic = missing matcher.' ,
176 },
177 {
178 id: 'middlewareDurationP95' ,
179 metricId: 'vercel.middleware_invocation.duration_ms' ,
180 aggregation: 'p95' ,
181 groupBy: [ 'request_path' ],
182 limit: ROUTE_LIMIT ,
183 description: 'p95 middleware duration per request_path.' ,
184 },
185
186 {
187 id: 'isrReadsByRoute' ,
188 metricId: 'vercel.isr_operation.read_units' ,
189 aggregation: 'sum' ,
190 groupBy: [ 'route' ],
191 limit: ROUTE_LIMIT ,
192 description: 'ISR read units per route. Healthy when high relative to writes.' ,
193 },
194 {
195 id: 'isrWritesByRoute' ,
196 metricId: 'vercel.isr_operation.write_units' ,
197 aggregation: 'sum' ,
198 groupBy: [ 'route' ],
199 limit: ROUTE_LIMIT ,
200 description: 'ISR write units per route. High writes/reads = over-aggressive revalidate.' ,
201 },
202
203 {
204 id: 'imageCount' ,
205 metricId: 'vercel.image_transformation.count' ,
206 aggregation: 'sum' ,
207 groupBy: [],
208 limit: 1 ,
209 description: 'Total image transformations performed.' ,
210 },
211 {
212 id: 'imageByHost' ,
213 metricId: 'vercel.image_transformation.count' ,
214 aggregation: 'sum' ,
215 groupBy: [ 'source_image_hostname' ],
216 limit: HOST_LIMIT ,
217 description: 'Image transformations per source hostname. Identify which hosts dominate the bill.' ,
218 },
219 {
220 id: 'imageSourceBytes' ,
221 metricId: 'vercel.image_transformation.source_size_bytes' ,
222 aggregation: 'sum' ,
223 groupBy: [],
224 limit: 1 ,
225 description: 'Bytes of source images optimized. High = ingress bandwidth cost.' ,
226 },
227
228 {
229 id: 'cwvLcpByRoute' ,
230 metricId: 'vercel.speed_insights_metric.lcp' ,
231 aggregation: 'p75' ,
232 groupBy: [ 'route' ],
233 limit: ROUTE_LIMIT ,
234 description: 'p75 Largest Contentful Paint per route. > 2500ms = poor.' ,
235 },
236 {
237 id: 'cwvInpByRoute' ,
238 metricId: 'vercel.speed_insights_metric.inp' ,
239 aggregation: 'p75' ,
240 groupBy: [ 'route' ],
241 limit: ROUTE_LIMIT ,
242 description: 'p75 Interaction to Next Paint per route. > 200ms = poor.' ,
243 },
244 {
245 id: 'cwvClsByRoute' ,
246 metricId: 'vercel.speed_insights_metric.cls' ,
247 aggregation: 'p75' ,
248 groupBy: [ 'route' ],
249 limit: ROUTE_LIMIT ,
250 description: 'p75 Cumulative Layout Shift per route. > 0.1 = poor.' ,
251 },
252 {
253 id: 'cwvTtfbByRoute' ,
254 metricId: 'vercel.speed_insights_metric.ttfb_ms' ,
255 aggregation: 'p75' ,
256 groupBy: [ 'route' ],
257 limit: ROUTE_LIMIT ,
258 description: 'p75 client-measured TTFB per route.' ,
259 },
260 {
261 id: 'cwvCount' ,
262 metricId: 'vercel.speed_insights_metric.count' ,
263 aggregation: 'sum' ,
264 groupBy: [],
265 limit: 1 ,
266 description: 'Total Speed Insights measurements. Use to decide whether CWV gates have enough signal.' ,
267 },
268 {
269 id: 'cwvCountByRoute' ,
270 metricId: 'vercel.speed_insights_metric.count' ,
271 aggregation: 'sum' ,
272 groupBy: [ 'route' ],
273 limit: ROUTE_LIMIT ,
274 description: 'Speed Insights measurements per route. CWV route gates require at least 50 samples on the specific route.' ,
275 },
276
277 {
278 id: 'firewallByAction' ,
279 metricId: 'vercel.firewall_action.count' ,
280 aggregation: 'sum' ,
281 groupBy: [ 'waf_action' ],
282 limit: DIM_LIMIT ,
283 description: 'Firewall action count per waf_action (allow | challenge | block | log).' ,
284 },
285 {
286 id: 'botIdChecks' ,
287 metricId: 'vercel.bot_id_check.count' ,
288 aggregation: 'sum' ,
289 groupBy: [],
290 limit: 1 ,
291 description: 'Total BotID checks. > 0 confirms BotID is wired up; = 0 confirms it is not.' ,
292 },
293
294 {
295 id: 'externalApiCount' ,
296 metricId: 'vercel.external_api_request.count' ,
297 aggregation: 'sum' ,
298 groupBy: [ 'origin_hostname' ],
299 limit: HOST_LIMIT ,
300 description: 'External API call count per origin hostname.' ,
301 },
302 {
303 id: 'externalApiBytes' ,
304 metricId: 'vercel.external_api_request.transfer_bytes' ,
305 aggregation: 'sum' ,
306 groupBy: [ 'origin_hostname' ],
307 limit: HOST_LIMIT ,
308 description: 'Outbound bytes per external API hostname.' ,
309 },
310 ];
311
312 export function normalizerFor ( entry ) {
313 if (entry.normalizer) return entry.normalizer;
314 return defaultNormalize (entry.metricId, entry.aggregation, entry.groupBy);
315 }