Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow updating an existing cache #505

Open
davidsbond opened this issue Jun 25, 2020 · 25 comments
Open

Allow updating an existing cache #505

davidsbond opened this issue Jun 25, 2020 · 25 comments
Labels
cache enhancement New feature or request

Comments

@davidsbond
Copy link

davidsbond commented Jun 25, 2020

GitHub actions supports caching across builds which is super powerful. However, once a cache has been saved it can no longer be modified. While this is fine for most scenarios where you need the cache there are a few times when you would like to update it.

I created this PR on the actual cache action to allow updating a cache when providing it in configuration:

actions/cache#353

I created it after reading the related issue:

actions/cache#342

However, it seems like something either needs to change in this package or server-side on the actions cache.

My use-case for wanting to update a cache is when using GitHub actions in golang projects. Go supports a test cache which allows tests to be skipped if the code they use has not changed, which can significantly improve test times (especially in large projects). Each time I run my tests, I'd like to maintain a single cache for tests, that way, when a run completes I can update the cache and all my workflows share a single test cache

There are some extra use-cases described on the issue above.

@davidsbond davidsbond added the enhancement New feature or request label Jun 25, 2020
@smorimoto
Copy link
Contributor

I would also like to support this proposal.

@half2me
Copy link

half2me commented Sep 4, 2021

I need this too

@eLarocque
Copy link

same

@CAMOBAP
Copy link

CAMOBAP commented Oct 27, 2022

@Blissful89
Copy link

#poke

@hyperxpro
Copy link

+1

3 similar comments
@stephanrotolante
Copy link

+1

@DenizUgur
Copy link

+1

@emrebayramc
Copy link

+1

@YegorMedvedev
Copy link

In the meantime, here's a workaround that I use:

- name: Get cache
  uses: actions/cache@v3
  with:
    path: ... # a list of cache paths
    key: ${{ runner.os }}-my-cache-${{ github.head_ref }}-${{ hashFiles(...for example yarn.lock...) }}

- name: Do something to change a cache
  run: ...

- name: Enforce cache update for a local NX cache
  run: |
    STATE_CACHE_KEY="${{ runner.os }}-my-cache-${{ github.head_ref }}-${{ hashFiles(...) }}"
    echo "STATE_CACHE_KEY=${STATE_CACHE_KEY}" >> $GITHUB_ENV

@emrebayramc
Copy link

+1

4 similar comments
@wind57
Copy link

wind57 commented Jun 9, 2023

+1

@arpanrec
Copy link

arpanrec commented Jul 8, 2023

+1

@bexsoft
Copy link

bexsoft commented Jul 12, 2023

+1

@nnsay
Copy link

nnsay commented Jul 13, 2023

+1

@dsame
Copy link

dsame commented Jul 25, 2023

Updating the cache is supposed to be done with restore-keys

This input allows to restore the cache even if it is not fully matched. And to generate the new key to update the cache do one of:

  1. include the changing file into ${{ hashFiles(...) }}
  2. set output of the previous step and include it into the key

@isaacbowen
Copy link

@dsame This is exactly what I was missing. Thank you for sharing! Super super super helpful. ❤️

@pauldraper
Copy link

@dsame's workaround is the best available currently, though it does have problems.

Github hard-limits repos to 10GB of caches. So creating lots of caches can push other less-frequent-but-helpful caches to deletion. Whereas if you could update/replace an existing cache, that wouldn't happen.

wongsyrone added a commit to wongsyrone/wongsyrone-scale-build-ntfs3 that referenced this issue Sep 27, 2023
CI: split pkg build and update w/ iso

ntfs3 patches from paragon group (unupstream)

openseachest: use upstream pull script

install pkgs

single-branch

add artifacts

ignore error for make all step to get log artifacts

china timezone

linux: merge linux 6.1

update grub2 to latest

remove grub2 single job

 * this is leftover of previous bash-based build script

add iso and update chksum files

use latest pip packages

 * pin jsonschema due to schema exception

CI: use git from Ubuntu PPA

git: depth 1

prepend sudo to cmdline

show info after build

CI: 8G root; 10G swap + 7G mem = 17G

update smartmontools to 7.3

CI: reclaim disk space

truenas/zfs@165d115

CI: do not run in the background

CI: setup-python github action uses this dir

CI: only use setup-python for scale-build

do NOT set PYTHON env

CI: add comments and echoes

disable packages with '-dbg' by removing them from manifest

scst: back to official after cleanup dbg pkgs

copy manifest from update dir

use python 3.11

do NOT show kernel config changes

CI: exclude ccache dir

