This repository has been archived by the owner on Sep 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
resolve major search perf regression in v3.2.0 (add redis cache to gi…
…t commit OID resolution) (#3685) * refactor OID resolution logic (review with whitespace off) * add oid resolution cache * add observability * CHANGELOG: update * custom cache implementation * enterprise/dev/Procfile: fix bug where zoekt is not listening * remove redundant return stmt * resolveCommitIODUncached * faster test * Update CHANGELOG.md
- Loading branch information
Showing
5 changed files
with
358 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
cmd/frontend/graphqlbackend/git_commit_resolution_cache.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package graphqlbackend | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
|
||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
type resolutionCacheEntry struct { | ||
t time.Time | ||
value string | ||
} | ||
|
||
type resolutionCache struct { | ||
// ttl indicates how long before cache entries expire. There is no limit on | ||
// the size of the cache except the effective # of repositories on the | ||
// Sourcegraph instance. | ||
ttl time.Duration | ||
|
||
// cacheEntries, if non-nil, is used to record the number of entries in the cache. | ||
cacheEntries prometheus.Histogram | ||
|
||
// workerInterval, if non-zero, specifies the interval at which the worker | ||
// checks for entries to evict. Defaults ttl / 2. | ||
workerInterval time.Duration | ||
|
||
// mockSleep, if non-nil can be used to mock time.Sleep for testing purposes. | ||
mockSleep func(d time.Duration) | ||
|
||
m sync.Map | ||
} | ||
|
||
func (r *resolutionCache) Set(k, v string) { | ||
r.m.Store(k, resolutionCacheEntry{ | ||
t: time.Now(), | ||
value: v, | ||
}) | ||
} | ||
|
||
func (r *resolutionCache) Get(k string) (string, bool) { | ||
v, ok := r.m.Load(k) | ||
if !ok { | ||
return "", false | ||
} | ||
e := v.(resolutionCacheEntry) | ||
if time.Since(e.t) >= r.ttl { | ||
// entry has expired | ||
r.m.Delete(k) | ||
return "", false | ||
} | ||
return e.value, true | ||
} | ||
|
||
func (r *resolutionCache) startWorker() *resolutionCache { | ||
if r.workerInterval == 0 { | ||
r.workerInterval = r.ttl / 2 | ||
} | ||
sleep := time.Sleep | ||
if r.mockSleep != nil { | ||
sleep = r.mockSleep | ||
} | ||
go func() { | ||
for { | ||
sleep(r.workerInterval) | ||
size := 0 | ||
r.m.Range(func(key, value interface{}) bool { | ||
size++ | ||
e := value.(resolutionCacheEntry) | ||
if time.Since(e.t) >= r.ttl { | ||
// entry has expired | ||
r.m.Delete(key) | ||
} | ||
return true | ||
}) | ||
r.cacheEntries.Observe(float64(size)) | ||
} | ||
}() | ||
return r | ||
} | ||
|
||
var ( | ||
// oidResolutionCache is used to cache Git commit OID resolution. This is | ||
// used because OID resolution happens extremely often (e.g. multiple times | ||
// per search result). | ||
oidResolutionCache = (&resolutionCache{ | ||
ttl: 60 * time.Second, | ||
cacheEntries: prometheus.NewHistogram(prometheus.HistogramOpts{ | ||
Namespace: "src", | ||
Subsystem: "graphql", | ||
Name: "git_commit_oid_resolution_cache_entries", | ||
Help: "Total number of entries in the in-memory Git commit OID resolution cache.", | ||
}), | ||
}).startWorker() | ||
|
||
oidResolutionCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ | ||
Namespace: "src", | ||
Subsystem: "graphql", | ||
Name: "git_commit_oid_resolution_cache_hit", | ||
Help: "Counts cache hits and misses for Git commit OID resolution.", | ||
}, []string{"type"}) | ||
|
||
oidResolutionDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ | ||
Namespace: "src", | ||
Subsystem: "graphql", | ||
Name: "git_commit_oid_resolution_duration_seconds", | ||
Help: "Total time spent performing uncached Git commit OID resolution.", | ||
}) | ||
|
||
oidResolutionCacheLookupDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ | ||
Namespace: "src", | ||
Subsystem: "graphql", | ||
Name: "git_commit_oid_resolution_cache_lookup_duration_seconds", | ||
Help: "Total time spent performing cache lookups for Git commit OID resolution.", | ||
}) | ||
) | ||
|
||
func init() { | ||
prometheus.MustRegister(oidResolutionCache.cacheEntries) | ||
prometheus.MustRegister(oidResolutionCounter) | ||
prometheus.MustRegister(oidResolutionDuration) | ||
prometheus.MustRegister(oidResolutionCacheLookupDuration) | ||
} |
Oops, something went wrong.