Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci-preflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ on:
description: Run the repository Dylint gate. Enabled only for the native Linux x86 lane.
type: boolean
default: false
cache-targets:
description: Cache Cargo target artifacts. False retains only the Cargo registry.
type: boolean
default: true
cache-key-suffix:
description: Stable cache family name for this lane.
type: string
default: shared

permissions:
contents: read
Expand Down Expand Up @@ -102,14 +110,15 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-targets: ${{ inputs.cache-targets }}
# Keep cache path discovery scoped to this checkout's workspace
# target root. rust-cache's automatic discovery can otherwise
# register a nested `target/tests/target` path and fail its post-job
# cleanup when that optional directory was never created (#1173).
workspaces: ". -> target"
# Key kept from the fan-out era so existing warm caches carry
# over. OS + Cargo.lock hash are included via Swatinem defaults.
key: ${{ inputs.label }}-shared
key: ${{ inputs.label }}-${{ inputs.cache-key-suffix }}
cache-on-failure: true

- name: Sync venv (frozen, no project install)
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ jobs:
label: windows-arm
rustdoc: false
integration-test: true
cache-targets: false
cache-key-suffix: registry
5 changes: 4 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
key: ubuntu-24.04-coverage
# Coverage target artifacts dominate its cache and are rebuilt on
# every source/lock change; retain registry downloads under a new key.
cache-targets: false
key: ubuntu-24.04-coverage-registry
cache-on-failure: true

- name: Sync
Expand Down
5 changes: 5 additions & 0 deletions ci/cache_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,15 @@
"v0-rust-release-binaries-x86_64-pc-windows-msvc-build-binaries-",
"v0-rust-release-binaries-aarch64-pc-windows-msvc-build-binaries-",
)
SUPERSEDED_TARGET_CACHE_PREFIXES = (
"v0-rust-windows-arm-shared-preflight-",
"v0-rust-ubuntu-24.04-coverage-coverage-",
)
RETIRED_CACHE_PREFIXES = (
*ALL_FEATURES_CACHE_PREFIXES,
*RELEASE_BUILD_CACHE_PREFIXES,
*RELEASE_BINARY_CACHE_PREFIXES,
*SUPERSEDED_TARGET_CACHE_PREFIXES,
)


Expand Down
20 changes: 18 additions & 2 deletions docs/cache-budget.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,21 @@ caches are also restore-only and retired from the main ref. The measured
12-entry family occupied 4,078,226,358 bytes. Release packaging, target builds,
and validation still run unchanged; the tradeoff is that a future release can
start those Rust target builds cold instead of restoring the old multi-gigabyte
archives. Shared development/preflight, coverage, musl, security, and Dylint
caches remain eligible for main saves.
archives.

Windows ARM preflight and coverage retain Cargo registry downloads but no
workspace target artifacts. Their prior main-ref target archives occupied
2,105,526,302 and 944,115,359 bytes, respectively, in the observed full
no-eviction cycle. The replacement registry-only keys use new namespaces, so
the budget workflow retires only the superseded target-bearing keys and keeps
the smaller replacements. Test coverage remains unchanged; Windows ARM and
coverage builds may take longer when their target artifacts are cold. The
measured no-eviction steady-state projection after these retirements is about
8.44 billion bytes, leaving roughly 1.06 billion bytes below the 9.5-billion
guard. Future Cargo.lock key churn can temporarily add replacement entries;
the main writer barrier and post-writer cleanup keep obsolete disabled-family
entries out of the settled budget, but that transient headroom should be
rechecked if future full cycles grow materially.

Other development/preflight, musl, security, and Dylint caches remain eligible
for main saves.
85 changes: 81 additions & 4 deletions tests/test_cache_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ def test_selects_only_exact_disabled_families_on_main(self) -> None:
"v0-rust-release-binaries-x86_64-pc-windows-msvc-build-binaries-Linux-x64-hash-version",
ref="refs/tags/v4.10.15",
),
entry(
21,
"v0-rust-windows-arm-shared-preflight-Windows_NT-arm64-hash-version",
),
entry(
22,
"v0-rust-ubuntu-24.04-coverage-coverage-Linux-x64-hash-version",
),
entry(
23,
"v0-rust-windows-arm-registry-preflight-Windows_NT-arm64-hash-version",
),
entry(
24,
"v0-rust-ubuntu-24.04-coverage-registry-coverage-Linux-x64-hash-version",
),
]

