()
| 202 | }; |
| 203 | |
| 204 | const main = async () => { |
| 205 | const config = new Config(); |
| 206 | config.setPort(PORT); |
| 207 | config.setToken(TOKEN); |
| 208 | config.setConcurrent(10); |
| 209 | |
| 210 | const browserless = new Browserless({ config, metrics: new Metrics() }); |
| 211 | await browserless.start(); |
| 212 | const dataDir = await config.getDataDir(); |
| 213 | console.log(`Server up on :${PORT} — ${WAVES} waves, ~60 requests each\n`); |
| 214 | |
| 215 | const results = []; |
| 216 | const requestFailures = []; |
| 217 | |
| 218 | for (let wave = 1; wave <= WAVES; wave++) { |
| 219 | const startedAt = Date.now(); |
| 220 | const failures = await pool(scenarios(wave)); |
| 221 | requestFailures.push(...failures.map((f) => `wave ${wave}: ${f}`)); |
| 222 | // Let in-flight teardown (browser closes, dir deletes) finish |
| 223 | await sleep(2000); |
| 224 | |
| 225 | const mem = await settleAndMeasure(); |
| 226 | const state = internals(browserless); |
| 227 | const dataDirs = await countDataDirs(dataDir); |
| 228 | results.push({ dataDirs, mem, state, wave }); |
| 229 | |
| 230 | console.log( |
| 231 | `wave ${wave}: heap=${mb(mem.heapUsed)} rss=${mb(mem.rss)} ` + |
| 232 | `sessions=${state.sessions} timers=${state.timers} ` + |
| 233 | `limiterQueue=${state.limiterQueue} fsCache=${state.fsCacheEntries} ` + |
| 234 | `writeChains=${state.writeChains} ` + |
| 235 | `cfgListeners=${state.configListeners} dataDirs=${dataDirs} ` + |
| 236 | `(${((Date.now() - startedAt) / 1000).toFixed(1)}s, ${failures.length} req errors)`, |
| 237 | ); |
| 238 | } |
| 239 | |
| 240 | await browserless.stop(); |
| 241 | await sleep(1000); |
| 242 | const finalMem = await settleAndMeasure(); |
| 243 | |
| 244 | // ---- Verdict ---------------------------------------------------------- |
| 245 | console.log('\n--- Analysis ---'); |
| 246 | const measured = results.slice(WARMUP_WAVES); |
| 247 | const deltas = measured |
| 248 | .slice(1) |
| 249 | .map((r, i) => r.mem.heapUsed - measured[i].mem.heapUsed); |
| 250 | const avgGrowth = deltas.length |
| 251 | ? deltas.reduce((a, b) => a + b, 0) / deltas.length |
| 252 | : 0; |
| 253 | const monotonic = deltas.length > 1 && deltas.every((d) => d > 0); |
| 254 | |
| 255 | const problems = []; |
| 256 | const warnings = []; |
| 257 | const last = results[results.length - 1]; |
| 258 | |
| 259 | // Only a large, unambiguous average growth is a hard failure. A small |
| 260 | // monotonic creep across this few measured waves is too noise-sensitive to |
| 261 | // block CI — GC timing and heap fragmentation alone can produce a short run |
no test coverage detected