Skip to content

experimental/air: parallel gzip for the plain_tar snapshot packer - #6571

Open
ben-hansen-db wants to merge 3 commits into
mainfrom
air-plain-tar-pgzip
Open

experimental/air: parallel gzip for the plain_tar snapshot packer#6571
ben-hansen-db wants to merge 3 commits into
mainfrom
air-plain-tar-pgzip

Conversation

@ben-hansen-db

@ben-hansen-db ben-hansen-db commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

The air run plain_tar snapshot path (dirty working tree / no git ref) packages the code_source by shelling out to tar -czf. tar's built-in gzip is single-threaded, so for a large tree it dominates packaging latency.

This pipes tar -cf - (uncompressed) through klauspost/pgzip (MIT), a parallel drop-in for gzip that spreads compression across cores and still emits an ordinary gzip stream.

  • Level is DefaultCompression — the same level as the old tar -czf, not BestSpeed. The archive is re-uploaded on every run, so its size matters. So this is a pure latency win with no upload-size regression.
  • Compressing outside tar means no archive path is passed to tar, which lets us drop the Windows colon-in-path workaround the -f <path> form required.

Results (local packaging: git walk + tar + gzip; warm page cache)

Target before (tar -czf, serial gz6) after (tar -cf - | pgzip, gz6)
universe/research (7.4k files) 2,456 ms 716 ms (~3.4×)
gzip step alone, 470 MiB tar 7,940 ms 436 ms (~18×)

Same compression level, so the tarball is the same size as before (~24 MB for research). Level chosen from the curve on a 476 MiB tar: L1→L6 costs +150 ms for ~17% fewer bytes (97→80 MB); L6→L9 doubles time for ~1%, so 6 is the knee.

Validation

  • Output verified: gzip -t clean, entry count matches the file list, extracts correctly.
  • Existing snapshot_package_test.go unit tests pass; the internal/build license test passes for the new pgzip dep (// MIT in go.mod + NOTICE entry).

Follow-up

A separate PR (#6572) adds a warm snapshot cache on top of this, which helps most for large monorepos and cold page cache.


This pull request and its description were written by Isaac.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Approval status: pending

/experimental/air/ - approved by @vinchenzo-db

Files: experimental/air/cmd/snapshot_package.go

General files (require maintainer)

Files: NOTICE, go.mod, go.sum
Based on git history:

  • @pietern -- recent work in ./

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db, @rugpanov, @rclarey) can approve all areas.
See OWNERS for ownership rules.

@vinchenzo-db vinchenzo-db left a comment

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.

lgtm

@@ -9,6 +9,8 @@ import (
"os/exec"
"path/filepath"
"strings"

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.

is Go semantics/lint to have this empty space? if not then rm

@ben-hansen-db ben-hansen-db Sep 8, 2026

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.

need to keep it according to claude:

that blank line is the standard import grouping (stdlib vs third-party) that goimports/gofumpt enforce, and this repo runs both as formatters. Without it, gofmt sorts github.com/klauspost/pgzip alphabetically into the stdlib block (between fmt and os) and goimports re-adds the separator, so ./task fmt would put it right back.

Comment on lines +69 to +71
// level 9 buys almost nothing beyond that for ~2x the time. Compressing outside tar
// also passes no archive path to tar, sidestepping the Windows colon-in-path issue a
// `-f <path>` argument otherwise hits (tar reads the `C:` in `C:\out\x` as a host).

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.

Either dumb down this part or rm imo

@ben-hansen-db ben-hansen-db Sep 8, 2026

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.

trimmed

ben-hansen-db and others added 3 commits September 8, 2026 21:48
createPlainTarball shelled out to `tar -czf`, whose gzip is single-threaded and
dominates packaging time for a large code_source tree. Pipe `tar -cf -` through
klauspost/pgzip instead, spreading compression across cores. Measured ~4x on
universe/research (2456 ms -> 609 ms) and ~18x on the 470 MiB gzip step alone;
the output is an ordinary gzip stream. Level is BestSpeed since the uploaded size
does not matter for this workflow, only latency.

Compressing outside tar also passes no archive path to tar, which removes the
Windows colon-in-path workaround (bare `-f` basename + -C) the -czf form needed.

Co-authored-by: Isaac <no-reply@databricks.com>
Now that gzip is parallel the compression level is nearly free, so trade a little
CPU for a smaller upload -- the plain_tar archive is re-uploaded on every run.
DefaultCompression matches the old `tar -czf` size at ~18x the speed (research:
716 ms / 24 MB, vs BestSpeed 609 ms / 27 MB). Level 9 buys ~1% fewer bytes for
~2x the time, so 6 is the knee.

Co-authored-by: Isaac <no-reply@databricks.com>
Vincent found the pack-step comment too dense. Cut it from the
compression-level essay (that rationale lives in the PR description and
commit message) down to the two non-obvious whys: parallel gzip vs tar's
single-threaded -z, and compressing outside tar to avoid the Windows
colon-in-path issue.

Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 8155ccf

Run: 34283015251

Env 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux 1 275 15 7:46
💚​ aws windows 1 277 13 7:51
💚​ azure linux 1 274 15 8:05
💚​ azure windows 1 276 13 8:19
💚​ gcp linux 1 275 15 9:01
💚​ gcp windows 1 277 13 7:39
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 7 slowest tests (at least 2 minutes):
duration env testname
8:17 azure windows TestAccept
7:49 aws windows TestAccept
7:37 gcp windows TestAccept
4:06 gcp linux TestAccept
3:58 aws linux TestAccept
3:55 azure linux TestAccept
2:31 gcp linux TestFilerWorkspaceFilesExtensionsReadDir

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants