Repository navigation
Conversation
|
This PR just sets up the forking, but otherwise leaves all IPC just the same as prod We could consider deploying this directly to make prod apollo run faster TODO:
Consider:
|
| # Warm entry.py's heavy deps so forked children inherit them (copy-on-write). | ||
| # Mirrors entry.py's imports; a missing one is just imported per-child - slower, | ||
| # never wrong. | ||
| PRELOAD = ( |
There was a problem hiding this comment.
I'd like to pull this preload list out of here and into a yaml file, with some comments to explain what it is and what it's doing
Also, since 95% of calls will be through global_chat, can we add its heaviest dependencies here? Claude suggests Pinecone and openai, but maybe there are others.
|
I ran some review with claude which is worth checking up on: The following issues mean the forks are not safe to deploy to prod:
I think that 4th one relates to calls to apollo through the CLI - a super cool feature which has never been used. I think we can afford to drop that stuff BUT Id' rather formally remove it from the server and CLI than just leave it broken. If there's an easy fix, I'd like to adopt it for now. Also some suggested manual tests:
|
Short Description
Adds an opt-in warm fork-server for Python services: a long-lived master pays the startup cost (
poetry run+ heavy imports) once and forks a fresh child per request, cutting warm per-request latency ~25× whilekeeping the per-request process isolation. This is gated behind
APOLLO_FORK_SERVER=true; the spawn path is unchanged when off.Fixes #
Implementation Details
Today every request spawns
poetry run python services/entry.py, paying ~210 msof
poetry run+ ~450 ms of heavy imports (sentry/opentelemetry/langfuse/anthropic)before any work. This PR pays that once in a warm master and forks per request.
services/entry.fork.py(new) — the master. Preloadsentry.py's heavylibraries once with no init, so it stays single-threaded and fork-safe, then
os.fork()s a fresh child per request over a unix socket. Each child importsentry.pyafter forking, so entry.py's own init (OTel/Langfuse/Sentry) runsin the child where forking is safe — this is what lets
entry.pystayuntouched. The child
dup2()s the socket onto stdout/stderr so servicelogging streams unchanged, emits
PID:/EXIT:control lines, and returns theresult via the existing output-file contract.
platform/src/bridge.fork.ts(new) — all Node-side fork code (warm-mastersingleton, unix-socket client,
runForked). Same output-file + error-mappingcontract as the spawn path.
platform/src/bridge.ts— 3-line change: import, flag, oneifbranch.The spawn path is otherwise untouched.
.env.example— documentsAPOLLO_FORK_SERVERand, for macOS,OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES(a forked child touching an Obj-Cframework otherwise aborts; no-op on Linux, so prod needs nothing).
AI Usage
Please disclose whether you've used AI in this work (it's cool, we just want to
know!):
You can read more details in our
Responsible AI Policy