CI: use sudo for commands on GH

CI: exclude scale-build repo hash from cache key calculation
 also takes conf manifest into consideration

CI: set permission for tmp

CI: cache rootfs and iso chroot

output flag on whether rootfs and iso chroot cache is usable to /tmp

add subpkg to build grub-efi-amd64-signed
when we are using customized grub

CI: always upload cache when success build

CI: use unversioned cache

delete old one if we can upload new(update existing cache is not
possible) actions/toolkit#505
wongsyrone added a commit to wongsyrone/wongsyrone-scale-build-ntfs3 that referenced this issue Sep 28, 2023
CI: split pkg build and update w/ iso

ntfs3 patches from paragon group (unupstream)

openseachest: use upstream pull script

install pkgs

single-branch

add artifacts

ignore error for make all step to get log artifacts

china timezone

linux: merge linux 6.1

update grub2 to latest

remove grub2 single job

 * this is leftover of previous bash-based build script

add iso and update chksum files

use latest pip packages

 * pin jsonschema due to schema exception

CI: use git from Ubuntu PPA

git: depth 1

prepend sudo to cmdline

show info after build

CI: 8G root; 10G swap + 7G mem = 17G

update smartmontools to 7.3

CI: reclaim disk space

truenas/zfs@165d115

CI: do not run in the background

CI: setup-python github action uses this dir

CI: only use setup-python for scale-build

do NOT set PYTHON env

CI: add comments and echoes

disable packages with '-dbg' by removing them from manifest

scst: back to official after cleanup dbg pkgs

copy manifest from update dir

use python 3.11

do NOT show kernel config changes

CI: exclude ccache dir

CI: use sudo for commands on GH

CI: exclude scale-build repo hash from cache key calculation
 also takes conf manifest into consideration

CI: set permission for tmp

CI: cache rootfs and iso chroot

output flag on whether rootfs and iso chroot cache is usable to /tmp

add subpkg to build grub-efi-amd64-signed
when we are using customized grub

CI: always upload cache when success build

CI: use unversioned cache

delete old one if we can upload new(update existing cache is not
possible) actions/toolkit#505
wongsyrone added a commit to wongsyrone/wongsyrone-scale-build-ntfs3 that referenced this issue Sep 28, 2023
CI: split pkg build and update w/ iso

ntfs3 patches from paragon group (unupstream)

openseachest: use upstream pull script

install pkgs

single-branch

add artifacts

ignore error for make all step to get log artifacts

china timezone

linux: merge linux 6.1

update grub2 to latest

remove grub2 single job

 * this is leftover of previous bash-based build script

add iso and update chksum files

use latest pip packages

 * pin jsonschema due to schema exception

CI: use git from Ubuntu PPA

git: depth 1

prepend sudo to cmdline

show info after build

CI: 8G root; 10G swap + 7G mem = 17G

update smartmontools to 7.3

CI: reclaim disk space

truenas/zfs@165d115

CI: do not run in the background

CI: setup-python github action uses this dir

CI: only use setup-python for scale-build

do NOT set PYTHON env

CI: add comments and echoes

disable packages with '-dbg' by removing them from manifest

scst: back to official after cleanup dbg pkgs

copy manifest from update dir

use python 3.11

do NOT show kernel config changes

CI: exclude ccache dir

CI: use sudo for commands on GH

CI: exclude scale-build repo hash from cache key calculation
 also takes conf manifest into consideration

CI: set permission for tmp

CI: cache rootfs and iso chroot

output flag on whether rootfs and iso chroot cache is usable to /tmp

add subpkg to build grub-efi-amd64-signed
when we are using customized grub

CI: always upload cache when success build

CI: use unversioned cache

delete old one if we can upload new(update existing cache is not
possible) actions/toolkit#505
@lcswillems
Copy link

lcswillems commented Jun 22, 2024

On Gitlab or a lot of other different CI/CD tools, this is built-in 😭 Such a pain not to have this!

@trallnag
Copy link

I came across this documentation that uses artifacts across workflow runs as a workaround for this: https://github.com/renovatebot/github-action?tab=readme-ov-file#persisting-the-repository-cache

@BillyTom
Copy link

I have tried several workarounds to get actions/cache to update my existing test results correctly. But my conclusion is that it is much easier to use actions/upload-artifact and actions/download-artifact instead.

I have a matrix job that creates test results for a dozen different features. Each job in the matrix uploads its test results with:

  - name: "Upload test results"
    uses: actions/upload-artifact@v4
    with:
      name: test-result-${{ matrix.feature.name }}
      path: |
        frontend/cypress/screenshots
        frontend/cypress/reports

