You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Live stanza runs preload_app=False + 1 worker because preload_app=Truedeadlocks after gunicorn's fork() — PyTorch spins up OpenMP/MKL thread pools in the master, and fork() copies the memory image but not the threads, so the child blocks on a futex held by threads that no longer exist. Disabled in 2393b7c1 (Jan 2026).
Consequence: workers don't share models (no copy-on-write), so each worker costs ~5.9 GB (measured). That's why the healthcheck-flapping fix (ops#10) could only afford 2 workers, and why "/health stays answerable" is only probabilistic — it relies on not having ≥2 large (~12 s) tokenizations occupying both workers within the ~60–90 s detection window.
The lead
ADR 017 (asr-single-threaded-compute) solved the identical fork-after-OMP deadlock for the ASR service, four months later: set OMP_NUM_THREADS=1 + MKL_NUM_THREADS=1 → no thread pool → fork() is safe → preload_app=True + CoW model sharing + workers=2. The ADR explicitly says it applies to "any future ML-on-CPU service." Stanza never got this fix — it has no OMP/MKL env vars (only asr does), because the stanza disable predates the ADR-017 discovery.
If it works for stanza, extra workers become nearly free (shared models) → run 5–6 workers → /health answerability becomes robust even under a spike, removing the fragile assumption.
The catch (why it is not a blind flip)
ADR 017 notes single-threaded OMP makes each op 2–4× slower. Invisible for ASR's 3 s clips (sub-second), but stanza's large-text tail is already ~12 s → single-threaded could push it to ~24–48 s, inflating the very tail the healthcheck timeout was just tuned around. The other ADR-017 option (threaded gunicorn, workers=1 threads=N) is blocked because the stanza pipeline is marked not thread-safe.
In a staging stanza: OMP_NUM_THREADS=1 + MKL_NUM_THREADS=1, preload_app=True, workers>=2; compare the histogram — does the tail inflate unacceptably?
Tail acceptable → adopt preload + more workers (cheap), make /health robust, revisit the timeout.
Tail too inflated → keep preload off; pursue decoupling /health from the worker pool (progress-based liveness) instead, which sidesteps worker count entirely.
Why
Live stanza runs
preload_app=False+ 1 worker becausepreload_app=Truedeadlocks after gunicorn'sfork()— PyTorch spins up OpenMP/MKL thread pools in the master, andfork()copies the memory image but not the threads, so the child blocks on a futex held by threads that no longer exist. Disabled in2393b7c1(Jan 2026).Consequence: workers don't share models (no copy-on-write), so each worker costs ~5.9 GB (measured). That's why the healthcheck-flapping fix (ops#10) could only afford 2 workers, and why "/health stays answerable" is only probabilistic — it relies on not having ≥2 large (~12 s) tokenizations occupying both workers within the ~60–90 s detection window.
The lead
ADR 017 (
asr-single-threaded-compute) solved the identical fork-after-OMP deadlock for the ASR service, four months later: setOMP_NUM_THREADS=1+MKL_NUM_THREADS=1→ no thread pool →fork()is safe →preload_app=True+ CoW model sharing +workers=2. The ADR explicitly says it applies to "any future ML-on-CPU service." Stanza never got this fix — it has no OMP/MKL env vars (onlyasrdoes), because the stanza disable predates the ADR-017 discovery.If it works for stanza, extra workers become nearly free (shared models) → run 5–6 workers →
/healthanswerability becomes robust even under a spike, removing the fragile assumption.The catch (why it is not a blind flip)
ADR 017 notes single-threaded OMP makes each op 2–4× slower. Invisible for ASR's 3 s clips (sub-second), but stanza's large-text tail is already ~12 s → single-threaded could push it to ~24–48 s, inflating the very tail the healthcheck timeout was just tuned around. The other ADR-017 option (threaded gunicorn,
workers=1 threads=N) is blocked because the stanza pipeline is marked not thread-safe.Plan (data-gated)
OMP_NUM_THREADS=1+MKL_NUM_THREADS=1,preload_app=True,workers>=2; compare the histogram — does the tail inflate unacceptably?/healthrobust, revisit the timeout./healthfrom the worker pool (progress-based liveness) instead, which sidesteps worker count entirely.Refs
2393b7c1