diff options
| author | Dax <[email protected]> | 2026-04-02 22:34:33 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-03 02:34:33 +0000 |
| commit | aa2239d5decac0002de2ddd3d8f8c7f6ccccd760 (patch) | |
| tree | 938c4b16d1c8ff29c4864cd6637f3e451ef32440 /packages | |
| parent | 8daeacc98989c0cb1ce88c7fca94367e4249c9af (diff) | |
| download | opencode-aa2239d5decac0002de2ddd3d8f8c7f6ccccd760.tar.gz opencode-aa2239d5decac0002de2ddd3d8f8c7f6ccccd760.zip | |
add automatic heap snapshots for high-memory cli processes (#20788)
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/opencode/src/cli/cmd/tui/worker.ts | 3 | ||||
| -rw-r--r-- | packages/opencode/src/cli/heap.ts | 59 | ||||
| -rw-r--r-- | packages/opencode/src/flag/flag.ts | 1 | ||||
| -rw-r--r-- | packages/opencode/src/index.ts | 3 |
4 files changed, 66 insertions, 0 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts index a83645d89..643676e34 100644 --- a/packages/opencode/src/cli/cmd/tui/worker.ts +++ b/packages/opencode/src/cli/cmd/tui/worker.ts @@ -13,6 +13,7 @@ import { Flag } from "@/flag/flag" import { setTimeout as sleep } from "node:timers/promises" import { writeHeapSnapshot } from "node:v8" import { WorkspaceID } from "@/control-plane/schema" +import { Heap } from "@/cli/heap" await Log.init({ print: process.argv.includes("--print-logs"), @@ -23,6 +24,8 @@ await Log.init({ })(), }) +Heap.start() + process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { e: e instanceof Error ? e.message : e, diff --git a/packages/opencode/src/cli/heap.ts b/packages/opencode/src/cli/heap.ts new file mode 100644 index 000000000..bb5a3d093 --- /dev/null +++ b/packages/opencode/src/cli/heap.ts @@ -0,0 +1,59 @@ +import path from "path" +import { writeHeapSnapshot } from "node:v8" +import { Flag } from "@/flag/flag" +import { Global } from "@/global" +import { Log } from "@/util/log" + +const log = Log.create({ service: "heap" }) +const MINUTE = 60_000 +const LIMIT = 2 * 1024 * 1024 * 1024 + +export namespace Heap { + let timer: Timer | undefined + let lock = false + let armed = true + + export function start() { + if (!Flag.OPENCODE_AUTO_HEAP_SNAPSHOT) return + if (timer) return + + const run = async () => { + if (lock) return + + const stat = process.memoryUsage() + if (stat.rss <= LIMIT) { + armed = true + return + } + if (!armed) return + + lock = true + armed = false + const file = path.join( + Global.Path.log, + `heap-${process.pid}-${new Date().toISOString().replace(/[:.]/g, "")}.heapsnapshot`, + ) + log.warn("heap usage exceeded limit", { + rss: stat.rss, + heap: stat.heapUsed, + file, + }) + + await Promise.resolve() + .then(() => writeHeapSnapshot(file)) + .catch((err) => { + log.error("failed to write heap snapshot", { + error: err instanceof Error ? err.message : String(err), + file, + }) + }) + + lock = false + } + + timer = setInterval(() => { + void run() + }, MINUTE) + timer.unref?.() + } +} diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 27190f2eb..1ac52dd17 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -12,6 +12,7 @@ function falsy(key: string) { export namespace Flag { export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE") + export const OPENCODE_AUTO_HEAP_SNAPSHOT = truthy("OPENCODE_AUTO_HEAP_SNAPSHOT") export const OPENCODE_GIT_BASH_PATH = process.env["OPENCODE_GIT_BASH_PATH"] export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"] export declare const OPENCODE_PURE: boolean diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index bb14e0588..1fa027abf 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -35,6 +35,7 @@ import { JsonMigration } from "./storage/json-migration" import { Database } from "./storage/db" import { errorMessage } from "./util/error" import { PluginCommand } from "./cli/cmd/plug" +import { Heap } from "./cli/heap" process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { @@ -96,6 +97,8 @@ const cli = yargs(args) })(), }) + Heap.start() + process.env.AGENT = "1" process.env.OPENCODE = "1" process.env.OPENCODE_PID = String(process.pid) |
