* 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.3.0-rc1
; 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" |
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 | ||||
"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" | ||||
) | ) | ||||
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) |
// 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) | |||||
} |
"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" | ||||
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) { | ||||
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 |
// 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" | ||||
} | } | ||||
} | } | ||||
// 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") | ||||
} | } |
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 | ||||
"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" | ||||
"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" | ||||
) | ) | ||||
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. |
} | } | ||||
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 | ||||
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 |
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, | ||||
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 |