forked from sourcegraph/sourcegraph-public-snapshot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommit_cache.go
More file actions
155 lines (131 loc) · 4.84 KB
/
Copy pathcommit_cache.go
File metadata and controls
155 lines (131 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package codenav
import (
"context"
"fmt"
"strings"
"sync"
"github.com/sourcegraph/sourcegraph/internal/codeintel/codenav/shared"
"github.com/sourcegraph/sourcegraph/internal/codeintel/shared/gitserver"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
type CommitCache interface {
AreCommitsResolvable(ctx context.Context, commits []gitserver.RepositoryCommit) ([]bool, error)
ExistsBatch(ctx context.Context, commits []gitserver.RepositoryCommit) ([]bool, error)
SetResolvableCommit(repositoryID int, commit string)
}
type commitCache struct {
gitserverClient shared.GitserverClient
mutex sync.RWMutex
cache map[int]map[string]bool
}
func NewCommitCache(client shared.GitserverClient) CommitCache {
return &commitCache{
gitserverClient: client,
cache: map[int]map[string]bool{},
}
}
// ExistsBatch determines if the given commits are resolvable for the given repositories.
// If we do not know the answer from a previous call to set or existsBatch, we ask gitserver
// to resolve the remaining commits and store the results for subsequent calls. This method
// returns a slice of the same size as the input slice, true indicating that the commit at
// the symmetric index exists.
func (c *commitCache) ExistsBatch(ctx context.Context, commits []gitserver.RepositoryCommit) ([]bool, error) {
exists := make([]bool, len(commits))
rcIndexMap := make([]int, 0, len(commits))
rcs := make([]gitserver.RepositoryCommit, 0, len(commits))
for i, rc := range commits {
if e, ok := c.getInternal(rc.RepositoryID, rc.Commit); ok {
exists[i] = e
} else {
rcIndexMap = append(rcIndexMap, i)
rcs = append(rcs, gitserver.RepositoryCommit{
RepositoryID: rc.RepositoryID,
Commit: rc.Commit,
})
}
}
if len(rcs) == 0 {
return exists, nil
}
// Perform heavy work outside of critical section
e, err := c.gitserverClient.CommitsExist(ctx, rcs)
if err != nil {
return nil, errors.Wrap(err, "gitserverClient.CommitsExist")
}
if len(e) != len(rcs) {
panic(strings.Join([]string{
fmt.Sprintf("Expected slice returned from CommitsExist to have len %d, but has len %d.", len(rcs), len(e)),
"If this panic occurred during a test, your test is missing a mock definition for CommitsExist.",
"If this is occurred during runtime, please file a bug.",
}, " "))
}
for i, rc := range rcs {
exists[rcIndexMap[i]] = e[i]
c.setInternal(rc.RepositoryID, rc.Commit, e[i])
}
return exists, nil
}
// AreCommitsResolvable determines if the given commits are resolvable for the given repositories.
// If we do not know the answer from a previous call to set or AreCommitsResolvable, we ask gitserver
// to resolve the remaining commits and store the results for subsequent calls. This method
// returns a slice of the same size as the input slice, true indicating that the commit at
// the symmetric index exists.
func (c *commitCache) AreCommitsResolvable(ctx context.Context, commits []gitserver.RepositoryCommit) ([]bool, error) {
exists := make([]bool, len(commits))
rcIndexMap := make([]int, 0, len(commits))
rcs := make([]gitserver.RepositoryCommit, 0, len(commits))
for i, rc := range commits {
if e, ok := c.getInternal(rc.RepositoryID, rc.Commit); ok {
exists[i] = e
} else {
rcIndexMap = append(rcIndexMap, i)
rcs = append(rcs, gitserver.RepositoryCommit{
RepositoryID: rc.RepositoryID,
Commit: rc.Commit,
})
}
}
// if there are no repository commits to fetch, we're done
if len(rcs) == 0 {
return exists, nil
}
// Perform heavy work outside of critical section
e, err := c.gitserverClient.CommitsExist(ctx, rcs)
if err != nil {
return nil, errors.Wrap(err, "gitserverClient.CommitsExist")
}
if len(e) != len(rcs) {
panic(strings.Join([]string{
fmt.Sprintf("Expected slice returned from CommitsExist to have len %d, but has len %d.", len(rcs), len(e)),
"If this panic occurred during a test, your test is missing a mock definition for CommitsExist.",
"If this is occurred during runtime, please file a bug.",
}, " "))
}
for i, rc := range rcs {
exists[rcIndexMap[i]] = e[i]
c.setInternal(rc.RepositoryID, rc.Commit, e[i])
}
return exists, nil
}
// set marks the given repository and commit as valid and resolvable by gitserver.
func (c *commitCache) SetResolvableCommit(repositoryID int, commit string) {
c.setInternal(repositoryID, commit, true)
}
func (c *commitCache) getInternal(repositoryID int, commit string) (bool, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if repositoryMap, ok := c.cache[repositoryID]; ok {
if exists, ok := repositoryMap[commit]; ok {
return exists, true
}
}
return false, false
}
func (c *commitCache) setInternal(repositoryID int, commit string, exists bool) {
c.mutex.Lock()
defer c.mutex.Unlock()
if _, ok := c.cache[repositoryID]; !ok {
c.cache[repositoryID] = map[string]bool{}
}
c.cache[repositoryID][commit] = exists
}