mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	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
This commit is contained in:
		
							
								
								
									
										3
									
								
								conf/app.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								conf/app.ini
									
									
									
									
										vendored
									
									
								
							| @@ -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) | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								modules/cache/cache.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								modules/cache/cache.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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"}) | 	sec := Cfg.Section("cache") | ||||||
| 	switch CacheAdapter { | 	CacheService = &Cache{ | ||||||
| 	case "memory": | 		Adapter: sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"}), | ||||||
| 		CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60) |  | ||||||
| 	case "redis", "memcache": |  | ||||||
| 		CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ") |  | ||||||
| 	default: |  | ||||||
| 		log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter) |  | ||||||
| 	} | 	} | ||||||
|  | 	switch CacheService.Adapter { | ||||||
|  | 	case "memory": | ||||||
|  | 		CacheService.Interval = sec.Key("INTERVAL").MustInt(60) | ||||||
|  | 	case "redis", "memcache": | ||||||
|  | 		CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ") | ||||||
|  | 	default: | ||||||
|  | 		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["CacheAdapter"] = setting.CacheService.Adapter | ||||||
| 	ctx.Data["CacheInterval"] = setting.CacheInterval | 	ctx.Data["CacheInterval"] = setting.CacheService.Interval | ||||||
| 	ctx.Data["CacheConn"] = setting.CacheConn | 	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, | 		Adapter:       setting.CacheService.Adapter, | ||||||
| 		AdapterConfig: setting.CacheConn, | 		AdapterConfig: setting.CacheService.Conn, | ||||||
| 		Interval:      setting.CacheInterval, | 		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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lauris BH
					Lauris BH