fix(build): address WSL runtime review findings

This commit is contained in:
Jinwoo Hong
2026-07-10 02:04:36 -04:00
parent 15eb2b1bac
commit 6a3d3ca7f8
5 changed files with 67 additions and 25 deletions
@@ -51,29 +51,31 @@ function currentProcessStartToken() {
try {
if (process.platform === 'linux') {
const contents = readFileSync(`/proc/${process.pid}/stat`, 'utf8')
cachedProcessStartToken = contents.slice(contents.lastIndexOf(') ') + 2).split(' ')[19]
cachedProcessStartToken =
contents.slice(contents.lastIndexOf(') ') + 2).split(' ')[19] || null
} else if (process.platform === 'win32') {
cachedProcessStartToken = execFileSync(
'powershell.exe',
[
'-NoProfile',
'-NonInteractive',
'-Command',
`(Get-Process -Id ${process.pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`
],
{ encoding: 'utf8', timeout: 2_000, windowsHide: true }
).trim()
cachedProcessStartToken =
execFileSync(
'powershell.exe',
[
'-NoProfile',
'-NonInteractive',
'-Command',
`(Get-Process -Id ${process.pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`
],
{ encoding: 'utf8', timeout: 2_000, windowsHide: true }
).trim() || null
} else {
cachedProcessStartToken = execFileSync(
'ps',
['-o', 'lstart=', '-p', String(process.pid)],
{ encoding: 'utf8', timeout: 2_000 }
).trim()
cachedProcessStartToken =
execFileSync('ps', ['-o', 'lstart=', '-p', String(process.pid)], {
encoding: 'utf8',
timeout: 2_000
}).trim() || null
}
} catch {
cachedProcessStartToken = null
}
return cachedProcessStartToken || null
return cachedProcessStartToken
}
function parseWslRuntimeBuildSource(output) {
@@ -83,8 +85,7 @@ function parseWslRuntimeBuildSource(output) {
function parseWslRuntimePreparationOutput(output) {
const text = Buffer.isBuffer(output) ? output.toString('utf8') : String(output ?? '')
const lines = text.split(/\r?\n/)
const value = (name) =>
lines.find((line) => line.startsWith(`${name}=`))?.slice(name.length + 1)
const value = (name) => lines.find((line) => line.startsWith(`${name}=`))?.slice(name.length + 1)
const source = value('ORCA_WSL_WATCHER_BUILD_SOURCE')
const lease = value('ORCA_WSL_WATCHER_BUILD_LEASE')
if (!source || !lease) {
@@ -199,9 +199,11 @@ async function activeLeaseVersions(buildsDir, names, operations, options) {
let owner
let modifiedAt = 0
try {
owner = JSON.parse(await operations.readFile(join(leasePath, 'owner.json'), 'utf8'))
modifiedAt = (await operations.stat(leasePath)).mtimeMs
} catch {}
try {
owner = JSON.parse(await operations.readFile(join(leasePath, 'owner.json'), 'utf8'))
} catch {}
const alive = Number.isSafeInteger(owner?.pid) ? await options.ownerIsAlive(owner) : null
if (
alive === true ||
@@ -14,6 +14,43 @@ import {
const execFileAsync = promisify(execFile)
describe('immutable WSL runtime build retention', () => {
it('preserves a fresh lease while its owner metadata is being published', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-wsl-build-pending-lease-'))
const output = join(root, 'out', 'wsl-watcher')
const version = 'b'.repeat(20)
const buildsDir = `${output}.builds`
const build = join(buildsDir, version)
const lease = join(buildsDir, `${version}.lease-pending`)
try {
await mkdir(build, { recursive: true })
await mkdir(lease)
await utimes(lease, new Date(99_500), new Date(99_500))
await pruneImmutableRuntimeBuilds(output, {
maxCompletedBuilds: 0,
minRecentBuilds: 0,
maxBuildAgeMs: 0,
unknownLeaseAgeMs: 1_000,
now: () => 100_000
})
expect(await readdir(buildsDir)).toEqual(
expect.arrayContaining([version, `${version}.lease-pending`])
)
await pruneImmutableRuntimeBuilds(output, {
maxCompletedBuilds: 0,
minRecentBuilds: 0,
maxBuildAgeMs: 0,
unknownLeaseAgeMs: 1_000,
now: () => 101_000
})
expect(await readdir(buildsDir)).not.toContain(version)
expect(await readdir(buildsDir)).not.toContain(`${version}.lease-pending`)
} finally {
await rm(root, { recursive: true, force: true })
}
})
it('preserves current, recent, and actively leased builds while bounding completed versions', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-wsl-build-retention-'))
const output = join(root, 'out', 'wsl-watcher')
+6 -5
View File
@@ -1,6 +1,7 @@
import { randomUUID } from 'node:crypto'
import { execFile } from 'node:child_process'
import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { promisify } from 'node:util'
const DEFAULT_HEARTBEAT_INTERVAL_MS = 1_000
@@ -21,7 +22,7 @@ function delay(milliseconds) {
async function readOwner(lockPath, operations) {
try {
const parsed = JSON.parse(await operations.readFile(`${lockPath}/owner.json`, 'utf8'))
const parsed = JSON.parse(await operations.readFile(join(lockPath, 'owner.json'), 'utf8'))
return typeof parsed?.token === 'string' && Number.isSafeInteger(parsed.pid) ? parsed : null
} catch {
return null
@@ -78,7 +79,7 @@ async function defaultOwnerIsAlive(owner) {
async function heartbeatIsStale(lockPath, operations, now, staleMs) {
try {
const heartbeat = await operations.stat(`${lockPath}/heartbeat`)
const heartbeat = await operations.stat(join(lockPath, 'heartbeat'))
return now() - heartbeat.mtimeMs >= staleMs
} catch {
try {
@@ -125,7 +126,7 @@ function startHeartbeat(lockPath, owner, operations, intervalMs) {
const tick = async () => {
const current = await readOwner(lockPath, operations)
if (current?.token === owner.token) {
await operations.writeFile(`${lockPath}/heartbeat`, owner.token).catch(() => undefined)
await operations.writeFile(join(lockPath, 'heartbeat'), owner.token).catch(() => undefined)
}
}
const timer = setInterval(() => void tick(), intervalMs)
@@ -163,8 +164,8 @@ export async function withRuntimePublicationLock(
try {
await operations.mkdir(lockPath)
try {
await operations.writeFile(`${lockPath}/owner.json`, JSON.stringify(owner))
await operations.writeFile(`${lockPath}/heartbeat`, token)
await operations.writeFile(join(lockPath, 'owner.json'), JSON.stringify(owner))
await operations.writeFile(join(lockPath, 'heartbeat'), token)
} catch (error) {
await operations.rm(lockPath, { recursive: true, force: true })
throw error
@@ -7,6 +7,7 @@ export function createWatcherBindingWatchdog(
timeoutMs: number
): WatcherBindingWatchdog {
let recycling = false
// Why: concurrent native timeouts must trigger only one host recycle.
const recycleHost = (operation: string): void => {
if (recycling) {
return