Skip to content

Bring the CI cache back under budget - #794

Merged
d-chambers merged 4 commits into
devfrom
ci-cache-budget
Aug 1, 2026
Merged

Bring the CI cache back under budget#794
d-chambers merged 4 commits into
devfrom
ci-cache-budget

Conversation

@d-chambers

@d-chambers d-chambers commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Description

The repo's GitHub Actions cache sits at 14.04 GB against a 10 GB limit, so LRU eviction runs continuously and the test-data caches are the collateral. Every eviction forces a re-download of the whole registry from raw.githubusercontent.com, one file at a time with no retry, which is where the intermittent CI failures come from — a single 30 s read timeout fails the whole job before any test runs.

Measured breakdown of the 41 live caches:

prefix caches size
environment-* 35 10.37 GB
data-* 6 3.67 GB

The environment caches are 74% of the budget on their own.

Why the environment caches explode

cache-environment-key embedded $(date '+%Y-%m-%d'), minting a brand-new key every day. setup-micromamba then appends platform, python-args, and an env-file content hash, so the real multiplier is 3 platforms × 4 python versions × 2 env files ≈ 35 fresh caches per day at 240–560 MB each.

The date isn't pointless — with unpinned deps it's what picks up upstream package updates — so it's now ISO week (%G-W%V) rather than deleted. Same intent, ~7× less churn. %G rather than %Y because ISO week numbers pair with the ISO year and %Y-W%V collides across a new-year boundary.

Evidence this was eviction rather than a key-design problem: at registry hash 6f304af6 the Windows and macOS data caches survived while the Linux one was gone, so a run had to re-prime and re-save it. Same key, same day, one OS evicted and two kept is the LRU signature. The data cache key itself — data-{OS}-{DATA_VERSION}-{sha256(registry)}-{bust} — is content-addressed and correct, and is unchanged here.

Changes

  • Weekly environment cache key instead of daily (above).

  • restore-keys fallback for the data cache. A registry change previously missed the exact key and re-downloaded all 47 files. It now falls back to data-{OS}-{DATA_VERSION}-{cache_number}-, and pooch fetches only the added files. A prefix-only match reports cache-hit != 'true', so the prime and save steps still run and publish under the new exact key. get_key builds on get_restore_prefix so the two cannot drift.

    The key is reordered to put cache_number before the registry hash, so it can live in the prefix. Without that, bumping cache_number — the documented manual reset — would fall back onto the very cache the bump was meant to discard, silently doing nothing. The reorder costs one re-prime per OS on first run; a working reset knob is worth it.

  • The cache key now lives in the CI script, not the library. TestDataCacheInfo / get_test_data_cache_info were removed from dascore.utils.downloader and the key/prefix construction moved into .github/scripts/export_test_data_cache_env.py, its only consumer. A GitHub Actions cache key is not library API, and its tests do not belong in the test suite; DASCore stays agnostic about the CI backend. retry_if_failed stays — retrying a transient read timeout is useful to anyone fetching the registry, not just CI.

  • Unregistered files are pruned before saving. Restoring an older cache carries along files since dropped from the registry, which would otherwise ride into every later cache forever. Priming now deletes anything not in the full registry — the full one, not the primed subset, so a large file that was legitimately fetched is kept.

  • retry_if_failed=3 on the pooch fetcher, with the pooch floor raised >=1.2>=1.3 (the option landed in 1.3, so on 1.2 this would have been a TypeError at import). Priming pulls the whole registry in one pass, so one transient read timeout used to fail the job.

  • fail-fast: false on the test_code and min-deps matrices. This is a caching fix as much as a reporting one: a cancelled job never reaches its cache-save step, so one unrelated failure both hid the other results and forced every other job to re-prime on the next run. (network_tests already set this.)

  • Python matrix trimmed from 3.11–3.14 to 3.12–3.14. Each entry costs one environment cache per OS.

  • Min-deps moved from 3.13/3.14 to 3.11/3.14. Same job count and same cache count, but it now covers the oldest supported version, which is the more useful thing for a minimum-dependency run — and it keeps 3.11 verified despite leaving the full matrix.

  • requires-python raised to >=3.11 and the 3.10 classifier dropped. 3.10 was never in any CI matrix, so the package claimed support it did not verify; it also reaches end of life in October 2026. Every version pyproject now claims is tested somewhere.

  • python-gil added to the micromamba create-args. This is the fallout of the key change, and worth spelling out: python=3.14 alone lets the solver pick the free-threaded cp314t build on linux-64 (scipy is what tips it — python=3.14 scipy>=1.15.0 reproduces it locally, python=3.14 alone does not). dev only passed because its ubuntu 3.14 job kept restoring a pre-cp314t environment cache; the first fresh solve — this PR's, or dev's own next week — hits it. The job then has no orjson wheel (a pymseed dependency), tries to build it from source, and orjson refuses to build against free-threaded CPython. python-gil pins the GIL-enabled build; it resolves cleanly for 3.11–3.14, and free-threading keeps its own coverage in test_free_threaded.yml.

