* 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 ittags/v1.21.12.1
| @@ -339,6 +339,9 @@ INTERVAL = 60 | |||||
| ; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 | ; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 | ||||
| ; memcache: `127.0.0.1:11211` | ; memcache: `127.0.0.1:11211` | ||||
| HOST = | HOST = | ||||
| ; Time to keep items in cache if not used, default is 16 hours. | |||||
| ; Setting it to 0 disables caching | |||||
| ITEM_TTL = 16h | |||||
| [session] | [session] | ||||
| ; Either "memory", "file", or "redis", default is "memory" | ; Either "memory", "file", or "redis", default is "memory" | ||||
| @@ -258,6 +258,17 @@ func (repo *Repository) APIFormat(mode AccessMode) *api.Repository { | |||||
| return repo.innerAPIFormat(mode, false) | return repo.innerAPIFormat(mode, false) | ||||
| } | } | ||||
| // GetCommitsCountCacheKey returns cache key used for commits count caching. | |||||
| func (repo *Repository) GetCommitsCountCacheKey(contextName string, isRef bool) string { | |||||
| var prefix string | |||||
| if isRef { | |||||
| prefix = "ref" | |||||
| } else { | |||||
| prefix = "commit" | |||||
| } | |||||
| return fmt.Sprintf("commits-count-%d-%s-%s", repo.ID, prefix, contextName) | |||||
| } | |||||
| func (repo *Repository) innerAPIFormat(mode AccessMode, isParent bool) *api.Repository { | func (repo *Repository) innerAPIFormat(mode AccessMode, isParent bool) *api.Repository { | ||||
| var parent *api.Repository | var parent *api.Repository | ||||
| @@ -11,7 +11,7 @@ import ( | |||||
| "strings" | "strings" | ||||
| "code.gitea.io/git" | "code.gitea.io/git" | ||||
| "code.gitea.io/gitea/modules/cache" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| ) | ) | ||||
| @@ -205,19 +205,26 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { | |||||
| var commits = &PushCommits{} | var commits = &PushCommits{} | ||||
| if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | ||||
| // If is tag reference | // If is tag reference | ||||
| tagName := opts.RefFullName[len(git.TagPrefix):] | |||||
| if isDelRef { | if isDelRef { | ||||
| err = pushUpdateDeleteTag(repo, gitRepo, opts.RefFullName[len(git.TagPrefix):]) | |||||
| err = pushUpdateDeleteTag(repo, gitRepo, tagName) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err) | return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err) | ||||
| } | } | ||||
| } else { | } else { | ||||
| err = pushUpdateAddTag(repo, gitRepo, opts.RefFullName[len(git.TagPrefix):]) | |||||
| // Clear cache for tag commit count | |||||
| cache.Remove(repo.GetCommitsCountCacheKey(tagName, true)) | |||||
| err = pushUpdateAddTag(repo, gitRepo, tagName) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("pushUpdateAddTag: %v", err) | return nil, fmt.Errorf("pushUpdateAddTag: %v", err) | ||||
| } | } | ||||
| } | } | ||||
| } else if !isDelRef { | } else if !isDelRef { | ||||
| // If is branch reference | // If is branch reference | ||||
| // Clear cache for branch commit count | |||||
| cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true)) | |||||
| newCommit, err := gitRepo.GetCommit(opts.NewCommitID) | newCommit, err := gitRepo.GetCommit(opts.NewCommitID) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("gitRepo.GetCommit: %v", err) | return nil, fmt.Errorf("gitRepo.GetCommit: %v", err) | ||||
| @@ -0,0 +1,72 @@ | |||||
| // Copyright 2017 The Gitea Authors. All rights reserved. | |||||
| // Use of this source code is governed by a MIT-style | |||||
| // license that can be found in the LICENSE file. | |||||
| package cache | |||||
| import ( | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| mc "github.com/go-macaron/cache" | |||||
| ) | |||||
| var conn mc.Cache | |||||
| // NewContext start cache service | |||||
| func NewContext() error { | |||||
| if setting.CacheService == nil || conn != nil { | |||||
| return nil | |||||
| } | |||||
| var err error | |||||
| conn, err = mc.NewCacher(setting.CacheService.Adapter, mc.Options{ | |||||
| Adapter: setting.CacheService.Adapter, | |||||
| AdapterConfig: setting.CacheService.Conn, | |||||
| Interval: setting.CacheService.Interval, | |||||
| }) | |||||
| return err | |||||
| } | |||||
| // GetInt returns key value from cache with callback when no key exists in cache | |||||
| func GetInt(key string, getFunc func() (int, error)) (int, error) { | |||||
| if conn == nil || setting.CacheService.TTL == 0 { | |||||
| return getFunc() | |||||
| } | |||||
| if !conn.IsExist(key) { | |||||
| var ( | |||||
| value int | |||||
| err error | |||||
| ) | |||||
| if value, err = getFunc(); err != nil { | |||||
| return value, err | |||||
| } | |||||
| conn.Put(key, value, int64(setting.CacheService.TTL.Seconds())) | |||||
| } | |||||
| return conn.Get(key).(int), nil | |||||
| } | |||||
| // GetInt64 returns key value from cache with callback when no key exists in cache | |||||
| func GetInt64(key string, getFunc func() (int64, error)) (int64, error) { | |||||
| if conn == nil || setting.CacheService.TTL == 0 { | |||||
| return getFunc() | |||||
| } | |||||
| if !conn.IsExist(key) { | |||||
| var ( | |||||
| value int64 | |||||
| err error | |||||
| ) | |||||
| if value, err = getFunc(); err != nil { | |||||
| return value, err | |||||
| } | |||||
| conn.Put(key, value, int64(setting.CacheService.TTL.Seconds())) | |||||
| } | |||||
| return conn.Get(key).(int64), nil | |||||
| } | |||||
| // Remove key from cache | |||||
| func Remove(key string) { | |||||
| if conn == nil { | |||||
| return | |||||
| } | |||||
| conn.Delete(key) | |||||
| } | |||||
| @@ -13,7 +13,9 @@ import ( | |||||
| "code.gitea.io/git" | "code.gitea.io/git" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/cache" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
| "gopkg.in/editorconfig/editorconfig-core-go.v1" | "gopkg.in/editorconfig/editorconfig-core-go.v1" | ||||
| "gopkg.in/macaron.v1" | "gopkg.in/macaron.v1" | ||||
| @@ -100,6 +102,21 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b | |||||
| r.IsWriter() || issue.IsPoster(user.ID) || issue.AssigneeID == user.ID) | r.IsWriter() || issue.IsPoster(user.ID) || issue.AssigneeID == user.ID) | ||||
| } | } | ||||
| // GetCommitsCount returns cached commit count for current view | |||||
| func (r *Repository) GetCommitsCount() (int64, error) { | |||||
| var contextName string | |||||
| if r.IsViewBranch { | |||||
| contextName = r.BranchName | |||||
| } else if r.IsViewTag { | |||||
| contextName = r.TagName | |||||
| } else { | |||||
| contextName = r.CommitID | |||||
| } | |||||
| return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, r.IsViewBranch || r.IsViewTag), func() (int64, error) { | |||||
| return r.Commit.CommitsCount() | |||||
| }) | |||||
| } | |||||
| // GetEditorconfig returns the .editorconfig definition if found in the | // GetEditorconfig returns the .editorconfig definition if found in the | ||||
| // HEAD of the default repo branch. | // HEAD of the default repo branch. | ||||
| func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { | func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { | ||||
| @@ -535,9 +552,9 @@ func RepoRef() macaron.Handler { | |||||
| ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit | ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit | ||||
| ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() | ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() | ||||
| ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | |||||
| ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "CommitsCount", err) | |||||
| ctx.Handle(500, "GetCommitsCount", err) | |||||
| return | return | ||||
| } | } | ||||
| ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | ||||
| @@ -325,11 +325,6 @@ var ( | |||||
| // Time settings | // Time settings | ||||
| TimeFormat string | TimeFormat string | ||||
| // Cache settings | |||||
| CacheAdapter string | |||||
| CacheInterval int | |||||
| CacheConn string | |||||
| // Session settings | // Session settings | ||||
| SessionConfig session.Options | SessionConfig session.Options | ||||
| CSRFCookieName = "_csrf" | CSRFCookieName = "_csrf" | ||||
| @@ -1295,16 +1290,33 @@ func NewXORMLogService(disableConsole bool) { | |||||
| } | } | ||||
| } | } | ||||
| // Cache represents cache settings | |||||
| type Cache struct { | |||||
| Adapter string | |||||
| Interval int | |||||
| Conn string | |||||
| TTL time.Duration | |||||
| } | |||||
| var ( | |||||
| // CacheService the global cache | |||||
| CacheService *Cache | |||||
| ) | |||||
| func newCacheService() { | func newCacheService() { | ||||
| CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"}) | |||||
| switch CacheAdapter { | |||||
| sec := Cfg.Section("cache") | |||||
| CacheService = &Cache{ | |||||
| Adapter: sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"}), | |||||
| } | |||||
| switch CacheService.Adapter { | |||||
| case "memory": | case "memory": | ||||
| CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60) | |||||
| CacheService.Interval = sec.Key("INTERVAL").MustInt(60) | |||||
| case "redis", "memcache": | case "redis", "memcache": | ||||
| CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ") | |||||
| CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ") | |||||
| default: | default: | ||||
| log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter) | |||||
| log.Fatal(4, "Unknown cache adapter: %s", CacheService.Adapter) | |||||
| } | } | ||||
| CacheService.TTL = sec.Key("ITEM_TTL").MustDuration(16 * time.Hour) | |||||
| log.Info("Cache Service Enabled") | log.Info("Cache Service Enabled") | ||||
| } | } | ||||
| @@ -224,9 +224,9 @@ func Config(ctx *context.Context) { | |||||
| ctx.Data["Mailer"] = setting.MailService | ctx.Data["Mailer"] = setting.MailService | ||||
| } | } | ||||
| ctx.Data["CacheAdapter"] = setting.CacheAdapter | |||||
| ctx.Data["CacheInterval"] = setting.CacheInterval | |||||
| ctx.Data["CacheConn"] = setting.CacheConn | |||||
| ctx.Data["CacheAdapter"] = setting.CacheService.Adapter | |||||
| ctx.Data["CacheInterval"] = setting.CacheService.Interval | |||||
| ctx.Data["CacheConn"] = setting.CacheService.Conn | |||||
| ctx.Data["SessionConfig"] = setting.SessionConfig | ctx.Data["SessionConfig"] = setting.SessionConfig | ||||
| @@ -11,6 +11,7 @@ import ( | |||||
| "code.gitea.io/git" | "code.gitea.io/git" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/models/migrations" | "code.gitea.io/gitea/models/migrations" | ||||
| "code.gitea.io/gitea/modules/cache" | |||||
| "code.gitea.io/gitea/modules/cron" | "code.gitea.io/gitea/modules/cron" | ||||
| "code.gitea.io/gitea/modules/highlight" | "code.gitea.io/gitea/modules/highlight" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| @@ -18,6 +19,7 @@ import ( | |||||
| "code.gitea.io/gitea/modules/markup" | "code.gitea.io/gitea/modules/markup" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/ssh" | "code.gitea.io/gitea/modules/ssh" | ||||
| macaron "gopkg.in/macaron.v1" | macaron "gopkg.in/macaron.v1" | ||||
| ) | ) | ||||
| @@ -37,6 +39,7 @@ func checkRunMode() { | |||||
| func NewServices() { | func NewServices() { | ||||
| setting.NewServices() | setting.NewServices() | ||||
| mailer.NewContext() | mailer.NewContext() | ||||
| cache.NewContext() | |||||
| } | } | ||||
| // GlobalInit is for global configuration reload-able. | // GlobalInit is for global configuration reload-able. | ||||
| @@ -55,7 +55,7 @@ func Commits(ctx *context.Context) { | |||||
| } | } | ||||
| ctx.Data["PageIsViewCode"] = true | ctx.Data["PageIsViewCode"] = true | ||||
| commitsCount, err := ctx.Repo.Commit.CommitsCount() | |||||
| commitsCount, err := ctx.Repo.GetCommitsCount() | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "GetCommitsCount", err) | ctx.Handle(500, "GetCommitsCount", err) | ||||
| return | return | ||||
| @@ -91,7 +91,7 @@ func Graph(ctx *context.Context) { | |||||
| ctx.Data["PageIsCommits"] = true | ctx.Data["PageIsCommits"] = true | ||||
| ctx.Data["PageIsViewCode"] = true | ctx.Data["PageIsViewCode"] = true | ||||
| commitsCount, err := ctx.Repo.Commit.CommitsCount() | |||||
| commitsCount, err := ctx.Repo.GetCommitsCount() | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "GetCommitsCount", err) | ctx.Handle(500, "GetCommitsCount", err) | ||||
| return | return | ||||
| @@ -99,9 +99,9 @@ func NewMacaron() *macaron.Macaron { | |||||
| Redirect: true, | Redirect: true, | ||||
| })) | })) | ||||
| m.Use(cache.Cacher(cache.Options{ | m.Use(cache.Cacher(cache.Options{ | ||||
| Adapter: setting.CacheAdapter, | |||||
| AdapterConfig: setting.CacheConn, | |||||
| Interval: setting.CacheInterval, | |||||
| Adapter: setting.CacheService.Adapter, | |||||
| AdapterConfig: setting.CacheService.Conn, | |||||
| Interval: setting.CacheService.Interval, | |||||
| })) | })) | ||||
| m.Use(captcha.Captchaer(captcha.Options{ | m.Use(captcha.Captchaer(captcha.Options{ | ||||
| SubURL: setting.AppSubURL, | SubURL: setting.AppSubURL, | ||||
| @@ -576,9 +576,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| ctx.Handle(500, "GetBranchCommit", err) | ctx.Handle(500, "GetBranchCommit", err) | ||||
| return | return | ||||
| } | } | ||||
| ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | |||||
| ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "CommitsCount", err) | |||||
| ctx.Handle(500, "GetCommitsCount", err) | |||||
| return | return | ||||
| } | } | ||||
| ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | ||||