Skip to content

Commit eca05b0

Browse files
lafrikslunny
authored andcommitted
Add commit count caching (#2774)
* Add commit count caching * Small refactoring * Add different key prefix for refs and commits * Add configuratuion option to allow to change caching time or disable it
1 parent 3ab580c commit eca05b0

File tree

10 files changed

+150
-25
lines changed

10 files changed

+150
-25
lines changed

conf/app.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ INTERVAL = 60
339339
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
340340
; memcache: `127.0.0.1:11211`
341341
HOST =
342+
; Time to keep items in cache if not used, default is 16 hours.
343+
; Setting it to 0 disables caching
344+
ITEM_TTL = 16h
342345

343346
[session]
344347
; Either "memory", "file", or "redis", default is "memory"

models/repo.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,17 @@ func (repo *Repository) APIFormat(mode AccessMode) *api.Repository {
258258
return repo.innerAPIFormat(mode, false)
259259
}
260260

261+
// GetCommitsCountCacheKey returns cache key used for commits count caching.
262+
func (repo *Repository) GetCommitsCountCacheKey(contextName string, isRef bool) string {
263+
var prefix string
264+
if isRef {
265+
prefix = "ref"
266+
} else {
267+
prefix = "commit"
268+
}
269+
return fmt.Sprintf("commits-count-%d-%s-%s", repo.ID, prefix, contextName)
270+
}
271+
261272
func (repo *Repository) innerAPIFormat(mode AccessMode, isParent bool) *api.Repository {
262273
var parent *api.Repository
263274

models/update.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"strings"
1212

1313
"code.gitea.io/git"
14-
14+
"code.gitea.io/gitea/modules/cache"
1515
"code.gitea.io/gitea/modules/log"
1616
)
1717

@@ -205,19 +205,26 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) {
205205
var commits = &PushCommits{}
206206
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
207207
// If is tag reference
208+
tagName := opts.RefFullName[len(git.TagPrefix):]
208209
if isDelRef {
209-
err = pushUpdateDeleteTag(repo, gitRepo, opts.RefFullName[len(git.TagPrefix):])
210+
err = pushUpdateDeleteTag(repo, gitRepo, tagName)
210211
if err != nil {
211212
return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err)
212213
}
213214
} else {
214-
err = pushUpdateAddTag(repo, gitRepo, opts.RefFullName[len(git.TagPrefix):])
215+
// Clear cache for tag commit count
216+
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
217+
err = pushUpdateAddTag(repo, gitRepo, tagName)
215218
if err != nil {
216219
return nil, fmt.Errorf("pushUpdateAddTag: %v", err)
217220
}
218221
}
219222
} else if !isDelRef {
220223
// If is branch reference
224+
225+
// Clear cache for branch commit count
226+
cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
227+
221228
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
222229
if err != nil {
223230
return nil, fmt.Errorf("gitRepo.GetCommit: %v", err)

modules/cache/cache.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cache
6+
7+
import (
8+
"code.gitea.io/gitea/modules/setting"
9+
10+
mc "github.com/go-macaron/cache"
11+
)
12+
13+
var conn mc.Cache
14+
15+
// NewContext start cache service
16+
func NewContext() error {
17+
if setting.CacheService == nil || conn != nil {
18+
return nil
19+
}
20+
21+
var err error
22+
conn, err = mc.NewCacher(setting.CacheService.Adapter, mc.Options{
23+
Adapter: setting.CacheService.Adapter,
24+
AdapterConfig: setting.CacheService.Conn,
25+
Interval: setting.CacheService.Interval,
26+
})
27+
return err
28+
}
29+
30+
// GetInt returns key value from cache with callback when no key exists in cache
31+
func GetInt(key string, getFunc func() (int, error)) (int, error) {
32+
if conn == nil || setting.CacheService.TTL == 0 {
33+
return getFunc()
34+
}
35+
if !conn.IsExist(key) {
36+
var (
37+
value int
38+
err error
39+
)
40+
if value, err = getFunc(); err != nil {
41+
return value, err
42+
}
43+
conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
44+
}
45+
return conn.Get(key).(int), nil
46+
}
47+
48+
// GetInt64 returns key value from cache with callback when no key exists in cache
49+
func GetInt64(key string, getFunc func() (int64, error)) (int64, error) {
50+
if conn == nil || setting.CacheService.TTL == 0 {
51+
return getFunc()
52+
}
53+
if !conn.IsExist(key) {
54+
var (
55+
value int64
56+
err error
57+
)
58+
if value, err = getFunc(); err != nil {
59+
return value, err
60+
}
61+
conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
62+
}
63+
return conn.Get(key).(int64), nil
64+
}
65+
66+
// Remove key from cache
67+
func Remove(key string) {
68+
if conn == nil {
69+
return
70+
}
71+
conn.Delete(key)
72+
}

modules/context/repo.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import (
1313

1414
"code.gitea.io/git"
1515
"code.gitea.io/gitea/models"
16+
"code.gitea.io/gitea/modules/cache"
1617
"code.gitea.io/gitea/modules/setting"
18+
1719
"github.com/Unknwon/com"
1820
"gopkg.in/editorconfig/editorconfig-core-go.v1"
1921
"gopkg.in/macaron.v1"
@@ -100,6 +102,21 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
100102
r.IsWriter() || issue.IsPoster(user.ID) || issue.AssigneeID == user.ID)
101103
}
102104

105+
// GetCommitsCount returns cached commit count for current view
106+
func (r *Repository) GetCommitsCount() (int64, error) {
107+
var contextName string
108+
if r.IsViewBranch {
109+
contextName = r.BranchName
110+
} else if r.IsViewTag {
111+
contextName = r.TagName
112+
} else {
113+
contextName = r.CommitID
114+
}
115+
return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, r.IsViewBranch || r.IsViewTag), func() (int64, error) {
116+
return r.Commit.CommitsCount()
117+
})
118+
}
119+
103120
// GetEditorconfig returns the .editorconfig definition if found in the
104121
// HEAD of the default repo branch.
105122
func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) {
@@ -535,9 +552,9 @@ func RepoRef() macaron.Handler {
535552
ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
536553
ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch()
537554

538-
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
555+
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
539556
if err != nil {
540-
ctx.Handle(500, "CommitsCount", err)
557+
ctx.Handle(500, "GetCommitsCount", err)
541558
return
542559
}
543560
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount

modules/setting/setting.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,6 @@ var (
325325
// Time settings
326326
TimeFormat string
327327

328-
// Cache settings
329-
CacheAdapter string
330-
CacheInterval int
331-
CacheConn string
332-
333328
// Session settings
334329
SessionConfig session.Options
335330
CSRFCookieName = "_csrf"
@@ -1295,16 +1290,33 @@ func NewXORMLogService(disableConsole bool) {
12951290
}
12961291
}
12971292

1293+
// Cache represents cache settings
1294+
type Cache struct {
1295+
Adapter string
1296+
Interval int
1297+
Conn string
1298+
TTL time.Duration
1299+
}
1300+
1301+
var (
1302+
// CacheService the global cache
1303+
CacheService *Cache
1304+
)
1305+
12981306
func newCacheService() {
1299-
CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
1300-
switch CacheAdapter {
1307+
sec := Cfg.Section("cache")
1308+
CacheService = &Cache{
1309+
Adapter: sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"}),
1310+
}
1311+
switch CacheService.Adapter {
13011312
case "memory":
1302-
CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
1313+
CacheService.Interval = sec.Key("INTERVAL").MustInt(60)
13031314
case "redis", "memcache":
1304-
CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
1315+
CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ")
13051316
default:
1306-
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
1317+
log.Fatal(4, "Unknown cache adapter: %s", CacheService.Adapter)
13071318
}
1319+
CacheService.TTL = sec.Key("ITEM_TTL").MustDuration(16 * time.Hour)
13081320

13091321
log.Info("Cache Service Enabled")
13101322
}

routers/admin/admin.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,9 @@ func Config(ctx *context.Context) {
224224
ctx.Data["Mailer"] = setting.MailService
225225
}
226226

227-
ctx.Data["CacheAdapter"] = setting.CacheAdapter
228-
ctx.Data["CacheInterval"] = setting.CacheInterval
229-
ctx.Data["CacheConn"] = setting.CacheConn
227+
ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
228+
ctx.Data["CacheInterval"] = setting.CacheService.Interval
229+
ctx.Data["CacheConn"] = setting.CacheService.Conn
230230

231231
ctx.Data["SessionConfig"] = setting.SessionConfig
232232

routers/init.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import (
1111
"code.gitea.io/git"
1212
"code.gitea.io/gitea/models"
1313
"code.gitea.io/gitea/models/migrations"
14+
"code.gitea.io/gitea/modules/cache"
1415
"code.gitea.io/gitea/modules/cron"
1516
"code.gitea.io/gitea/modules/highlight"
1617
"code.gitea.io/gitea/modules/log"
1718
"code.gitea.io/gitea/modules/mailer"
1819
"code.gitea.io/gitea/modules/markup"
1920
"code.gitea.io/gitea/modules/setting"
2021
"code.gitea.io/gitea/modules/ssh"
22+
2123
macaron "gopkg.in/macaron.v1"
2224
)
2325

@@ -37,6 +39,7 @@ func checkRunMode() {
3739
func NewServices() {
3840
setting.NewServices()
3941
mailer.NewContext()
42+
cache.NewContext()
4043
}
4144

4245
// GlobalInit is for global configuration reload-able.

routers/repo/commit.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func Commits(ctx *context.Context) {
5555
}
5656
ctx.Data["PageIsViewCode"] = true
5757

58-
commitsCount, err := ctx.Repo.Commit.CommitsCount()
58+
commitsCount, err := ctx.Repo.GetCommitsCount()
5959
if err != nil {
6060
ctx.Handle(500, "GetCommitsCount", err)
6161
return
@@ -91,7 +91,7 @@ func Graph(ctx *context.Context) {
9191
ctx.Data["PageIsCommits"] = true
9292
ctx.Data["PageIsViewCode"] = true
9393

94-
commitsCount, err := ctx.Repo.Commit.CommitsCount()
94+
commitsCount, err := ctx.Repo.GetCommitsCount()
9595
if err != nil {
9696
ctx.Handle(500, "GetCommitsCount", err)
9797
return

routers/routes/routes.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ func NewMacaron() *macaron.Macaron {
9999
Redirect: true,
100100
}))
101101
m.Use(cache.Cacher(cache.Options{
102-
Adapter: setting.CacheAdapter,
103-
AdapterConfig: setting.CacheConn,
104-
Interval: setting.CacheInterval,
102+
Adapter: setting.CacheService.Adapter,
103+
AdapterConfig: setting.CacheService.Conn,
104+
Interval: setting.CacheService.Interval,
105105
}))
106106
m.Use(captcha.Captchaer(captcha.Options{
107107
SubURL: setting.AppSubURL,
@@ -576,9 +576,9 @@ func RegisterRoutes(m *macaron.Macaron) {
576576
ctx.Handle(500, "GetBranchCommit", err)
577577
return
578578
}
579-
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
579+
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
580580
if err != nil {
581-
ctx.Handle(500, "CommitsCount", err)
581+
ctx.Handle(500, "GetCommitsCount", err)
582582
return
583583
}
584584
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount

0 commit comments

Comments
 (0)