[pull] canary from vercel:canary - #1363
Merged
Merged
Conversation
## What? Adds per-family compression configuration to `turbo-persistence` and configures the Turbopack filesystem cache according to each keyspace's access pattern: | Family | Compression | Reason | | --- | --- | --- | | Infra | LZ4 HC level 4 | Preserve LZ4 decode performance while improving write-time compression | | TaskMeta | LZ4 HC level 4 | Preserve latency-sensitive metadata reads | | TaskCache | LZ4 HC level 4 | Preserve latency-sensitive task-cache reads | | TaskData | zstd level 3 | Prioritize the dominant disk-size opportunity | The low-level configuration exposes only the three presets this database needs: LZ4, LZ4 HC4, and zstd3. Compression is applied consistently to SST blocks, blob values, and compaction output. Each database owns a `ThreadLocal` that lazily creates one lock-free zstd decompressor per participating thread; the linked zstd reports each context as about 96 KiB, and all contexts are released when the database drops. Each meta file stores its family's length-prefixed, bincode-encoded compression preset once. SST and blob headers remain unchanged, including the 12.5% minimum-savings fallback to uncompressed blocks. On normal database open, the stored marker must match the runtime `FamilyConfig`; a mismatch, invalid/truncated preset, or pre-marker meta file is rejected before reads are served. This intentionally requires a fresh/versioned cache after a codec change, consistent with `turbo-persistence`'s existing no-cross-version-compatibility policy. The marker also removes the duplicated keyspace-to-codec mapping from `sst_inspect`: it reads the codec directly from metadata. `turbo-persistence-tools` can inspect metadata without supplying a Turbopack-specific family configuration. ## Why? LZ4 decode performance is important for cache query latency, but using one codec for every keyspace leaves a substantial disk-size opportunity in TaskData. The families do not have the same tradeoff: metadata and task-cache reads should stay on the LZ4 decoder, while TaskData can spend modestly more CPU to reduce persistent cache size. The final three-workload A/B reduced fresh cache directories by **20.61% overall**, with no measured cold/write or warm/read regression (the observed -3.42% / -2.39% should be treated as noise, not a speed claim). ## How? ### Real Next.js cache A/B The complete A/B was re-run after the final fixed-preset, bincode-marker, and database-owned thread-local revision. Both the unchanged base worktree and final proposed working tree were bootstrapped with `pnpm install --frozen-lockfile` and `pnpm build-all`, using locally compiled native bindings on the same 8-vCPU Intel Xeon VM (16.3 GiB RAM, Linux 6.18.40, Node 24.14.1). Each workload used three fresh-cache builds and five warm-cache rebuilds; the table reports medians. ```sh TURBO_ENGINE_IGNORE_DIRTY=1 NEXT_TELEMETRY_DISABLED=1 \ node packages/next/dist/bin/next build <fixture> --turbopack ``` `test/e2e/filesystem-cache` additionally used `ENABLE_CACHING=1`. The other fixtures temporarily enabled `experimental.turbopackFileSystemCacheForBuild`; those fixture changes are not in this PR. | Workload | Cache size | Cold/write | Warm/read | | --- | ---: | ---:| ---: | | `test/e2e/filesystem-cache` | **-20.62%** | -2.24% | -3.00% | | `test/e2e/app-dir/app-rendering` | **-20.11%** | -6.04% | -1.47% | | `test/e2e/app-dir/client-reference-chunking` | **-21.02%** | -1.45% | -3.56% | | Combined medians | **-20.61%** (96.810 → 76.862 MiB) | **-3.42%** | **-2.39%** | Per-family inspection of a final filesystem-cache build, using the marker-derived codec: | Family | Total file size | Value-block savings | | --- | ---: | ---: | | Infra | 52 B | n/a | | TaskMeta | 6.31 MB | 29% | | TaskData | 18.17 MB | 66% | | TaskCache | 1.65 MB | n/a | The originally planned `app` and `app-static` fixtures could not run as standalone builds: `app` hit the existing Edge-runtime `process.cwd()` build failure, and `app-static` required an external data endpoint during page-data collection. They were replaced with the two successful repository fixtures shown above under the same base/proposed methodology. ### Temporary crate benchmark instrumentation The codec-specific Criterion cases were used locally to gather receipts, then removed from the delivered patch per review. | Codec | Synthetic write | vs LZ4 | Uncached get | Cached get | DB size | | --- | ---: | ---: | ---: | ---: | ---: | | LZ4 | 38.173 ms | baseline | 7.3004 µs | 10.735 µs | 21.59 MiB | | LZ4 HC4 | 256.31 ms | **+571.7%** | 3.4853 µs | 8.5467 µs | 21.57 MiB | | zstd3 | 57.790 ms | **+51.4%** | 6.2460 µs | 9.5323 µs | 21.49 MiB | The synthetic workload applies one codec to the entire database and barely separates codecs by size, so it is not representative of the real TaskData distribution. The all-HC write regression is real but confined to smaller families in the actual configuration. Cached reads do not decompress blocks, and the read samples contain substantial ordering/outlier noise. The real-cache A/B is the read-regression guard. ## Vercel Site ### Size 2.9G canary 2.2G this PR a 25% savings ### Performance Cold build (n=5), warm build (n=3), medians: | Scenario | Metric | Canary | Compression | Δ | |---|---|---|---|---| | **Cold** | wall (s) | 71.28 | 72.86 | +2.2% | | | user (s) | 555.21 | 570.23 | +2.7% | | | sys (s) | 94.49 | 95.00 | +0.5% | | | maxRSS (GB) | 17.95 | 17.98 | +0.2% | | **Warm** | wall (s) | 15.00 | 15.32 | +2.1% | | | user (s) | 7.59 | 9.98 | +31% | | | sys (s) | 12.79 | 12.87 | +0.6% | | | maxRSS (GB) | 3.78 | 3.86 | +2.0% | maxRSS converted at 1 GB = 2^30 bytes. Looking at tracing data i see in a cold build `persist` span went from 5.06s duration using 63s cpu time to 6.04s using 73s cpu time, a ~20% regression. Which explains the cpu regression and the time progression. Because the time regression is in the `persist` span during shutdown the added latency is somewhat hidden. Of course we also see a small wall/user time regression in warm builds due to the extra decompression costs for zstd. ### Verification - `cargo test -p turbo-persistence` (79 tests) - `cargo test -p turbo-tasks-backend` (99 unit tests plus integration suites) - `cargo test -p turbo-persistence-tools` - `cargo test -p turbo-persistence --all-targets --no-run` - `cargo fmt --all -- --check` - `cargo clippy -p turbo-persistence -p turbo-persistence-tools -p turbo-tasks-backend --all-targets -- -D warnings` - `cargo check -p turbo-persistence -p turbo-persistence-tools -p turbo-tasks-backend` - `pnpm lint-ast-grep` - `pnpm build-all` <!-- NEXT_JS_LLM --> <!-- fleet 621238c3-5a53-431b-8661-8c5b2920d55d --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )