add resetable sync.Once for timed caching of ctags globs (#32082)

This commit is contained in:
Noah S-C 2022-03-02 22:08:35 +00:00 committed by GitHub
parent b098cc60f4
commit ff87cb8d96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 36 deletions

View File

@ -2,14 +2,10 @@ package resolvers
import (
"context"
"path"
"time"
"github.com/gobwas/glob"
"github.com/opentracing/opentracing-go/log"
"github.com/sourcegraph/go-ctags"
gql "github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
"github.com/sourcegraph/sourcegraph/enterprise/internal/codeintel/policies"
store "github.com/sourcegraph/sourcegraph/enterprise/internal/codeintel/stores/dbstore"
@ -304,15 +300,10 @@ func (r *resolver) SupportedByCtags(ctx context.Context, filepath string, repoNa
return false, "", err
}
for _, allowedLanguage := range ctags.SupportedLanguages {
for _, pattern := range mappings[allowedLanguage] {
compiled, err := glob.Compile(pattern)
if err != nil {
return false, "", err
}
if compiled.Match(path.Base(filepath)) {
return true, allowedLanguage, nil
for language, globs := range mappings {
for _, glob := range globs {
if glob.Match(filepath) {
return true, language, nil
}
}
}

View File

@ -0,0 +1,34 @@
package resetonce
import (
"sync"
"sync/atomic"
)
// Once is a copy of `sync.Once` with a `Reset` method, inspired by
// https://github.com/matryer/resync/blob/master/once.go
type Once struct {
done uint32
m sync.Mutex
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
func (o *Once) Reset() {
o.m.Lock()
defer o.m.Unlock()
atomic.StoreUint32(&o.done, 0)
}

View File

@ -8,18 +8,23 @@ import (
"net/http"
"strings"
"sync"
"time"
"github.com/gobwas/glob"
"github.com/neelance/parallel"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go/ext"
otlog "github.com/opentracing/opentracing-go/log"
"github.com/sourcegraph/go-ctags"
"github.com/sourcegraph/sourcegraph/internal/actor"
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/authz"
"github.com/sourcegraph/sourcegraph/internal/endpoint"
"github.com/sourcegraph/sourcegraph/internal/env"
"github.com/sourcegraph/sourcegraph/internal/httpcli"
"github.com/sourcegraph/sourcegraph/internal/resetonce"
"github.com/sourcegraph/sourcegraph/internal/search"
"github.com/sourcegraph/sourcegraph/internal/search/result"
"github.com/sourcegraph/sourcegraph/internal/trace/ot"
@ -61,12 +66,15 @@ type Client struct {
// database connection.
SubRepoPermsChecker func() authz.SubRepoPermissionChecker
once sync.Once
endpoint *endpoint.Map
endpointOnce sync.Once
endpoint *endpoint.Map
langMappingOnce resetonce.Once
langMappingCache map[string][]glob.Glob
}
func (c *Client) url(repo api.RepoName) (string, error) {
c.once.Do(func() {
c.endpointOnce.Do(func() {
if len(strings.Fields(c.URL)) == 0 {
c.endpoint = endpoint.Empty(errors.New("a symbols service has not been configured"))
} else {
@ -76,30 +84,48 @@ func (c *Client) url(repo api.RepoName) (string, error) {
return c.endpoint.Get(string(repo))
}
func (c *Client) ListLanguageMappings(ctx context.Context, repo api.RepoName) (_ map[string][]string, err error) {
mapping := make(map[string][]string)
func (c *Client) ListLanguageMappings(ctx context.Context, repo api.RepoName) (_ map[string][]glob.Glob, err error) {
c.langMappingOnce.Do(func() {
var resp *http.Response
resp, err = c.httpPost(ctx, "list-languages", repo, nil)
if err != nil {
return
}
defer resp.Body.Close()
resp, err := c.httpPost(ctx, "list-languages", repo, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// best-effort inclusion of body in error message
body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
err = errors.Errorf(
"Symbol.ListLanguageMappings http status %d: %s",
resp.StatusCode,
string(body),
)
return
}
if resp.StatusCode != http.StatusOK {
// best-effort inclusion of body in error message
body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
return nil, errors.Errorf(
"Symbol.ListLanguageMappings http status %d: %s",
resp.StatusCode,
string(body),
)
}
mapping := make(map[string][]string)
err = json.NewDecoder(resp.Body).Decode(&mapping)
if err = json.NewDecoder(resp.Body).Decode(&mapping); err != nil {
return nil, err
}
globs := make(map[string][]glob.Glob, len(ctags.SupportedLanguages))
return mapping, nil
for _, allowedLanguage := range ctags.SupportedLanguages {
for _, pattern := range mapping[allowedLanguage] {
var compiled glob.Glob
compiled, err = glob.Compile(pattern)
if err != nil {
return
}
globs[allowedLanguage] = append(globs[allowedLanguage], compiled)
}
}
c.langMappingCache = globs
time.AfterFunc(time.Minute*10, c.langMappingOnce.Reset)
})
return c.langMappingCache, nil
}
// Search performs a symbol search on the symbols service.