Skip to content

Cache RBS rewrites with bootsnap & add dsl --only-bootsnap-rbs-cache#2617

Open
KaanOzkan wants to merge 5 commits intomainfrom
kaan/rbs-bootsnap-cache
Open

Cache RBS rewrites with bootsnap & add dsl --only-bootsnap-rbs-cache#2617
KaanOzkan wants to merge 5 commits intomainfrom
kaan/rbs-bootsnap-cache

Conversation

@KaanOzkan
Copy link
Copy Markdown
Contributor

@KaanOzkan KaanOzkan commented May 8, 2026

Motivation

tapioca dsl rewrites RBS comments to Sorbet sig {} blocks on every loaded file via require-hooks. On Shopify's monolith this dominates wall time (locally ~16 min). Caching the post-rewrite bytecode in bootsnap drops it to ~4 min (4.2x).

Implementation

  • Opt-in via TAPIOCA_RBS_CACHE=1. The rewriter sets up bootsnap with a dedicated cache directory (default tmp/cache/bootsnap-tapioca-rbs, override via TAPIOCA_BOOTSNAP_CACHE_DIR) and forwards BOOTSNAP_READONLY to bootsnap's readonly: arg. After setup, Tapioca::RBS::BootsnapGuard is prepended on Bootsnap.singleton_class so the host's own Bootsnap.setup raises instead of silently clobbering the cache dir.

  • --only-bootsnap-rbs-cache populates the cache and exits before any RBI work (CI prime/consumer pattern, mutually exclusive with --verify). bootsnap stays an optional runtime dep; a Sorbet shim covers the static surface.

Tests

CLI tests in spec/tapioca/cli/dsl_spec.rb cover the flag's early-exit, the --verify mutex, and the guard's raise.

@KaanOzkan KaanOzkan force-pushed the kaan/rbs-bootsnap-cache branch 5 times, most recently from 7cbcfaa to 7ce6d57 Compare May 8, 2026 18:36
Set up bootsnap with a dedicated cache directory and load require-hooks so the RBS-to-sig rewrite is baked into the cached bytecode. The first run is slow (rewrite + compile every file); subsequent runs against the same cache directory skip the rewrite entirely.

Cache directory defaults to `tmp/cache/bootsnap-tapioca-rbs` (override with `TAPIOCA_BOOTSNAP_CACHE_DIR`); kept separate from the host app's regular bootsnap cache so rewritten iseqs don't leak into other processes. `BOOTSNAP_READONLY=1` flows through to bootsnap for consumers reading a pre-populated cache. `Bootsnap.log_stats!` prints hit/miss counts at exit so cache effectiveness is visible.
@KaanOzkan KaanOzkan force-pushed the kaan/rbs-bootsnap-cache branch 2 times, most recently from 6e4b544 to 4676c1e Compare May 8, 2026 19:08
#
# The host app must also skip its own Bootsnap.setup under this flag,
# otherwise it'll override our settings during Rails boot.
if ENV["TAPIOCA_RBS_CACHE"] == "1"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewriter is loaded before any CLI parsing so went with a ENV variable.

KaanOzkan added 4 commits May 8, 2026 17:46
`--only-bootsnap-rbs-cache` boots the app and loads DSL extensions/compilers (populating the RBS rewrite cache), then exits before any RBI work. Run it once in a CI prime step so downstream jobs read from a warm cache. The flag is mutually exclusive with `--verify`.
`bootsnap` is an optional dependency. `lib/tapioca/rbs/rewriter.rb` only requires it under `TAPIOCA_RBS_CACHE=1`, so it's not in the Gemfile and Sorbet can't resolve `Bootsnap.setup`/`Bootsnap.log_stats!`. Add a minimal shim under `sorbet/rbi/shims/bootsnap.rbi` declaring just the methods we call.
When `TAPIOCA_RBS_CACHE=1`, tapioca configures bootsnap with its own dedicated cache directory before loading the host app. If the host later runs `Bootsnap.setup` itself (Rails apps do this in `config/boot.rb`), the host's call clobbers tapioca's settings and silently breaks the RBS rewrite cache.

Prepend `Tapioca::RBS::BootsnapGuard` so any subsequent `Bootsnap.setup` raises `HostBootsnapSetupError` pointing at the fix (gate the host's setup on `TAPIOCA_RBS_CACHE`). Add a CLI test asserting the raise.
Add a `Rewriting RBS comments to Sorbet signatures` section under Usage explaining what the rewrite does and why it exists, plus subsections covering `TAPIOCA_RBS_CACHE=1` for caching rewrites via bootsnap, the `--only-bootsnap-rbs-cache` priming workflow for CI, and how to coordinate a host app's own `Bootsnap.setup`.
@KaanOzkan KaanOzkan force-pushed the kaan/rbs-bootsnap-cache branch from 4676c1e to 5e30f4c Compare May 8, 2026 21:49
@KaanOzkan KaanOzkan added the enhancement New feature or request label May 8, 2026
@KaanOzkan KaanOzkan marked this pull request as ready for review May 8, 2026 21:53
@KaanOzkan KaanOzkan requested a review from a team as a code owner May 8, 2026 21:53
Comment on lines +48 to +49
readonly_env = ENV["BOOTSNAP_READONLY"]
readonly = !readonly_env.nil? && !["0", "false"].include?(readonly_env)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants