Skip to content

ci: optimise runtime#2860

Closed
CommanderStorm wants to merge 18 commits into
maplibre:mainfrom
CommanderStorm:precomp
Closed

ci: optimise runtime#2860
CommanderStorm wants to merge 18 commits into
maplibre:mainfrom
CommanderStorm:precomp

Conversation

@CommanderStorm

@CommanderStorm CommanderStorm commented Jun 5, 2026

Copy link
Copy Markdown
Member

Our CI is agonislingly slow.
Lets try if this improves CI perf..

This applies a few ideas:

  • switch to precompiled assets where possible
  • only dry-run the publishing step
  • twiddle with job order to fit the new runtimes to well-schedule all 10x2x2 cores.

@CommanderStorm CommanderStorm changed the title wip ci: switch to precompiled assets where possible Jun 5, 2026
@CommanderStorm CommanderStorm marked this pull request as ready for review June 5, 2026 23:22
Copilot AI review requested due to automatic review settings June 5, 2026 23:22

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@CommanderStorm CommanderStorm changed the title ci: switch to precompiled assets where possible ci: optimise runtime Jun 5, 2026
- unit-test-svc-pg: 3-entry matrix collapsed into one job iterating
  over postgis variants in a bash loop. Frees 2 runner slots at t=0
  (we sit on the 10-concurrent-job cap).
- unit-test-svc-minio: chained behind unit-test-svc-pg to share a slot.
- Minor-checks lane (single slot):
  lint-unit-test-js -> validate-schemas -> test-publish -> check-doc.
- lint-rust split: clippy stays as lint-rust (head of the rust chain
  that gates unit-test-rust and lint-rust-dependencies); cargo-hack
  --each-feature check moved to a new `check-features` job chained
  behind it. `just check-doc` extracted into its own (docker-only)
  job at the tail of the minor-checks lane.

t=0 fan-out: 10 jobs, fits exactly under the cap.
Comment thread .github/workflows/ci.yml Fixed
Moves the bash loop from unit-test-svc-pg into reusable justfile
recipes:

- _test-pg-cargo: cargo invocations against the existing DATABASE_URL
- test-pg-compile: --no-run compile of the test binaries
- test-pg-against image args sslmode: start an ephemeral postgis,
  init schema, run _test-pg-cargo, tear down
- test-pg-matrix: explicit list of test-pg-against calls, one per
  postgis variant. Depends on test-pg-compile so the binaries are
  built once.

`test-pg` now delegates to start + _test-pg-cargo (no behavioral
change). The matrix runner uses _test-pg-cargo directly to avoid the
docker-compose `start` dep that previously conflicted with the manual
docker run container on port 5432.

CI step shrinks to a single `just test-pg-matrix` call.
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Performance Comparison mainprecomp

Total Elapsed Time: 68.49s → 71.63s (+4.6%)
CPU Baseline: 91.13µs → 91.74µs (+0.7%)
Benchmark ID: timing

timing - Function execution time metrics.

+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| Function                   | Calls                        | Avg                              | P95                              | Total                            | % Total                      |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| martin::main               | 1 → 1 (+0.0%)                | 68.50s → 71.64s (+4.6%)          | 68.52s → 71.67s (+4.6%)          | 68.49s → 71.62s (+4.6%)          | 100.00% → 100.00% (+0.0%)    |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| martin::start              | 1 → 1 (+0.0%)                | 68.47s → 71.64s (+4.6%)          | 68.48s → 71.67s (+4.7%)          | 68.48s → 71.61s (+4.6%)          | 99.99% → 99.98% (-0.0%)      |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::get_tile          | 2200600 → 2200600 (+0.0%)    | 23.87µs → 26.08µs (+9.3%)        | 41.57µs → 42.17µs (+1.4%)        | 52.53s → 57.39s (+9.3%)          | 76.70% → 80.13% (+4.5%)      |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::get_http_response | 2200600 → 2200600 (+0.0%)    | 17.26µs → 19.39µs (+12.3%)       | 35.01µs → 35.52µs (+1.5%)        | 37.99s → 42.67s (+12.3%)         | 55.46% → 59.58% (+7.4%)      |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::get_tile_content  | 2200600 → 2200600 (+0.0%)    | 15.22µs → 16.64µs (+9.3%)        | 32.93µs → 33.09µs (+0.5%)        | 33.50s → 36.61s (+9.3%)          | 48.91% → 51.12% (+4.5%)      |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::new               | 2200600 → 2200600 (+0.0%)    | 2.46µs → 2.14µs (-13.0%)         | 1.84µs → 1.91µs (+3.8%)          | 5.41s → 4.71s (-12.9%)           | 7.90% → 6.58% (-16.7%)       |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::recompress        | 2200600 → 2200600 (+0.0%)    | 1.72µs → 1.78µs (+3.5%)          | 121.00ns → 140.00ns (+15.7%)     | 3.78s → 3.93s (+4.0%)            | 5.52% → 5.48% (-0.7%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| content::encode            | 100100 → 100100 (+0.0%)      | 36.44µs → 37.76µs (+3.6%)        | 53.82µs → 53.73µs (-0.2%)        | 3.65s → 3.78s (+3.6%)            | 5.33% → 5.28% (-0.9%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| source::get_sources        | 2200600 → 2200600 (+0.0%)    | 1.21µs → 1.20µs (-0.8%)          | 1.51µs → 1.60µs (+6.0%)          | 2.65s → 2.64s (-0.4%)            | 3.87% → 3.69% (-4.7%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| server::new_server         | 1 → 1 (+0.0%)                | 220.61µs → 284.80µs (+29.1%) ⚠️  | 220.67µs → 284.93µs (+29.1%) ⚠️  | 220.65µs → 284.85µs (+29.1%) ⚠️  | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| optimizer::encode          | 1 → 1 (+0.0%)                | 63.98µs → 77.34µs (+20.9%) ⚠️    | 64.00µs → 77.38µs (+20.9%) ⚠️    | 63.99µs → 77.31µs (+20.8%) ⚠️    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| optimizer::encode_into     | 4 → 4 (+0.0%)                | 8.66µs → 10.92µs (+26.1%) ⚠️     | 13.81µs → 24.59µs (+78.1%) ⚠️    | 34.63µs → 43.67µs (+26.1%) ⚠️    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| encode::write_to           | 4 → 4 (+0.0%)                | 8.02µs → 9.98µs (+24.4%) ⚠️      | 12.08µs → 22.93µs (+89.8%) ⚠️    | 32.08µs → 39.91µs (+24.4%) ⚠️    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| writer::with               | 72 → 72 (+0.0%)              | 127.00ns → 163.00ns (+28.3%) ⚠️  | 291.00ns → 380.00ns (+30.6%) ⚠️  | 9.21µs → 11.80µs (+28.1%) ⚠️     | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| 🗑️ optimizer::analyze      | 1 → 0 (-100.0%) 🚀           | 12.00µs → 0.00ns (-100.0%) 🚀    | 12.00µs → 0.00ns (-100.0%) 🚀    | 12.00µs → 0.00ns (-100.0%) 🚀    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+
| 🆕 tile::from_tile         | 0 → 4 (+100.0%) ⚠️           | 0.00ns → 3.12µs (+100.0%) ⚠️     | 0.00ns → 7.43µs (+100.0%) ⚠️     | 0.00ns → 12.50µs (+100.0%) ⚠️    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+----------------------------------+----------------------------------+----------------------------------+------------------------------+

alloc-bytes - Exclusive allocation bytes by each function (excluding nested calls).

+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| Function                       | Calls                        | Avg                            | P95                            | Total                          | % Total                    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_tile_content      | 2200600 → 2200600 (+0.0%)    | 93.0 KB → 93.0 KB (+0.0%)      | 181.9 KB → 181.9 KB (+0.0%)    | 195.2 GB → 195.2 GB (+0.0%)    | 84.06% → 84.06% (+0.0%)    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::encode                | 100100 → 100100 (+0.0%)      | 344.5 KB → 344.5 KB (+0.0%)    | 347.2 KB → 347.2 KB (+0.0%)    | 32.9 GB → 32.9 GB (+0.0%)      | 14.16% → 14.16% (+0.0%)    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| source::get_sources            | 2200600 → 2200600 (+0.0%)    | 1.5 KB → 1.5 KB (+0.0%)        | 2.4 KB → 2.4 KB (+0.0%)        | 3.1 GB → 3.1 GB (+0.0%)        | 1.35% → 1.35% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_tile              | 2200600 → 2200600 (+0.0%)    | 297 B → 297 B (+0.0%)          | 296 B → 296 B (+0.0%)          | 624.7 MB → 624.7 MB (+0.0%)    | 0.26% → 0.26% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_http_response     | 2200600 → 2200600 (+0.0%)    | 182 B → 182 B (+0.0%)          | 200 B → 200 B (+0.0%)          | 383.6 MB → 383.6 MB (+0.0%)    | 0.16% → 0.16% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| martin::start                  | 1 → 1 (+0.0%)                | 3.0 MB → 3.0 MB (+0.0%)        | 3.0 MB → 3.0 MB (+0.0%)        | 3.0 MB → 3.0 MB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| martin::main                   | 1 → 1 (+0.0%)                | 139.9 KB → 139.9 KB (+0.0%)    | 139.9 KB → 139.9 KB (+0.0%)    | 139.9 KB → 139.9 KB (+0.0%)    | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| optimizer::encode              | 1 → 1 (+0.0%)                | 67.7 KB → 67.7 KB (+0.0%)      | 67.7 KB → 67.7 KB (+0.0%)      | 67.7 KB → 67.7 KB (+0.0%)      | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| server::new_server             | 1 → 1 (+0.0%)                | 31.1 KB → 31.1 KB (+0.0%)      | 31.1 KB → 31.1 KB (+0.0%)      | 31.1 KB → 31.1 KB (+0.0%)      | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| tile::from_tile                | 4 → 4 (+0.0%)                | 376 B → 376 B (+0.0%)          | 376 B → 376 B (+0.0%)          | 1.5 KB → 1.5 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| encode::write_to               | 4 → 4 (+0.0%)                | 370 B → 370 B (+0.0%)          | 624 B → 624 B (+0.0%)          | 1.4 KB → 1.4 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| writer::with                   | 72 → 72 (+0.0%)              | 16 B → 16 B (+0.0%)            | 380 B → 380 B (+0.0%)          | 1.2 KB → 1.2 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| encode::dict_may_be_beneficial | 4 → 4 (+0.0%)                | 256 B → 256 B (+0.0%)          | 256 B → 256 B (+0.0%)          | 1.0 KB → 1.0 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| sort::sort                     | 4 → 4 (+0.0%)                | 120 B → 120 B (+0.0%)          | 160 B → 160 B (+0.0%)          | 480 B → 480 B (+0.0%)          | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| writer::write_header           | 4 → 4 (+0.0%)                | 69 B → 69 B (+0.0%)            | 104 B → 104 B (+0.0%)          | 276 B → 276 B (+0.0%)          | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+

Threads

Total Alloc: 3.0 MB → 3.0 MB (+0.0%)
Total Dealloc: 301.4 MB → 301.1 MB (-0.1%)
Mem Diff: -298.4 MB → -298.2 MB (+0.1%)

+--------+--------------------------+----------------------------+------------------------------+----------------------------+--------------------------------+
| Thread | CPU % Avg                | CPU % Max                  | Alloc                        | Dealloc                    | Mem Diff                       |
+--------+--------------------------+----------------------------+------------------------------+----------------------------+--------------------------------+
| martin | 0.10% → 0.10% (+0.0%)    | 15.90% → 16.00% (+0.6%)    | 2.8 MB → 2.8 MB (+0.0%)      | 1.9 MB → 1.9 MB (+0.0%)    | 927.2 KB → 935.3 KB (+0.9%)    |
+--------+--------------------------+----------------------------+------------------------------+----------------------------+--------------------------------+
| hp-mcp | 0.00% → 0.00% (+0.0%)    | 0.00% → 0.00% (+0.0%)      | 54.7 KB → 54.7 KB (+0.0%)    | 3.4 KB → 3.4 KB (+0.0%)    | 51.4 KB → 51.4 KB (+0.0%)      |
+--------+--------------------------+----------------------------+------------------------------+----------------------------+--------------------------------+

Generated with hotpath-rs

justfile's `export DATABASE_URL := 'postgres://...@localhost:' + PGPORT
+ '/db' + ...` overrides any caller-supplied DATABASE_URL when a new
`just` process starts. test-pg-against was exporting the matrix-built
DATABASE_URL in bash and then invoking `just _test-pg-cargo`, which
re-entered just and reset DATABASE_URL to the justfile default
(:5411/db), so cargo test connected to the wrong port and missing
database.

Inline the three cargo test invocations directly in test-pg-against so
the bash-exported DATABASE_URL is what cargo sees. Drop _test-pg-cargo
and revert `test-pg` to inline cargo commands (its original shape).

Also rename PG* shell locals to *_LOCAL to keep them clearly distinct
from the inherited PG* env vars consumed by tests/fixtures/initdb.sh.
@CommanderStorm CommanderStorm marked this pull request as draft June 6, 2026 05:28
…shot

Address two critical-section problems in the PR pipeline:

- `unit-test-rust` ran `test-packages-ci` then `test-doc` serially in one
  job (~10 min each = ~20 min). Extract `test-doc-rust` into its own job
  gated on the same `lint-rust` predecessor. The two now run in parallel,
  cutting that lane to ~10 min wall clock.

- `lint-rust-dependencies` recompiles the workspace on nightly for
  `cargo-shear` (~30 min). It was the longest single critical-section
  after `lint-rust`. Gate it to `event_name != 'pull_request'`: still
  runs on main and release, no longer blocks PR. `package` already
  doesn't gate on it; `done` keeps it in `needs:` but the merge gate
  no longer treats `skipped` as fatal so PR runs pass cleanly.

- Bless `pg_table_source_test::table_source-4` snapshot: bounds[3]
  shifted by 1 ULP (84.24401966908702 -> 84.24401966908701), likely a
  postgis version drift in ST_Extent precision on the matrix's first
  variant (postgis:11-3.0-alpine).
Comment thread .github/workflows/ci.yml Fixed
Comment thread .github/workflows/ci.yml Fixed
Skipping lint-rust-dependencies on PR meant cargo-shear failures only
showed up on the post-merge main push - a red main goes unnoticed in
practice, so the gate is effectively missing.

Run it on every PR again, but add Swatinem/rust-cache keyed
`shear-nightly` so the nightly workspace compile is amortised across
runs. On a warm cache the 30 min should drop to a few minutes; first
run after this change populates the cache.

Restore done's `skipped` failure clause now that nothing is
intentionally skipped on PR.
Comment thread .github/workflows/ci.yml Fixed
CommanderStorm and others added 4 commits June 6, 2026 08:32
Last-ULP value differs between runs because of postgres parallel
ST_Extent aggregation order. Previous bless made one variant pass and
another fail. Use insta's path redaction to mask just bounds[3]; the
remaining three bounds, srid, geometry, and properties are still
asserted exactly.
zizmor flags every Swatinem/rust-cache invocation as potentially cache-
poisonable. GitHub Actions cache is scoped per-ref, so a PR cannot
write to main's cache scope, and PR scopes are isolated from each
other. The warning is noise for our trust boundary.

Annotate all 5 uses (unit-test-rust, test-doc-rust, lint-rust,
check-features, lint-rust-dependencies) with the standard ignore.
Comment thread martin/tests/pg_table_source_test.rs Outdated
The bounds[3] snapshot mismatch was non-determinism, not a postgis
version drift: same image, same code, same test produced 84.244...8701
on some runs and ...8702 on others. The redaction was a band-aid; main
is consistent (always ...8702) because its CI setup happens to land in
a single-worker regime.

Set max_parallel_workers_per_gather=0 on the docker-run command so
ST_Extent aggregation runs single-threaded and the floating-point sum
order is deterministic. Snapshot file reverted to match main.
Comment thread .github/workflows/ci.yml
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Run cargo test
run: just test-packages-ci
test-doc-rust:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

split from just check, because largest critical section now

@CommanderStorm CommanderStorm marked this pull request as ready for review June 6, 2026 10:12
@CommanderStorm

Copy link
Copy Markdown
Member Author

Churn is necessary due to the mln introductions impact on compile times

@CommanderStorm CommanderStorm requested a review from nyurik June 6, 2026 10:13
cargo publish --dry-run was spending ~15 min on the per-crate
"extract the tarball and rebuild it from scratch" verification.
Workspace-level compilation is already covered by lint-rust and
check-features (both compile against default features). The remaining
value of test-publish - validating the published Cargo.toml shapes,
manifest fields, and tarball file list - is preserved.

Also drop the rendering-deps apt-install since --no-verify never links
maplibre-native.

Trade-off: --no-verify would miss bugs that only manifest when a
single crate is built in isolation (e.g. a file omitted from the
package include glob). These are rare; cargo's manifest check still
catches missing path-only deps, missing version fields, etc.
- package: only needs the 7 build-binary jobs whose artifacts it
  packages. Tests, lints, and test-publish no longer gate packaging.
- done: explicit merge gate listing every check, test, and build whose
  failure should block merge. unit-test-svc-minio was previously
  orphaned (nothing depended on it); now gated here.
- musl builds: chain behind lint-rust. They're relatively fast and
  don't need a t=0 slot; freeing those 2 slots leaves headroom for
  cross-workflow contention without queuing.

t=0 fan-out: 10 -> 8.

Critical path side effect: package can now start as soon as the
slowest build finishes (instead of waiting for unit-test-rust ubuntu),
so when the rust lane dominates, package runs in parallel with it.
lint-rust is ~13 min on cold cache; gating musl on it would delay the
musl jobs by that much on cache-miss runs. The dependency is only there
to free a t=0 slot, not to actually gate semantics. lint-unit-test-js
runs in ~32s either way (npm cache stable), giving the same
slot-pressure relief with a far smaller worst-case delay.
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