diff --git a/commit_submodule.go b/commit_submodule.go index da63231e..9db90e4e 100644 --- a/commit_submodule.go +++ b/commit_submodule.go @@ -8,6 +8,8 @@ import ( "bufio" "bytes" "strings" + + "github.com/golang/groupcache/lru" ) // Submodule contains information of a Git submodule. @@ -21,7 +23,7 @@ type Submodule struct { } // Submodules contains information of submodules. -type Submodules = *objectCache +type Submodules = *lru.Cache // Submodules returns submodules found in this commit. func (c *Commit) Submodules() (Submodules, error) { @@ -39,7 +41,7 @@ func (c *Commit) Submodules() (Submodules, error) { } scanner := bufio.NewScanner(bytes.NewReader(p)) - c.submodules = newObjectCache() + c.submodules = lru.New(0) var inSection bool var path string var url string @@ -72,7 +74,7 @@ func (c *Commit) Submodules() (Submodules, error) { return } - c.submodules.Set(path, mod) + c.submodules.Add(path, mod) inSection = false } } diff --git a/go.mod b/go.mod index ddbc2b6d..05c73b57 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gogs/git-module go 1.13 require ( + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 github.com/stretchr/testify v1.7.2 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e diff --git a/go.sum b/go.sum index ba4e65f8..05406e72 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk= github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/repo.go b/repo.go index d54cebac..75a065cc 100644 --- a/repo.go +++ b/repo.go @@ -15,14 +15,17 @@ import ( "strconv" "strings" "time" + + "github.com/golang/groupcache/lru" ) // Repository contains information of a Git repository. type Repository struct { path string - cachedCommits *objectCache - cachedTags *objectCache + cachedCommits *lru.Cache + cachedTags *lru.Cache + cachedTrees *lru.Cache } // Path returns the path of the repository. @@ -84,9 +87,18 @@ func Init(path string, opts ...InitOptions) error { return err } +// OpenOptions contains optional arguments for opening a repository. +type OpenOptions struct { + MaxCacheEntries int +} + // Open opens the repository at the given path. It returns an os.ErrNotExist if // the path does not exist. -func Open(repoPath string) (*Repository, error) { +func Open(repoPath string, opts ...OpenOptions) (*Repository, error) { + var opt OpenOptions + if len(opts) > 0 { + opt = opts[0] + } repoPath, err := filepath.Abs(repoPath) if err != nil { return nil, err @@ -96,8 +108,9 @@ func Open(repoPath string) (*Repository, error) { return &Repository{ path: repoPath, - cachedCommits: newObjectCache(), - cachedTags: newObjectCache(), + cachedCommits: lru.New(opt.MaxCacheEntries), + cachedTags: lru.New(opt.MaxCacheEntries), + cachedTrees: lru.New(opt.MaxCacheEntries), }, nil } diff --git a/repo_commit.go b/repo_commit.go index f7ce3bc2..efa9345f 100644 --- a/repo_commit.go +++ b/repo_commit.go @@ -112,7 +112,7 @@ func (r *Repository) CatFileCommit(rev string, opts ...CatFileCommitOptions) (*C c.repo = r c.ID = MustIDFromString(commitID) - r.cachedCommits.Set(commitID, c) + r.cachedCommits.Add(commitID, c) return c, nil } diff --git a/repo_tag.go b/repo_tag.go index 475f0623..ec5e5099 100644 --- a/repo_tag.go +++ b/repo_tag.go @@ -94,7 +94,7 @@ func (r *Repository) getTag(timeout time.Duration, id *SHA1) (*Tag, error) { return nil, fmt.Errorf("unsupported tag type: %s", typ) } - r.cachedTags.Set(id.String(), tag) + r.cachedTags.Add(id.String(), tag) return tag, nil } diff --git a/repo_tree.go b/repo_tree.go index fe98bb39..8037545c 100644 --- a/repo_tree.go +++ b/repo_tree.go @@ -97,25 +97,30 @@ type LsTreeOptions struct { } // LsTree returns the tree object in the repository by given revision. -func (r *Repository) LsTree(rev string, opts ...LsTreeOptions) (*Tree, error) { +func (r *Repository) LsTree(treeID string, opts ...LsTreeOptions) (*Tree, error) { var opt LsTreeOptions if len(opts) > 0 { opt = opts[0] } + cache, ok := r.cachedTrees.Get(treeID) + if ok { + log("Cached tree hit: %s", treeID) + return cache.(*Tree), nil + } var err error - rev, err = r.RevParse(rev, RevParseOptions{Timeout: opt.Timeout}) //nolint + treeID, err = r.RevParse(treeID, RevParseOptions{Timeout: opt.Timeout}) //nolint if err != nil { return nil, err } t := &Tree{ - id: MustIDFromString(rev), + id: MustIDFromString(treeID), repo: r, } stdout, err := NewCommand("ls-tree"). AddOptions(opt.CommandOptions). - AddArgs(rev). + AddArgs(treeID). RunInDirWithTimeout(opt.Timeout, r.path) if err != nil { return nil, err @@ -126,5 +131,6 @@ func (r *Repository) LsTree(rev string, opts ...LsTreeOptions) (*Tree, error) { return nil, err } + r.cachedTrees.Add(treeID, t) return t, nil } diff --git a/utils.go b/utils.go index 79c0e278..781b597c 100644 --- a/utils.go +++ b/utils.go @@ -8,37 +8,8 @@ import ( "fmt" "os" "strings" - "sync" ) -// objectCache provides thread-safe cache operations. TODO(@unknwon): Use -// sync.Map once requires Go 1.13. -type objectCache struct { - lock sync.RWMutex - cache map[string]interface{} -} - -func newObjectCache() *objectCache { - return &objectCache{ - cache: make(map[string]interface{}), - } -} - -func (oc *objectCache) Set(id string, obj interface{}) { - oc.lock.Lock() - defer oc.lock.Unlock() - - oc.cache[id] = obj -} - -func (oc *objectCache) Get(id string) (interface{}, bool) { - oc.lock.RLock() - defer oc.lock.RUnlock() - - obj, has := oc.cache[id] - return obj, has -} - // isDir returns true if given path is a directory, or returns false when it's a // file or does not exist. func isDir(dir string) bool {