Expected steady state: ~5 environment caches per week plus data, comfortably inside the budget. A further ~2.3 GB returns on its own when dev reaches master, since dev's downloader.py rework already cut the data cache from 1014 MB to 237 MB per OS.

Not included

Pruning the now-unreachable dated environment-* caches will speed this up, but is an operational step rather than a repo change.

Changelog

  • changed breaking: DASCore requires Python 3.11 or later (was 3.10, which was never covered by CI and reaches end of life in October 2026).

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes.
  • documented the new feature with docstrings and/or appropriate doc page.
  • included tests. See testing guidelines. CI-only change; the cache-key tests were removed along with the library helper they covered.
  • added the "ready_for_review" tag once the PR is ready to be reviewed.

Summary by CodeRabbit

  • Breaking Changes

    • Python 3.10 is no longer supported. DASCore now requires Python 3.11+ (up to Python 3.14).
  • Bug Fixes

    • Improved download reliability with enhanced transient network retries.
    • Improved test-data cache keying, fallback restoration, and automated cleanup to reduce stale or incomplete caches.
  • Chores

    • Updated CI caching behavior (including more flexible restore fallbacks) and micromamba cache keying.
    • Adjusted CI Python/test matrices and disabled fail-fast for matrix jobs to preserve other results.

The Actions cache sits at 14.04 GB against a 10 GB limit, so eviction runs
constantly and the test-data caches are what it takes. Every eviction
re-downloads the whole registry from raw.githubusercontent.com one file at
a time with no retry, and a single 30 s read timeout fails the job before
any test runs.

Of the 41 live caches, 35 are environment-* holding 10.37 GB. The
cache-environment-key embedded $(date +%Y-%m-%d), so 3 platforms x 4 python
versions x 2 env files minted ~35 fresh caches every day. The date is not
pointless - with unpinned deps it is what picks up upstream updates - so it
becomes an ISO week rather than going away. %G, not %Y: ISO week numbers
pair with the ISO year and %Y-W%V collides across a new year.

That this was eviction and not a key-design problem: at registry hash
6f304af6 the Windows and macOS data caches survived while the Linux one was
gone and had to be re-primed. Same key, same day, one OS evicted and two
kept.

Also:

- The data cache gains a restore-keys fallback, so a registry change reuses
  the previous cache and pooch fetches only the added files. The key now
  puts cache_number before the registry hash so it can sit in the prefix;
  otherwise bumping it - the documented reset - would fall back onto the
  cache the bump meant to discard.
- Priming prunes files no longer in the registry, which a prefix restore
  would otherwise carry into every later cache. It compares against the
  full registry, not the primed subset, so a legitimately fetched large
  file stays.
- pooch gets retry_if_failed=3, and its floor moves to >=1.3, where that
  option was added; on 1.2 it would have raised TypeError.
- fail-fast: false on the test and min-deps matrices. A cancelled job never
  reaches its cache-save step, so one unrelated failure both hid the other
  results and made every other job re-prime next run.
- The test matrix drops 3.11 (each entry costs one environment cache per
  OS) and min-deps moves from 3.13/3.14 to 3.11/3.14 - same job count, but
  it now covers the oldest supported version, which is the more useful
  thing for a minimum-dependency run and keeps 3.11 verified.
- requires-python moves to >=3.11 and the 3.10 classifier goes. No CI
  matrix ever covered 3.10, so the package claimed support it did not
  verify, and 3.10 is EOL in October 2026.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fd34a5e-62d4-4ecb-b2c2-c974fb46b31a

📥 Commits

