forked from tangled.org/core
this repo has no description

knotserver: git: cache merge check results

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 1229a5d6 46d66329

verified
Changed files
+66 -1
knotserver
git
+66 -1
knotserver/git/merge.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 + "crypto/sha256" 5 6 "fmt" 6 7 "os" 7 8 "os/exec" 8 9 "regexp" 9 10 "strings" 10 11 12 + "github.com/dgraph-io/ristretto" 11 13 "github.com/go-git/go-git/v5" 12 14 "github.com/go-git/go-git/v5/plumbing" 13 15 "tangled.sh/tangled.sh/core/patchutil" 14 16 ) 17 + 18 + type MergeCheckCache struct { 19 + cache *ristretto.Cache 20 + } 21 + 22 + var ( 23 + mergeCheckCache MergeCheckCache 24 + ) 25 + 26 + func init() { 27 + cache, _ := ristretto.NewCache(&ristretto.Config{ 28 + NumCounters: 1e7, 29 + MaxCost: 1 << 30, 30 + BufferItems: 64, 31 + TtlTickerDurationInSec: 60 * 60 * 24 * 2, // 2 days 32 + }) 33 + mergeCheckCache = MergeCheckCache{cache} 34 + } 35 + 36 + func (m *MergeCheckCache) cacheKey(g *GitRepo, patch []byte, targetBranch string) string { 37 + sep := byte(':') 38 + hash := sha256.Sum256(fmt.Append([]byte{}, g.path, sep, g.h.String(), sep, patch, sep, targetBranch)) 39 + return fmt.Sprintf("%x", hash) 40 + } 41 + 42 + // we can't cache "mergeable" in risetto, nil is not cacheable 43 + // 44 + // we use the sentinel value instead 45 + func (m *MergeCheckCache) cacheVal(check error) any { 46 + if check == nil { 47 + return struct{}{} 48 + } else { 49 + return check 50 + } 51 + } 52 + 53 + func (m *MergeCheckCache) Set(g *GitRepo, patch []byte, targetBranch string, mergeCheck error) { 54 + key := m.cacheKey(g, patch, targetBranch) 55 + val := m.cacheVal(mergeCheck) 56 + m.cache.Set(key, val, 0) 57 + } 58 + 59 + func (m *MergeCheckCache) Get(g *GitRepo, patch []byte, targetBranch string) (error, bool) { 60 + key := m.cacheKey(g, patch, targetBranch) 61 + if val, ok := m.cache.Get(key); ok { 62 + if val == struct{}{} { 63 + // cache hit for mergeable 64 + return nil, true 65 + } else if e, ok := val.(error); ok { 66 + // cache hit for merge conflict 67 + return e, true 68 + } 69 + } 70 + 71 + // cache miss 72 + return nil, false 73 + } 15 74 16 75 type ErrMerge struct { 17 76 Message string ··· 165 224 } 166 225 167 226 func (g *GitRepo) MergeCheck(patchData []byte, targetBranch string) error { 227 + if val, ok := mergeCheckCache.Get(g, patchData, targetBranch); ok { 228 + return val 229 + } 230 + 168 231 var opts MergeOptions 169 232 opts.FormatPatch = patchutil.IsFormatPatch(string(patchData)) 170 233 ··· 186 249 } 187 250 defer os.RemoveAll(tmpDir) 188 251 189 - return g.applyPatch(tmpDir, patchFile, true, &opts) 252 + result := g.applyPatch(tmpDir, patchFile, true, &opts) 253 + mergeCheckCache.Set(g, patchData, targetBranch, result) 254 + return result 190 255 } 191 256 192 257 func (g *GitRepo) Merge(patchData []byte, targetBranch string) error {