Merge pull request '[ENHANCEMENT] Improve caching of contributor stats' (#4367) from gusted/contributors-stats-cache into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4367 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
bf0c9837cc
3 changed files with 18 additions and 17 deletions
1
release-notes/9.0.0/4367.md
Normal file
1
release-notes/9.0.0/4367.md
Normal file
|
@ -0,0 +1 @@
|
|||
The caching of contributor stats was improved (the data used by `/<user>/<repo>/activity/recent-commits`) to use the configured cache TTL from the config (`[cache].ITEM_TTL`) instead of a hardcoded TTL of ten minutes. The computation of this operation is computationally heavy and makes a lot of requests to the database and Git on repositories with a lot of commits. It should be cached for longer than what was previously hardcoded, ten minutes.
|
|
@ -22,15 +22,13 @@ import (
|
|||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"gitea.com/go-chi/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
contributorStatsCacheKey = "GetContributorStats/%s/%s"
|
||||
contributorStatsCacheTimeout int64 = 60 * 10
|
||||
)
|
||||
const contributorStatsCacheKey = "GetContributorStats/%s/%s"
|
||||
|
||||
var (
|
||||
ErrAwaitGeneration = errors.New("generation took longer than ")
|
||||
|
@ -211,8 +209,7 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
|
|||
|
||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("OpenRepository: %w", err)
|
||||
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
|
||||
log.Error("OpenRepository[repo=%q]: %v", repo.FullName(), err)
|
||||
return
|
||||
}
|
||||
defer closer.Close()
|
||||
|
@ -222,13 +219,11 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
|
|||
}
|
||||
extendedCommitStats, err := getExtendedCommitStats(gitRepo, revision)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("ExtendedCommitStats: %w", err)
|
||||
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
|
||||
log.Error("getExtendedCommitStats[repo=%q revision=%q]: %v", repo.FullName(), revision, err)
|
||||
return
|
||||
}
|
||||
if len(extendedCommitStats) == 0 {
|
||||
err := fmt.Errorf("no commit stats returned for revision '%s'", revision)
|
||||
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
|
||||
log.Error("No commit stats were returned [repo=%q revision=%q]", repo.FullName(), revision)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -312,14 +307,13 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey
|
|||
|
||||
data, err := json.Marshal(contributorsCommitStats)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("couldn't marshal the data: %w", err)
|
||||
_ = cache.Put(cacheKey, err, contributorStatsCacheTimeout)
|
||||
log.Error("json.Marshal[repo=%q revision=%q]: %v", repo.FullName(), revision, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store the data as an string, to make it uniform what data type is returned
|
||||
// from caches.
|
||||
_ = cache.Put(cacheKey, string(data), contributorStatsCacheTimeout)
|
||||
_ = cache.Put(cacheKey, string(data), setting.CacheService.TTLSeconds())
|
||||
generateLock.Delete(cacheKey)
|
||||
if genDone != nil {
|
||||
genDone <- struct{}{}
|
||||
|
|
|
@ -6,12 +6,14 @@ package repository
|
|||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"gitea.com/go-chi/cache"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -27,10 +29,14 @@ func TestRepository_ContributorsGraph(t *testing.T) {
|
|||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
lc, cleanup := test.NewLogChecker(log.DEFAULT, log.INFO)
|
||||
lc.StopMark(`getExtendedCommitStats[repo="user2/repo2" revision="404ref"]: object does not exist [id: 404ref, rel_path: ]`)
|
||||
defer cleanup()
|
||||
|
||||
generateContributorStats(nil, mockCache, "key", repo, "404ref")
|
||||
err, isErr := mockCache.Get("key").(error)
|
||||
assert.True(t, isErr)
|
||||
assert.ErrorAs(t, err, &git.ErrNotExist{})
|
||||
assert.False(t, mockCache.IsExist("key"))
|
||||
_, stopped := lc.Check(100 * time.Millisecond)
|
||||
assert.True(t, stopped)
|
||||
|
||||
generateContributorStats(nil, mockCache, "key2", repo, "master")
|
||||
dataString, isData := mockCache.Get("key2").(string)
|
||||
|
|
Loading…
Reference in a new issue