Remove nested turbo from package build scripts#26
Merged
Conversation
Each package's "build" script invoked `yarn turbo run prepublish`, which re-enters turbo from inside a workspace already being built by the outer turbo run. The nested invocation discovers a different repo-root hash than the outer one, so it spawns its own turbo daemon with its own inotify watches and Unix socket. When many rrweb packages build in parallel, several nested turbos race to create the same daemon and the losers deadlock in futex_wait on Linux (macOS is more forgiving about socket/pidfile races and rarely hits this). Inline each "build" with its own "prepublish" command (`tsc -noEmit && vite build`, etc.) — matching the pattern already used by `packages/utils`. The outer turbo's graph already handles dep ordering via ^build, so the nested layer was redundant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RobertPakkoLD
approved these changes
Apr 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
\"build\": \"yarn turbo run prepublish\"in each package with the command the nested turbo was going to run (e.g.tsc -noEmit && vite build), matching the existing pattern inpackages/utils.turbo run buildfrom a consumer repo) already walks the dep graph via^build, so the nested layer was redundant — all it did was re-enter turbo from inside a workspace already being scheduled.Why
On Linux,
yarn buildof a consumer project that depends on these packages (we hit this in launchdarkly/observability-sdk) would hang indefinitely with no subprocess activity. Every turbo process stuck infutex_wait_queue. Novite, notscever spawned.Root cause: each nested
yarn turbo run prepublishre-runs turbo's root discovery from the package's own CWD, findsrrweb/package.json(withworkspaces), and computes a repo-root hash different from the outer turbo's root hash. That means it tries to start its own turbo daemon — separate inotify instance, separate Unix socket under/tmp/turbod/<hash>/. When 10+ rrweb packages build in parallel, several nested turbos race to create the same daemon. On Linux, losing that race leaves you with a half-open socket connection to a daemon that's already been replaced, and you sit infutex_waitforever. macOS rarely hits this becausekqueue/fseventsand BSD socket semantics resolve the race faster.Observed symptom when stuck:
Four daemons for one build = four root hashes = turbo thinks each package is its own root.
Additionally, most of these scripts were
yarn turbo run prepublishwithout a-Ffilter, so each nested turbo tried to scheduleprepublishacross the entire rrweb workspace again — a graph the outer turbo had already walked.After
Only one turbo daemon exists (the one the outer run starts).
yarn build:sdkinobservability-sdkgoes from "stuck, need to cancel and retry 3× on Linux" to ~58s deterministic.Semantics are preserved: each new
buildscript is the verbatim body of the same package'sprepublishscript.prepublishis left in place since it's a yarn/npm publish lifecycle name.Test plan
yarn buildinobservability-sdkon Linux — completes cleanly (18/18 tasks, no hang)🤖 Generated with Claude Code