Files
orca/.github/workflows
Neil 19e9ec695b perf(windows): ship the native process table to Windows relay hosts (#16598)
* feat(windows): let a relay host bind the native process table directly

The CIM fallback from #16550 answers on relay hosts, but it costs a
powershell.exe and ~1.4s per scan where the native reader costs ~57ms.
It is a parachute, not the destination.

Teach the loader a second source: the desktop app keeps resolving the
npm package, and a relay host -- which has none of our node_modules --
binds a bare `windows-process-tree.node` staged beside the bundle. The
CIM scan stays as the last resort, so a host with neither is unchanged.

Bind the addon directly rather than its package wrapper. lib/index.js
adds only a queue over getProcessList, and that queue is the wedge this
module already defends against: it latches a module-global
requestInProgress with no try/catch. We hold our own single-flight and
deadline, so going straight to the addon drops the duplicate.

Measured on a Windows 11 SSH host with ~1490 processes, running the
relay-externals bundle from the deployed relay directory:

  no addon staged   nativeAvailable=false  1247ms  (CIM)
  addon staged      nativeAvailable=true     57ms  memory restored

Degradation was exercised on that host, not just in fakes: a truncated
upload, a text file, and a foreign-arch ELF each fall through to the
scan rather than throwing, and restoring a good addon recovers. A file
that loads but lacks getProcessList is rejected by shape, because
binding to it would reject every read forever where falling through
still answers.

No artifact is staged yet, so this is inert until the packaging change
lands: today every relay takes the same CIM path it does now.

* build(relay): ship the Windows process-table addon to relay hosts

The CIM scan restored correctness on Windows SSH hosts, but it costs a
powershell.exe and ~1.4s per read where the native addon costs ~57ms. It
was always the floor, not the destination.

The addon cannot be npm-installed on a relay host: it carries a
binding.gyp, so npm rebuilds from source and the build wants
Spectre-mitigated libraries even where MSVC is already present. The
binary inside the published tarball loads, but predates our patch and
still caps enumeration at 1024 processes -- on a 1486-process host it
returned exactly 1024 rows with the querying process among the missing,
which reads as unavailable only under load. No published alternative
clears the bar either; the one fork with a working prebuild story still
carries the same cap.

So build it where a compiler exists and ship the result. The build script
refuses unpatched source -- checking the source rather than trusting the
install, because the Spectre hunk fails loudly while the 1024 hunk fails
silently -- and verifies the PE machine field so a cross-build cannot
emit host arch for another target.

The artifact is optional: hashed when present so a relay carrying it
never shares an immutable directory with one that does not, and never
probed, since requiring a file only a Windows build machine can produce
would make a correct relay read as MISSING and redeploy forever. Builds
on any other OS keep using the scan, unchanged.

arm64 cross-compiles from the x64 runner but needs the optional MSVC
ARM64 toolset, so it stays best-effort: a runner image without that
component should cost arm64 relays the fast path, not fail the release
the x64 relay is riding on. ORCA_REQUIRE_RELAY_NATIVE_ADDONS is a
per-arch list rather than a flag for exactly that reason.

* build(relay): require the arm64 process-table addon too

The arm64 cross-compile is no longer unproven. On a Windows x64 machine
with the MSVC v143 ARM64 build tools component installed, node-gyp
--arch=arm64 produces a genuine ARM64 image:

  x64    machine=0x8664  152064 bytes
  arm64  machine=0xaa64  139776 bytes

So arm64 stops being best-effort and joins x64 in the required list. It
was only best-effort because the component is optional and I had not seen
it succeed; a runner image without it now fails the build with MSB8020
naming the missing component, and that step runs before the long
packaging step so the failure costs seconds rather than twenty minutes.

The env var stays a per-arch list rather than reverting to a flag, so a
future arch can land best-effort before being promoted the same way.
2026-08-26 03:14:45 -07:00
..