Setting the file. One moment.
Bench Collect · Sandbox Bench · vercel/next.js · Skills Docs
ContentsBack to the top of the page 24.4
Bench Common
scripts/bench-collect.mjs
scripts/ bench-collect.mjs
JavaScript · 142 lines · 4 KB
import
path
from
'node:path'
13 import { loadConfig, sandboxScope } from './config.mjs'
14
15 const execFileP = promisify (execFile)
16 const CONFIG = loadConfig ()
17 const SCOPE = sandboxScope ( CONFIG )
18 const argv = process.argv. slice ( 2 )
19 const dir = argv. find (( a ) => ! a. startsWith ( '--' ))
20 const deadlineMin = argv. includes ( '--deadline-min' )
21 ? Number (argv[argv. indexOf ( '--deadline-min' ) + 1 ])
22 : 280
23 if ( ! dir || ! fs. existsSync (path. join (dir, 'status.json' ))) {
24 console. error ( 'usage: node bench-collect.mjs <runDir with status.json>' )
25 process. exit ( 1 )
26 }
27 const statusPath = path. join (dir, 'status.json' )
28 const st = JSON . parse (fs. readFileSync (statusPath, 'utf8' ))
29 const vms = Object. keys (st.vms ?? {})
30 function writeStatus ( patch ) {
31 Object. assign (st, patch, { updatedAt: new Date (). toISOString () })
32 fs. writeFileSync (statusPath, JSON . stringify (st, null , 2 ))
33 }
34 writeStatus ({ phase: 'collecting (recovery)' , pid: process.pid })
35 if (vms. length === 0 ) {
36 console. error (
37 'status.json lists no VMs — the run died before measurement; nothing to collect. ' +
38 'Check for a leaked snapshot builder with sandbox-sweep.mjs and relaunch the run.'
39 )
40 process. exit ( 1 )
41 }
42
43 async function sb ( args ) {
44 const scoped = [ 'sandbox' , ... args]
45 const sep = scoped. indexOf ( '--' )
46 scoped. splice (sep < 0 ? scoped. length : sep, 0 , ... SCOPE )
47 const { stdout } = await execFileP ( CONFIG .vercelBin, scoped, {
48 maxBuffer: 1 << 26 ,
49 })
50 return stdout
51 }
52
53 const pending = new Map (vms. map (( vm , i ) => [vm, i]))
54 const collected = []
55 const deadline = Date. now () + deadlineMin * 60000
56 while (pending.size > 0 && Date. now () < deadline) {
57 for ( const [ vm , idx ] of [ ... pending]) {
58 let out
59 try {
60 out = await sb ([
61 'exec' ,
62 vm,
63 '--timeout' ,
64 '2m' ,
65 '--' ,
66 'bash' ,
67 '-c' ,
68 'cat /vercel/sandbox/loop.done 2>/dev/null || echo RUNNING' ,
69 ])
70 } catch (e) {
71 // VM gone (timed out or removed): its data is lost; say so and move on.
72 console. error (
73 `${ vm }: unreachable (${ e . message . split ( ' \n ' )[ 0 ]. slice ( 0 , 100 ) }) — boot lost`
74 )
75 st.vms[vm] = { ... st.vms[vm], state: 'lost' }
76 writeStatus ({})
77 pending. delete (vm)
78 continue
79 }
80 const state = out. trim (). split ( ' \n ' ). pop ()
81 if (state === 'RUNNING' ) continue
82 if (state === 'LOOPOK' ) {
83 const local = path. join (dir, `results-vm${ idx }.jsonl` )
84 await sb ([ 'cp' , `${ vm }:/vercel/sandbox/results.jsonl` , local])
85 if (fs. existsSync (local) && fs. statSync (local).size > 500 ) {
86 collected. push (local)
87 st.vms[vm] = { ... st.vms[vm], state: 'done' }
88 writeStatus ({})
89 console. error ( `${ vm }: collected -> ${ local }` )
90 } else {
91 console. error ( `${ vm }: results too small; keeping VM for inspection` )
92 pending. delete (vm)
93 continue
94 }
95 } else {
96 const tail = await sb ([
97 'exec' ,
98 vm,
99 '--timeout' ,
100 '2m' ,
101 '--' ,
102 'bash' ,
103 '-c' ,
104 'tail -c 1500 /vercel/sandbox/loop.log' ,
105 ])
106 console. error ( `${ vm }: loop ended ${ state }; log tail: \n ${ tail }` )
107 }
108 try {
109 await sb ([ 'rm' , vm])
110 console. error ( `${ vm }: removed` )
111 } catch (e) {
112 console. error (
113 `${ vm }: rm failed: ${ e . message . split ( ' \n ' )[ 0 ]. slice ( 0 , 100 ) }`
114 )
115 }
116 pending. delete (vm)
117 }
118 if (pending.size > 0 ) await new Promise (( r ) => setTimeout (r, 60000 ))
119 }
120 for ( const [ vm ] of pending)
121 console. error ( `${ vm }: deadline exceeded, left running` )
122
123 if (collected. length === 0 ) {
124 console. error ( 'nothing collected' )
125 process. exit ( 1 )
126 }
127 writeStatus ({
128 phase: `done (recovered ${ collected . length }/${ vms . length } boots)` ,
129 })
130 console. log ( ` \n collected ${ collected . length }/${ vms . length } boots; analysis:` )
131 const { stdout } = await execFileP (
132 'node' ,
133 [
134 path. join (
135 path. dirname ( new URL ( import . meta .url).pathname),
136 'bench-analyze.mjs'
137 ),
138 dir,
139 ],
140 { maxBuffer: 1 << 24 }
141 )
142 console. log (stdout)