After the matrix job there comes another job that aggregates all test results into one single folder:

  - uses: actions/download-artifact@v4
    with:
      pattern: test-result-*
      merge-multiple: true
      path: frontend/cypress

After that you could just use actions/cache to create a new cache for the folder with the combined test results.

Optional: Since the individual artifacts are now obsolete you can perform a cleanup operation with:

  - name: "Delete obsolete artifacts"
    uses: geekyeggo/delete-artifact@65041433121f7239077fa20be14c0690f70569de
    with:
      name: test-result-*
      useGlob: true

@Lolicchi
Copy link

Lolicchi commented Oct 5, 2024

+8

@hamistao
Copy link

+9. Need this to cache a vulnerability database to improve the performance and reliability of our CI workflow.

@mabar
Copy link

mabar commented Nov 12, 2024

Using less specific key for loading cache than for saving (e.g. example-1 for saving and example- for loading) is the correct solution. Simply adding run ID to the key is enough to keep the cache always up to date.

Cache can currently be restored from the default branch - that allows use of cache for newly created PRs and branches from the start. Updating an existing cache would require removal of that feature because e.g. updating cache in third party PR and using it on master would eventually result in bad consequences.

Problem is that it is not explained in the documentation.

Secondary, cache size is limited and there is no way how to remove less useful and outdated cache that is likely to be never used again, causing more useful cache to be deleted.
I would e.g. like to have an ability to delete branch-specific cache when it is deleted and delete cache, that matches restore-keys but has a newer version.

Also having a button for deleting all the cache would be quite helpful for debugging cache-related issues.

@AlexanderRichert-NOAA
Copy link

Any update on this, including plans to implement? It would be very useful to be able to, say, set overwrite: true for my cache action and update in place as opposed to having to separately delete the cache each time.

PastaPastaPasta added a commit to dashpay/dash that referenced this issue Feb 25, 2025
…ional test logs as artifacts

3461c14 ci: tentatively drop multiprocess and tsan functional tests (Kittywhiskers Van Gogh)
5db8fa0 ci: cache previous releases if running `linux64` variant (Kittywhiskers Van Gogh)
cca0d89 ci: add functional tests for linux builds (Kittywhiskers Van Gogh)
fc2efb6 ci: upload functional test logs as artifacts (Kittywhiskers Van Gogh)
b25e846 ci: handle space exhaustion by deleting files we don't need (Kittywhiskers Van Gogh)
0a1e635 ci: add reusable workflow for running functional tests (Kittywhiskers Van Gogh)
57cf278 ci: use helper script to bundle artifacts (Kittywhiskers Van Gogh)

