feat: add size-based cache eviction (#1170)

The cache server retired entries 30 days after creation regardless of use, so a job that ran often enough to keep its cache warm still lost it on a fixed schedule. Nothing bounded the disk either.

Retention now counts from last access alone, and a repository over its limit sheds least recently accessed entries until it fits, enforced on commit as well as on the periodic sweep.

```yaml
cache:
  retention: 168h        # remove entries not accessed for seven days
  repo_size_limit: 10GB  # cap each repository
  size_limit: 0          # cap the whole cache, off by default
  sweep_interval: 1h     # minimum time between sweeps
```

Sizes accept `10GB`, `512mb`, `1TiB` or a plain byte count, binary either way. Leave a key out for its default; `0` turns a limit off, and `0s` does the same for `retention`. Whatever these allow, the cache also sheds entries to keep free space above `health_check.min_free_disk_space_mb` when health checks are enabled, so it cannot grow past the point where the runner stops accepting work.

Supporting fixes: serving an entry stamps its access time, so a find cannot hand a job a download URL for an entry the next eviction is about to remove; an entry larger than the limit is dropped on its own account rather than emptying its repository to make room; and a blob that cannot be unlinked keeps its row, so the next sweep retries instead of orphaning bytes no limit can account for.

Closes https://gitea.com/gitea/runner/issues/1168

---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1170
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
This commit is contained in:
bircni
2026-08-18 15:32:53 +00:00
parent 6c6a878403
commit be90c01468
21 changed files with 750 additions and 170 deletions
+9 -7
View File
@@ -10,6 +10,7 @@ import (
"os/signal"
"gitea.com/gitea/runner/act/artifactcache"
"gitea.com/gitea/runner/internal/app/run"
"gitea.com/gitea/runner/internal/pkg/config"
log "github.com/sirupsen/logrus"
@@ -52,13 +53,14 @@ func runCacheServer(configFile *string, cacheArgs *cacheServerArgs) func(cmd *co
if secret == "" {
return errors.New("cache.external_secret (or cache.external_secret_file) must be set for cache-server; configure the same value on each runner that points at this server via cache.external_server")
}
cacheHandler, err := artifactcache.StartHandler(
dir,
host,
port,
secret,
log.StandardLogger().WithField("module", "cache_request"),
)
cacheHandler, err := artifactcache.StartHandler(artifactcache.Options{
Dir: dir,
OutboundIP: host,
Port: port,
InternalSecret: secret,
Policy: run.CachePolicy(cfg),
Logger: log.StandardLogger().WithField("module", "cache_request"),
})
if err != nil {
return err
}