self.assertEqual(
Expand All @@ -115,7 +131,48 @@ def test_selects_only_exact_disabled_families_on_main(self) -> None:
for cache in candidates
if cache_budget.is_retired_cache(cache)
],
list(range(1, 4)) + list(range(7, 19)),
list(range(1, 4)) + list(range(7, 19)) + [21, 22],
)

def test_high_volume_lanes_keep_registry_only_under_new_namespaces(self) -> None:
root = Path(__file__).resolve().parents[1]
windows = (root / ".github/workflows/ci-windows.yml").read_text(
encoding="utf-8"
)
arm_job = re.search(r"(?ms)^ arm:.*?(?=^ \w+:|\Z)", windows)
self.assertIsNotNone(arm_job)
self.assertIn("cache-targets: false", arm_job.group())
self.assertIn("cache-key-suffix: registry", arm_job.group())

preflight = (root / ".github/workflows/ci-preflight.yml").read_text(
encoding="utf-8"
)
self.assertIn("cache-targets: ${{ inputs.cache-targets }}", preflight)
self.assertIn(
"key: ${{ inputs.label }}-${{ inputs.cache-key-suffix }}", preflight
)

coverage = (root / ".github/workflows/coverage.yml").read_text(encoding="utf-8")
coverage_cache = next(
block
for block in coverage.split("\n - ")
if "key: ubuntu-24.04-coverage-registry" in block
)
self.assertIn("cache-targets: false", coverage_cache)
self.assertIn("save-if: ${{ github.ref == 'refs/heads/main' }}", coverage_cache)
self.assertTrue(
{
"v0-rust-windows-arm-shared-preflight-",
"v0-rust-ubuntu-24.04-coverage-coverage-",
}
<= set(cache_budget.SUPERSEDED_TARGET_CACHE_PREFIXES)
)
self.assertFalse(
any(
prefix.endswith("registry-preflight-")
or prefix.endswith("registry-coverage-")
for prefix in cache_budget.RETIRED_CACHE_PREFIXES
)
)

def test_release_target_caches_are_restore_only(self) -> None:
Expand Down Expand Up @@ -252,6 +309,26 @@ def test_enforcer_retires_only_the_disabled_main_families(self) -> None:
"v0-rust-release-binaries-aarch64-pc-windows-msvc-build-binaries-Linux-x64-hash-version",
size=500,
),
entry(
7,
"v0-rust-windows-arm-shared-preflight-Windows_NT-arm64-hash-version",
size=2100,
),
entry(
8,
"v0-rust-ubuntu-24.04-coverage-coverage-Linux-x64-hash-version",
size=900,
),
entry(
9,
"v0-rust-windows-arm-registry-preflight-Windows_NT-arm64-hash-version",
size=50,
),
entry(
10,
"v0-rust-ubuntu-24.04-coverage-registry-coverage-Linux-x64-hash-version",
size=50,
),
]

class FakeAPI:
Expand All @@ -276,9 +353,9 @@ def delete_cache(self, cache_id: int) -> None:
api, sleep=lambda _seconds: None, attempts=2
)

self.assertEqual(api.deleted, [1, 2, 3, 5, 6])
self.assertEqual([int(cache["id"]) for cache in api.caches], [4])
self.assertEqual(budget, 200)
self.assertEqual(api.deleted, [1, 2, 3, 5, 6, 7, 8])
self.assertEqual([int(cache["id"]) for cache in api.caches], [4, 9, 10])
self.assertEqual(budget, 300)

def test_repeated_retirement_catches_a_cache_recreated_by_an_active_writer(
self,
Expand Down
Loading