Pull request description:

  ## Additional Information

  * [`actions/cache`](https://github.com/marketplace/actions/cache) allows specification of directories that want to be cached using glob expressions, which extend further into defining exclusions, needed due keep cache sizes maintainable ([source](https://github.com/dashpay/dash/blob/bb469687d3d936f82fd8e8fbe0934eec5e17df5e/.gitlab-ci.yml#L128-L138)).

    * Unfortunately, the implementation of globbing with respect to exclusions is more-or-less broken (see [actions/toolkit#713](actions/toolkit#713 (comment))) with the requirement that the inclusion depth should match the depth of the exclusion. Attempting to play by these rules more or less fails ([build](https://github.com/kwvg/dash/actions/runs/13344612118/job/37273624710#step:5:4634)).

    * Attempting to use third party actions like [`tj-actions/glob`](https://github.com/marketplace/actions/glob-match) provide for a much more saner experience but they enumerate individual files that match patterns, not folders. This means that when we pass them to `actions/cache`, we breach the arguments length limit ([build](https://github.com/kwvg/dash/actions/runs/13343953711/job/37272121153#step:9:4409)).

      * Modifying `ulimit` to get around this isn't very feasible due to odd behavior surrounding it (see [actions/runner#3421](actions/runner#3421)) and the general consensus is to save to a file and have the next action read from file ([source](https://stackoverflow.com/a/71349472)).

         [`tj-actions/glob`](https://github.com/marketplace/actions/glob-match) graciously does this with the `paths-output-file` output but it takes two to play and [`actions/cache`](https://github.com/marketplace/actions/cache) does not accept files (`path` must be a newline-delimited string).

    The path of least resistance, it seems, is to use a script to bundle our cache into a neat input and leave [`actions/cache`](https://github.com/marketplace/actions/cache) out of it entirely, this is the approach taken.

  * As we aren't using self-hosted runners, we are subject to GitHub's limits for everything, runner space, total artifact storage budget, total cache storage budget.

    * Caches that not **accessed** in 7 days are evicted and there's a 10 GB budget for all caches ([source](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy)) and GitHub will evict oldest (presumably by **creation**?) caches to make sure that limit is adhered to.

      * What makes this limit troubling is the immutable nature of caches as unlike GitLab, which is more conductive to shared caches ([source](https://github.com/dashpay/dash/blob/bb469687d3d936f82fd8e8fbe0934eec5e17df5e/.gitlab-ci.yml#L55-L69)), GitHub insists on its immutability (see [actions/toolkit#505](actions/toolkit#505)) and the only way to "update" a cache is to structure your cache key to allow the updated content to reflect in the key itself or delete the cache and create a new one, which brings race condition concerns ([comment](#6406 (review))).

        Sidenote, overwriting contents are allowed for artifacts ([source](https://github.com/actions/upload-artifact/blob/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08/README.md#overwriting-an-artifact)), just not caches.

      * This means we need to be proactive in getting rid of caches with a short shelf life to avoid more long lasting caches (like `depends-sources`) from being evicted due to old age as we breach the 10 GB limit. We cannot just set a short retention period as GitHub doesn't offer you to do that with caches like they do with artifacts ([source](https://github.com/actions/upload-artifact/blob/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08/README.md#retention-period)).

        * ~~While doing this properly would require us to implement a cache reaper workflow, this still needed to be addressed now as the contents of `build-ci` need to be passed onto the functional test workflow and this creates an ephemeral cache with a short lifespan that threatens longer-living (but older) caches.~~

       ~~This is currently approached by deleting `build-ci` (output) caches when the functional test runner is successful, we let the cache stick around if the build fails to allow for rerunning failed instances.~~

       ~~If for whatever reason a successful build has to be rerun, the build workflow would need to be rerun (though the `ccache` cache will speed this up significantly) to generate the output cache again for the test workflow to succeed. Failing to do this will result in a cache miss and run failure.~~ **Edit:** Switched to using artifacts to mitigate cache thrashing concerns. Auto-expiration is a huge plus, too.

    * Runners are limited to 14 GB of **addressable** storage space ([source](https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)) and breaching this limit will cause the runners to fail.

      * Our TSan ([build](https://github.com/kwvg/dash/actions/runs/13355816205/job/37298587344#step:5:1178)) and multiprocess ([build](https://github.com/kwvg/dash/actions/runs/13355816205/job/37298658464#step:5:1190)) test variants breach this limit when collecting logs and deleting the `build-ci` (see 2153b0b) cache doesn't make enough of a dent to help ([build](https://github.com/kwvg/dash/actions/runs/13356474530)).

        * Omitting the logs from successful runs would be a regression in content for our `test_logs` artifacts and therefore wasn't considered.

      * While third-party actions like [`AdityaGarg8/remove-unwanted-software`](https://github.com/marketplace/actions/maximize-build-disk-space-only-remove-unwanted-software) can bring significant space savings ([build](https://github.com/kwvg/dash/actions/runs/13357806504/job/37302970610#step:2:150)), they cannot be run in jobs that utilize the [`container`](https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs/running-jobs-in-a-container) context (so, all jobs after the container creation workflow) as they'd be executed inside the container when we want to affect the runner underneath ([build](https://github.com/kwvg/dash/actions/runs/13357260369/job/37301757225#step:3:29), notice the step being run after "Initialize containers").
        * There are no plans to implement definable "pre-steps" (see [actions/runner#812](actions/runner#812)) and the only way to implement "before" and "after" steps is by self-hosting ([source](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job)).

       This has been sidestepped tentatively by omitting TSan and multiprocess builds as any attempted fixes would require precision gutting to get borderline space savings which could easily be nullified by future code changes that occupy more space and such measures are better reserved for future PRs.

    * Artifacts share their storage quota with GitHub Packages ([source](https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-packages/about-billing-for-github-packages#about-billing-for-github-packages)) and artifacts by default linger around for 90 days ([source](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow#about-workflow-artifacts)) and considering that each run can generate multi-gigabyte total artifacts, testing the upper limit runs the risk of being very expensive. **Edit:** It appears pricing is reflective of artifacts in private repos, public repos don't seem to run this risk.

      * ~~All artifacts generated have an expiry of one day (compared to GitLab's three days, [source](https://github.com/dashpay/dash/blob/bb469687d3d936f82fd8e8fbe0934eec5e17df5e/.gitlab-ci.yml#L165), but they are self-hosted).~~ **Edit:** Artifacts now have an expiry of three days, matching GitLab.

      * Artifacts are compressed as ZIP archives and there is no way around that as of now (see [actions/upload-artifact#109](actions/upload-artifact#109 (comment))) and the permissions loss it entails is acknowledged by GitHub and their solution is... to put them in a tarball ([source](https://github.com/actions/upload-artifact/blob/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08/README.md#permission-loss)).

      To keep size under control, artifacts contain `zstd-5` compressed tarballs (compression level determined by benchmarks, see below) generated by bundling scripts alongside their checksum.

      <details>

      <summary>Benchmarks:</summary>

      ```
      $ zstd -b1 -e22 -T0 artifacts-linux64_multiprocess.tar
      1#_multiprocess.tar :1586411520 -> 537552492 (x2.951), 2396.2 MB/s, 1367.8 MB/s
      2#_multiprocess.tar :1586411520 -> 499098623 (x3.179), 2131.8 MB/s, 1306.6 MB/s
      3#_multiprocess.tar :1586411520 -> 474452284 (x3.344), 1371.6 MB/s, 1245.6 MB/s
      4#_multiprocess.tar :1586411520 -> 470931621 (x3.369),  620.3 MB/s, 1239.1 MB/s
      5#_multiprocess.tar :1586411520 -> 459075785 (x3.456),  457.2 MB/s, 1230.1 MB/s
      6#_multiprocess.tar :1586411520 -> 449594612 (x3.529),  415.3 MB/s, 1289.7 MB/s
      7#_multiprocess.tar :1586411520 -> 446208421 (x3.555),  282.6 MB/s, 1296.3 MB/s
      8#_multiprocess.tar :1586411520 -> 442797797 (x3.583),  254.3 MB/s, 1338.4 MB/s
      9#_multiprocess.tar :1586411520 -> 438690318 (x3.616),  210.8 MB/s, 1331.5 MB/s
      10#_multiprocess.tar :1586411520 -> 437195147 (x3.629),  164.1 MB/s, 1337.4 MB/s
      11#_multiprocess.tar :1586411520 -> 436501141 (x3.634),  108.2 MB/s, 1342.5 MB/s
      12#_multiprocess.tar :1586411520 -> 436405679 (x3.635),  102.7 MB/s, 1344.0 MB/s
      13#_multiprocess.tar :1586411520 -> 436340981 (x3.636),   65.9 MB/s, 1344.0 MB/s
      14#_multiprocess.tar :1586411520 -> 435626720 (x3.642),   61.5 MB/s, 1346.9 MB/s
      15#_multiprocess.tar :1586411520 -> 434882716 (x3.648),   49.4 MB/s, 1352.9 MB/s
      16#_multiprocess.tar :1586411520 -> 411221852 (x3.858),   33.6 MB/s, 1049.2 MB/s
      17#_multiprocess.tar :1586411520 -> 399523001 (x3.971),   26.0 MB/s, 1003.7 MB/s
      18#_multiprocess.tar :1586411520 -> 379278765 (x4.183),   21.0 MB/s,  897.5 MB/s
      19#_multiprocess.tar :1586411520 -> 378022246 (x4.197),   14.7 MB/s,  896.0 MB/s
      20#_multiprocess.tar :1586411520 -> 375741653 (x4.222),   14.0 MB/s,  877.6 MB/s
      21#_multiprocess.tar :1586411520 -> 373303486 (x4.250),   11.9 MB/s,  866.8 MB/s
      22#_multiprocess.tar :1586411520 -> 358172556 (x4.429),   6.09 MB/s,  884.9 MB/s
      ```

      </details>

        > **Note:** As mentioned above, we use similar bundling scripts for the outputs cache but unlike artifacts, we cannot disable their compression routines or even adjust compression levels (see [actions/tookit#544](actions/toolkit#544))

  ## Notes

  * ~~If we add or remove binaries in terms of compile output, `bundle-build.sh` needs to be updated. Without updating it, it will fail if it cannot find the files it was looking for and it will not include files that it wasn't told to include.~~ No longer applicable.

  ## Breaking Changes

  None expected.

  ## Checklist

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)**
  - [x] I have made corresponding changes to the documentation **(note: N/A)**
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    utACK 3461c14
  UdjinM6:
    ACK 3461c14

Tree-SHA512: ef55bc10902c57673ffd9bee6562b362a87658e4c51e543b8553bf48c41544a302d6acad7c5a30395fbfcfd085354251a07327c3e78c93c750585496926be9f6
priyadi added a commit to priyadi/priyadi.id that referenced this issue Feb 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cache enhancement New feature or request
Projects
None yet
Development

No branches or pull requests