* 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 | |||
| ; memcache: `127.0.0.1:11211` | |||
| HOST = | |||
| ; Time to keep items in cache if not used, default is 16 hours. | |||
| ; Setting it to 0 disables caching | |||
| ITEM_TTL = 16h | |||
| [session] | |||
| ; 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) | |||
| } | |||
| // 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 { | |||
| var parent *api.Repository | |||
| @@ -11,7 +11,7 @@ import ( | |||
| "strings" | |||
| "code.gitea.io/git" | |||
| "code.gitea.io/gitea/modules/cache" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| @@ -205,19 +205,26 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) { | |||
| var commits = &PushCommits{} | |||
| if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | |||
| // If is tag reference | |||
| tagName := opts.RefFullName[len(git.TagPrefix):] | |||
| if isDelRef { | |||
| err = pushUpdateDeleteTag(repo, gitRepo, opts.RefFullName[len(git.TagPrefix):]) | |||
| err = pushUpdateDeleteTag(repo, gitRepo, tagName) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("pushUpdateDeleteTag: %v", err) | |||
| } | |||
| } 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 { | |||
| return nil, fmt.Errorf("pushUpdateAddTag: %v", err) | |||
| } | |||
| } | |||
| } else if !isDelRef { | |||
| // 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) | |||
| if err != nil { | |||
| 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/gitea/models" | |||
| "code.gitea.io/gitea/modules/cache" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "github.com/Unknwon/com" | |||
| "gopkg.in/editorconfig/editorconfig-core-go.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) | |||
| } | |||
| // 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 | |||
| // HEAD of the default repo branch. | |||
| func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { | |||
| @@ -535,9 +552,9 @@ func RepoRef() macaron.Handler { | |||
| ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit | |||
| ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() | |||
| ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | |||
| ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() | |||
| if err != nil { | |||
| ctx.Handle(500, "CommitsCount", err) | |||
| ctx.Handle(500, "GetCommitsCount", err) | |||
| return | |||
| } | |||
| ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | |||
| @@ -325,11 +325,6 @@ var ( | |||
| // Time settings | |||
| TimeFormat string | |||
| // Cache settings | |||
| CacheAdapter string | |||
| CacheInterval int | |||
| CacheConn string | |||
| // Session settings | |||
| SessionConfig session.Options | |||
| 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() { | |||
| 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": | |||
| CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60) | |||
| CacheService.Interval = sec.Key("INTERVAL").MustInt(60) | |||
| case "redis", "memcache": | |||
| CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ") | |||
| CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ") | |||
| 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") | |||
| } | |||
| @@ -224,9 +224,9 @@ func Config(ctx *context.Context) { | |||
| 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 | |||
| @@ -11,6 +11,7 @@ import ( | |||
| "code.gitea.io/git" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/models/migrations" | |||
| "code.gitea.io/gitea/modules/cache" | |||
| "code.gitea.io/gitea/modules/cron" | |||
| "code.gitea.io/gitea/modules/highlight" | |||
| "code.gitea.io/gitea/modules/log" | |||
| @@ -18,6 +19,7 @@ import ( | |||
| "code.gitea.io/gitea/modules/markup" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/modules/ssh" | |||
| macaron "gopkg.in/macaron.v1" | |||
| ) | |||
| @@ -37,6 +39,7 @@ func checkRunMode() { | |||
| func NewServices() { | |||
| setting.NewServices() | |||
| mailer.NewContext() | |||
| cache.NewContext() | |||
| } | |||
| // GlobalInit is for global configuration reload-able. | |||
| @@ -55,7 +55,7 @@ func Commits(ctx *context.Context) { | |||
| } | |||
| ctx.Data["PageIsViewCode"] = true | |||
| commitsCount, err := ctx.Repo.Commit.CommitsCount() | |||
| commitsCount, err := ctx.Repo.GetCommitsCount() | |||
| if err != nil { | |||
| ctx.Handle(500, "GetCommitsCount", err) | |||
| return | |||
| @@ -91,7 +91,7 @@ func Graph(ctx *context.Context) { | |||
| ctx.Data["PageIsCommits"] = true | |||
| ctx.Data["PageIsViewCode"] = true | |||
| commitsCount, err := ctx.Repo.Commit.CommitsCount() | |||
| commitsCount, err := ctx.Repo.GetCommitsCount() | |||
| if err != nil { | |||
| ctx.Handle(500, "GetCommitsCount", err) | |||
| return | |||
| @@ -99,9 +99,9 @@ func NewMacaron() *macaron.Macaron { | |||
| Redirect: true, | |||
| })) | |||
| 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{ | |||
| SubURL: setting.AppSubURL, | |||
| @@ -576,9 +576,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| ctx.Handle(500, "GetBranchCommit", err) | |||
| return | |||
| } | |||
| ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | |||
| ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() | |||
| if err != nil { | |||
| ctx.Handle(500, "CommitsCount", err) | |||
| ctx.Handle(500, "GetCommitsCount", err) | |||
| return | |||
| } | |||
| ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | |||