Selaa lähdekoodia

chore: delete unused util/abort module + orphaned leak test (#27230)

Kit Langton 3 kuukautta sitten
vanhempi
sitoutus
e3684f36f9

+ 0 - 35
packages/opencode/src/util/abort.ts

@@ -1,35 +0,0 @@
-/**
- * Creates an AbortController that automatically aborts after a timeout.
- *
- * Uses bind() instead of arrow functions to avoid capturing the surrounding
- * scope in closures. Arrow functions like `() => controller.abort()` capture
- * request bodies and other large objects, preventing GC for the timer lifetime.
- *
- * @param ms Timeout in milliseconds
- * @returns Object with controller, signal, and clearTimeout function
- */
-export function abortAfter(ms: number) {
-  const controller = new AbortController()
-  const id = setTimeout(controller.abort.bind(controller), ms)
-  return {
-    controller,
-    signal: controller.signal,
-    clearTimeout: () => globalThis.clearTimeout(id),
-  }
-}
-
-/**
- * Combines multiple AbortSignals with a timeout.
- *
- * @param ms Timeout in milliseconds
- * @param signals Additional signals to combine
- * @returns Combined signal that aborts on timeout or when any input signal aborts
- */
-export function abortAfterAny(ms: number, ...signals: AbortSignal[]) {
-  const timeout = abortAfter(ms)
-  const signal = AbortSignal.any([timeout.signal, ...signals])
-  return {
-    signal,
-    clearTimeout: timeout.clearTimeout,
-  }
-}

+ 0 - 49
packages/opencode/test/memory/abort-leak-webfetch.ts

@@ -1,49 +0,0 @@
-import { abortAfterAny } from "../../src/util/abort"
-
-const MB = 1024 * 1024
-const ITERATIONS = 50
-
-const heap = () => {
-  Bun.gc(true)
-  return process.memoryUsage().heapUsed / MB
-}
-
-const server = Bun.serve({
-  port: 0,
-  fetch() {
-    return new Response("hello from local", {
-      headers: {
-        "content-type": "text/plain",
-      },
-    })
-  },
-})
-
-const url = `http://127.0.0.1:${server.port}`
-
-async function run() {
-  const { signal, clearTimeout } = abortAfterAny(30000, new AbortController().signal)
-  try {
-    const response = await fetch(url, { signal })
-    await response.text()
-  } finally {
-    clearTimeout()
-  }
-}
-
-try {
-  await run()
-  Bun.sleepSync(100)
-  const baseline = heap()
-
-  for (let i = 0; i < ITERATIONS; i++) {
-    await run()
-  }
-
-  Bun.sleepSync(100)
-  const after = heap()
-  process.stdout.write(JSON.stringify({ baseline, after, growth: after - baseline }))
-} finally {
-  void server.stop(true)
-  process.exit(0)
-}

+ 0 - 127
packages/opencode/test/memory/abort-leak.test.ts

@@ -1,127 +0,0 @@
-import { describe, test, expect } from "bun:test"
-import path from "path"
-
-const projectRoot = path.join(import.meta.dir, "../..")
-const worker = path.join(import.meta.dir, "abort-leak-webfetch.ts")
-
-const MB = 1024 * 1024
-const ITERATIONS = 50
-
-const getHeapMB = () => {
-  Bun.gc(true)
-  return process.memoryUsage().heapUsed / MB
-}
-
-describe("memory: abort controller leak", () => {
-  test("webfetch does not leak memory over many invocations", async () => {
-    // Measure the abort-timed fetch path in a fresh process so shared tool
-    // runtime state does not dominate the heap signal.
-    const proc = Bun.spawn({
-      cmd: [process.execPath, worker],
-      cwd: projectRoot,
-      stdout: "pipe",
-      stderr: "pipe",
-      env: process.env,
-    })
-
-    const [code, stdout, stderr] = await Promise.all([
-      proc.exited,
-      new Response(proc.stdout).text(),
-      new Response(proc.stderr).text(),
-    ])
-
-    if (code !== 0) {
-      throw new Error(stderr.trim() || stdout.trim() || `worker exited with code ${code}`)
-    }
-
-    const result = JSON.parse(stdout.trim()) as {
-      baseline: number
-      after: number
-      growth: number
-    }
-
-    console.log(`Baseline: ${result.baseline.toFixed(2)} MB`)
-    console.log(`After ${ITERATIONS} fetches: ${result.after.toFixed(2)} MB`)
-    console.log(`Growth: ${result.growth.toFixed(2)} MB`)
-
-    // Memory growth should be minimal - less than 1MB per 10 requests.
-    expect(result.growth).toBeLessThan(ITERATIONS / 10)
-  }, 60000)
-
-  test("compare closure vs bind pattern directly", async () => {
-    const ITERATIONS = 500
-
-    // Test OLD pattern: arrow function closure
-    // Store closures in a map keyed by content to force retention
-    const closureMap = new Map<string, () => void>()
-    const timers: Timer[] = []
-    const controllers: AbortController[] = []
-
-    Bun.gc(true)
-    Bun.sleepSync(100)
-    const baseline = getHeapMB()
-
-    for (let i = 0; i < ITERATIONS; i++) {
-      // Simulate large response body like webfetch would have
-      const content = `${i}:${"x".repeat(50 * 1024)}` // 50KB unique per iteration
-      const controller = new AbortController()
-      controllers.push(controller)
-
-      // OLD pattern - closure captures `content`
-      const handler = () => {
-        // Actually use content so it can't be optimized away
-        if (content.length > 1000000000) controller.abort()
-      }
-      closureMap.set(content, handler)
-      const timeoutId = setTimeout(handler, 30000)
-      timers.push(timeoutId)
-    }
-
-    Bun.gc(true)
-    Bun.sleepSync(100)
-    const after = getHeapMB()
-    const oldGrowth = after - baseline
-
-    console.log(`OLD pattern (closure): ${oldGrowth.toFixed(2)} MB growth (${closureMap.size} closures)`)
-
-    // Cleanup after measuring
-    timers.forEach(clearTimeout)
-    controllers.forEach((c) => c.abort())
-    closureMap.clear()
-
-    // Test NEW pattern: bind
-    Bun.gc(true)
-    Bun.sleepSync(100)
-    const baseline2 = getHeapMB()
-    const handlers2: (() => void)[] = []
-    const timers2: Timer[] = []
-    const controllers2: AbortController[] = []
-
-    for (let i = 0; i < ITERATIONS; i++) {
-      const _content = `${i}:${"x".repeat(50 * 1024)}` // 50KB - won't be captured
-      const controller = new AbortController()
-      controllers2.push(controller)
-
-      // NEW pattern - bind doesn't capture surrounding scope
-      const handler = controller.abort.bind(controller)
-      handlers2.push(handler)
-      const timeoutId = setTimeout(handler, 30000)
-      timers2.push(timeoutId)
-    }
-
-    Bun.gc(true)
-    Bun.sleepSync(100)
-    const after2 = getHeapMB()
-    const newGrowth = after2 - baseline2
-
-    // Cleanup after measuring
-    timers2.forEach(clearTimeout)
-    controllers2.forEach((c) => c.abort())
-    handlers2.length = 0
-
-    console.log(`NEW pattern (bind): ${newGrowth.toFixed(2)} MB growth`)
-    console.log(`Improvement: ${(oldGrowth - newGrowth).toFixed(2)} MB saved`)
-
-    expect(newGrowth).toBeLessThanOrEqual(oldGrowth)
-  })
-})