From 022d5ad3e7da85e436da5097555739f1f73a4024 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Tue, 19 Aug 2025 20:19:23 +0000 Subject: [PATCH] fix: artifact cache DB not using indexes for searching (#878) Uses the `Repo` field as an index during searches of the cache database. Removes unused indexes. To measure the performance of this change, I created a synthetic test which wrote 10,000 records into the artifact cache DB. Of course, all benchmarks are lies that can't be generalized to real-world usage, but it seems clear from the magnitude of improvement that this fixes a flawed implementation, even if it's not perfect. - Unmodified performance: - Write: 196 records/second - Read: 1 record/second - With `Repo` index being used for reads, and other indexes being removed: - Write: 347 records/second - Read: 22,398 records/second `Repo` is, I think, the only index that made sense to remain, with an eye on workflow run performance: - `Key` -- can't be used for index because `findCache` searches for key *prefixes*, not equal values. - `Version` -- isn't very distinct for different workflow runs (https://code.forgejo.org/actions/cache#cache-version) - `Complete` - significant portion of the cache DB will be complete, making it the least selective possible index - `UsedAt` & `CreatedAt` - only used in GC operation, so could remain, but this isn't a performance-sensitive codepath Closes #874. - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/878): fix: artifact cache DB not using indexes for searching Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/878 Reviewed-by: Michael Kriese Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- act/artifactcache/handler.go | 4 ++-- act/artifactcache/model.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/act/artifactcache/handler.go b/act/artifactcache/handler.go index b50b6b11..0f78be85 100644 --- a/act/artifactcache/handler.go +++ b/act/artifactcache/handler.go @@ -422,7 +422,7 @@ func findCache(db *bolthold.Store, repo string, keys []string, version string) ( for _, prefix := range keys { // if a key in the list matches exactly, don't return partial matches if err := db.FindOne(cache, - bolthold.Where("Repo").Eq(repo). + bolthold.Where("Repo").Eq(repo).Index("Repo"). And("Key").Eq(prefix). And("Version").Eq(version). And("Complete").Eq(true). @@ -438,7 +438,7 @@ func findCache(db *bolthold.Store, repo string, keys []string, version string) ( continue } if err := db.FindOne(cache, - bolthold.Where("Repo").Eq(repo). + bolthold.Where("Repo").Eq(repo).Index("Repo"). And("Key").RegExp(re). And("Version").Eq(version). And("Complete").Eq(true). diff --git a/act/artifactcache/model.go b/act/artifactcache/model.go index 1c0f855d..b27fd8ed 100644 --- a/act/artifactcache/model.go +++ b/act/artifactcache/model.go @@ -26,10 +26,10 @@ func (c *Request) ToCache() *Cache { type Cache struct { ID uint64 `json:"id" boltholdKey:"ID"` Repo string `json:"repo" boltholdIndex:"Repo"` - Key string `json:"key" boltholdIndex:"Key"` - Version string `json:"version" boltholdIndex:"Version"` + Key string `json:"key"` + Version string `json:"version"` Size int64 `json:"cacheSize"` - Complete bool `json:"complete" boltholdIndex:"Complete"` - UsedAt int64 `json:"usedAt" boltholdIndex:"UsedAt"` - CreatedAt int64 `json:"createdAt" boltholdIndex:"CreatedAt"` + Complete bool `json:"complete"` + UsedAt int64 `json:"usedAt"` + CreatedAt int64 `json:"createdAt"` }