From 30f7ddb833adfe276a93c1a79e243b8d33bdd41e Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Tue, 9 Feb 2021 22:29:03 +0000
Subject: [PATCH] Ensure memcache TTL cannot be over 30 days (#14592)

Memcached TTL cannot be > 30 days and if it is attempted the TTL is interpreted as
a unix timestamp.

This PR ensures that the TTL is switched to a unix timestamp in those cases.

Fix #14571

Signed-off-by: Andrew Thornton <art27@cantab.net>
---
 modules/cache/cache.go                   |  6 +++---
 modules/git/last_commit_cache.go         |  2 +-
 modules/git/last_commit_cache_gogit.go   |  4 ++--
 modules/git/last_commit_cache_nogogit.go |  4 ++--
 modules/repository/cache.go              |  2 +-
 modules/setting/cache.go                 | 19 +++++++++++++++++++
 routers/repo/view.go                     |  2 +-
 7 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/modules/cache/cache.go b/modules/cache/cache.go
index 609f5a242b..3a2732c343 100644
--- a/modules/cache/cache.go
+++ b/modules/cache/cache.go
@@ -58,7 +58,7 @@ func GetString(key string, getFunc func() (string, error)) (string, error) {
 		if value, err = getFunc(); err != nil {
 			return value, err
 		}
-		err = conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
+		err = conn.Put(key, value, setting.CacheService.TTLSeconds())
 		if err != nil {
 			return "", err
 		}
@@ -86,7 +86,7 @@ func GetInt(key string, getFunc func() (int, error)) (int, error) {
 		if value, err = getFunc(); err != nil {
 			return value, err
 		}
-		err = conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
+		err = conn.Put(key, value, setting.CacheService.TTLSeconds())
 		if err != nil {
 			return 0, err
 		}
@@ -118,7 +118,7 @@ func GetInt64(key string, getFunc func() (int64, error)) (int64, error) {
 		if value, err = getFunc(); err != nil {
 			return value, err
 		}
-		err = conn.Put(key, value, int64(setting.CacheService.TTL.Seconds()))
+		err = conn.Put(key, value, setting.CacheService.TTLSeconds())
 		if err != nil {
 			return 0, err
 		}
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index 7cca601226..37a59e1fa8 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -25,5 +25,5 @@ func (c *LastCommitCache) getCacheKey(repoPath, ref, entryPath string) string {
 // Put put the last commit id with commit and entry path
 func (c *LastCommitCache) Put(ref, entryPath, commitID string) error {
 	log("LastCommitCache save: [%s:%s:%s]", ref, entryPath, commitID)
-	return c.cache.Put(c.getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl)
+	return c.cache.Put(c.getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl())
 }
diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go
index 76c97a4cc0..65d6299bef 100644
--- a/modules/git/last_commit_cache_gogit.go
+++ b/modules/git/last_commit_cache_gogit.go
@@ -16,14 +16,14 @@ import (
 // LastCommitCache represents a cache to store last commit
 type LastCommitCache struct {
 	repoPath    string
-	ttl         int64
+	ttl         func() int64
 	repo        *Repository
 	commitCache map[string]*object.Commit
 	cache       Cache
 }
 
 // NewLastCommitCache creates a new last commit cache for repo
-func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl int64, cache Cache) *LastCommitCache {
+func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl func() int64, cache Cache) *LastCommitCache {
 	if cache == nil {
 		return nil
 	}
diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go
index b9c50b5cfb..0e52d16538 100644
--- a/modules/git/last_commit_cache_nogogit.go
+++ b/modules/git/last_commit_cache_nogogit.go
@@ -13,14 +13,14 @@ import (
 // LastCommitCache represents a cache to store last commit
 type LastCommitCache struct {
 	repoPath    string
-	ttl         int64
+	ttl         func() int64
 	repo        *Repository
 	commitCache map[string]*Commit
 	cache       Cache
 }
 
 // NewLastCommitCache creates a new last commit cache for repo
-func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl int64, cache Cache) *LastCommitCache {
+func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl func() int64, cache Cache) *LastCommitCache {
 	if cache == nil {
 		return nil
 	}
diff --git a/modules/repository/cache.go b/modules/repository/cache.go
index 0852771a55..5d6dea8fcb 100644
--- a/modules/repository/cache.go
+++ b/modules/repository/cache.go
@@ -41,7 +41,7 @@ func CacheRef(repo *models.Repository, gitRepo *git.Repository, fullRefName stri
 		return nil
 	}
 
-	commitCache := git.NewLastCommitCache(repo.FullName(), gitRepo, int64(setting.CacheService.LastCommit.TTL.Seconds()), cache.GetCache())
+	commitCache := git.NewLastCommitCache(repo.FullName(), gitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
 
 	return commitCache.CacheCommit(commit)
 }
diff --git a/modules/setting/cache.go b/modules/setting/cache.go
index 618be2482a..7bfea91961 100644
--- a/modules/setting/cache.go
+++ b/modules/setting/cache.go
@@ -49,6 +49,9 @@ var (
 	}
 )
 
+// MemcacheMaxTTL represents the maximum memcache TTL
+const MemcacheMaxTTL = 30 * 24 * time.Hour
+
 func newCacheService() {
 	sec := Cfg.Section("cache")
 	if err := sec.MapTo(&CacheService); err != nil {
@@ -85,3 +88,19 @@ func newCacheService() {
 		log.Info("Last Commit Cache Service Enabled")
 	}
 }
+
+// TTLSeconds returns the TTLSeconds or unix timestamp for memcache
+func (c Cache) TTLSeconds() int64 {
+	if c.Adapter == "memcache" && c.TTL > MemcacheMaxTTL {
+		return time.Now().Add(c.TTL).Unix()
+	}
+	return int64(c.TTL.Seconds())
+}
+
+// LastCommitCacheTTLSeconds returns the TTLSeconds or unix timestamp for memcache
+func LastCommitCacheTTLSeconds() int64 {
+	if CacheService.Adapter == "memcache" && CacheService.LastCommit.TTL > MemcacheMaxTTL {
+		return time.Now().Add(CacheService.LastCommit.TTL).Unix()
+	}
+	return int64(CacheService.LastCommit.TTL.Seconds())
+}
diff --git a/routers/repo/view.go b/routers/repo/view.go
index e50e4613b7..a5e3cbe3e4 100644
--- a/routers/repo/view.go
+++ b/routers/repo/view.go
@@ -140,7 +140,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 
 	var c *git.LastCommitCache
 	if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount {
-		c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, int64(setting.CacheService.LastCommit.TTL.Seconds()), cache.GetCache())
+		c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
 	}
 
 	var latestCommit *git.Commit