Reviewing files that changed from the base of the PR and between 4628c31 and 141c6d3.

📒 Files selected for processing (1)
  • tests/conftest.py

📝 Walkthrough

Walkthrough

Changes

Python support and CI cache behavior

Layer / File(s) Summary
Python support and test matrix
.github/actions/load-shared-vars/action.yml, .github/workflows/run_min_dep_tests.yml, .github/workflows/runtests.yml, docs/changelog.qmd, pyproject.toml, tests/conftest.py
Python support now requires 3.11+, with updated classifiers, dependency metadata, CI version matrices, non-fail-fast matrix execution, and revised exception-handling documentation.
Test-data cache key and restore contract
dascore/utils/downloader.py, .github/scripts/export_test_data_cache_env.py, .github/actions/mamba-install-dascore/action.yml, tests/test_utils/test_downloader.py
Cache keys now combine restore prefixes with registry hashes, and GitHub Actions exports and restores caches using prefix fallback.
Cache priming and environment setup
.github/scripts/cache_test_data.py, dascore/utils/downloader.py, .github/actions/mamba-install-dascore/action.yml, .github/doc_environment.yml, environment.yml
Cache priming retries downloads, removes unregistered files, uses ISO-week environment keys, pins GIL-enabled Python builds, and updates pooch constraints.

Possibly related PRs

Suggested labels: CI

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main goal: reducing CI cache usage and churn to stay under budget.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci-cache-budget

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the CI continuous integration label Jul 27, 2026
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (35f7e0d) to head (141c6d3).

Additional details and impacted files
@@            Coverage Diff            @@
##               dev      #794   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          164       164           
  Lines        17713     17696   -17     
=========================================
- Hits         17713     17696   -17     
Flag Coverage Δ
network 48.18% <ø> (-0.03%) ⬇️
unittests 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

micromamba resolved python=3.14 to the free-threaded (cp314t) build on
linux-64 once the environment cache key changed and forced a fresh solve;
scipy is what tips the solver there. That job then had no orjson wheel
(pymseed dependency) and failed to build it from source. python-gil keeps
the matrix on GIL-enabled builds; free-threading has its own workflow.

Also aligns the pooch floors in the env files with pyproject.
The test-data cache key is a GitHub Actions detail, so keep it in the CI
script that consumes it rather than in dascore.utils.downloader. Drops
TestDataCacheInfo, get_test_data_cache_info, and their tests.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
.github/scripts/export_test_data_cache_env.py (1)

18-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add focused tests for the new cache-key contract.

These helpers now control both actions/cache/restore and actions/cache/save, while the previous library-side cache-key tests were removed. Add cases covering DATA_VERSION/cache-number isolation, registry-hash suffixes, and exact restore-prefix formatting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/export_test_data_cache_env.py around lines 18 - 35, Add
focused tests for get_restore_prefix and get_key covering DATA_VERSION and
cache_number isolation, registry_hash appended as the key suffix, and the exact
restore-prefix formatting including the trailing separator. Replace reliance on
removed library-side cache-key coverage while validating both helpers’ cache-key
contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/scripts/export_test_data_cache_env.py:
- Around line 18-35: Add focused tests for get_restore_prefix and get_key
covering DATA_VERSION and cache_number isolation, registry_hash appended as the
key suffix, and the exact restore-prefix formatting including the trailing
separator. Replace reliance on removed library-side cache-key coverage while
validating both helpers’ cache-key contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0451b32e-bfea-4e5a-92ea-e65e5d1aca3b

📥 Commits

Reviewing files that changed from the base of the PR and between 36ea7c5 and 4628c31.

📒 Files selected for processing (6)
  • .github/actions/mamba-install-dascore/action.yml
  • .github/doc_environment.yml
  • .github/scripts/export_test_data_cache_env.py
  • dascore/utils/downloader.py
  • environment.yml
  • tests/test_utils/test_downloader.py
💤 Files with no reviewable changes (1)
  • tests/test_utils/test_downloader.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • dascore/utils/downloader.py

@d-chambers
d-chambers merged commit fc13329 into dev Aug 1, 2026
26 checks passed
@d-chambers
d-chambers deleted the ci-cache-budget branch August 1, 2026 05:54
d-chambers added a commit that referenced this pull request Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI continuous integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant