🌐 AI搜索 & 代理 主页
Skip to content

Commit b27f1e2

Browse files
CopilotJoannaaKL
andauthored
[WIP] Replace custom cache in lockdown.go with cache2go struct (#1425)
* Initial plan * Replace custom cache with cache2go library - Added github.com/muesli/cache2go dependency - Replaced custom map-based cache with cache2go.CacheTable - Removed manual timer management (scheduleExpiry, ensureEntry methods) - Removed timer field from repoAccessCacheEntry struct - Updated GetRepoAccessInfo to use cache2go's Value() and Add() methods - Updated SetTTL to flush and re-add entries with new TTL - Used unique cache names per instance to avoid test interference - All existing tests pass with the new implementation Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> * Final verification complete Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>
1 parent e29a179 commit b27f1e2

File tree

3 files changed

+54
-58
lines changed

3 files changed

+54
-58
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/mark3labs/mcp-go v0.36.0
99
github.com/microcosm-cc/bluemonday v1.0.27
1010
github.com/migueleliasweb/go-github-mock v1.3.0
11+
github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021
1112
github.com/spf13/cobra v1.10.1
1213
github.com/spf13/viper v1.21.0
1314
github.com/stretchr/testify v1.11.1
@@ -37,7 +38,7 @@ require (
3738
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3839
github.com/fsnotify/fsnotify v1.9.0 // indirect
3940
github.com/go-viper/mapstructure/v2 v2.4.0
40-
github.com/google/go-querystring v1.1.0
41+
github.com/google/go-querystring v1.1.0 // indirect
4142
github.com/google/uuid v1.6.0 // indirect
4243
github.com/inconshreveable/mousetrap v1.1.0 // indirect
4344
github.com/pelletier/go-toml/v2 v2.2.4 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
6363
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
6464
github.com/migueleliasweb/go-github-mock v1.3.0 h1:2sVP9JEMB2ubQw1IKto3/fzF51oFC6eVWOOFDgQoq88=
6565
github.com/migueleliasweb/go-github-mock v1.3.0/go.mod h1:ipQhV8fTcj/G6m7BKzin08GaJ/3B5/SonRAkgrk0zCY=
66+
github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g=
67+
github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc=
6668
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
6769
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
6870
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=

pkg/lockdown/lockdown.go

Lines changed: 50 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ import (
66
"log/slog"
77
"strings"
88
"sync"
9+
"sync/atomic"
910
"time"
1011

12+
"github.com/muesli/cache2go"
1113
"github.com/shurcooL/githubv4"
1214
)
1315

16+
var cacheNameCounter atomic.Uint64
17+
1418
// RepoAccessCache caches repository metadata related to lockdown checks so that
1519
// multiple tools can reuse the same access information safely across goroutines.
1620
type RepoAccessCache struct {
1721
client *githubv4.Client
1822
mu sync.Mutex
19-
cache map[string]*repoAccessCacheEntry
23+
cache *cache2go.CacheTable
2024
ttl time.Duration
2125
logger *slog.Logger
2226
}
@@ -25,7 +29,6 @@ type repoAccessCacheEntry struct {
2529
isPrivate bool
2630
knownUsers map[string]bool // normalized login -> has push access
2731
ready bool
28-
timer *time.Timer
2932
}
3033

3134
const defaultRepoAccessTTL = 5 * time.Minute
@@ -51,9 +54,11 @@ func WithLogger(logger *slog.Logger) RepoAccessOption {
5154
// NewRepoAccessCache returns a cache bound to the provided GitHub GraphQL
5255
// client. The cache is safe for concurrent use.
5356
func NewRepoAccessCache(client *githubv4.Client, opts ...RepoAccessOption) *RepoAccessCache {
57+
// Use a unique cache name for each instance to avoid sharing state between tests
58+
cacheName := fmt.Sprintf("repoAccess-%d", cacheNameCounter.Add(1))
5459
c := &RepoAccessCache{
5560
client: client,
56-
cache: make(map[string]*repoAccessCacheEntry),
61+
cache: cache2go.Cache(cacheName),
5762
ttl: defaultRepoAccessTTL,
5863
}
5964
for _, opt := range opts {
@@ -72,8 +77,19 @@ func (c *RepoAccessCache) SetTTL(ttl time.Duration) {
7277
defer c.mu.Unlock()
7378
c.ttl = ttl
7479
c.logInfo("repo access cache TTL updated", "ttl", ttl)
75-
for key, entry := range c.cache {
76-
entry.scheduleExpiry(c, key)
80+
81+
// Collect all current entries
82+
entries := make(map[interface{}]*repoAccessCacheEntry)
83+
c.cache.Foreach(func(key interface{}, item *cache2go.CacheItem) {
84+
entries[key] = item.Data().(*repoAccessCacheEntry)
85+
})
86+
87+
// Flush the cache
88+
c.cache.Flush()
89+
90+
// Re-add all entries with the new TTL
91+
for key, entry := range entries {
92+
c.cache.Add(key, ttl, entry)
7793
}
7894
}
7995

@@ -103,69 +119,46 @@ func (c *RepoAccessCache) GetRepoAccessInfo(ctx context.Context, username, owner
103119
userKey := strings.ToLower(username)
104120
c.mu.Lock()
105121
defer c.mu.Unlock()
106-
entry := c.ensureEntry(key)
107-
if entry.ready {
108-
if cachedHasPush, known := entry.knownUsers[userKey]; known {
109-
entry.scheduleExpiry(c, key)
110-
c.logDebug("repo access cache hit", "owner", owner, "repo", repo, "user", username)
111-
cachedPrivate := entry.isPrivate
112-
return cachedPrivate, cachedHasPush, nil
122+
123+
// Try to get entry from cache - this will keep the item alive if it exists
124+
cacheItem, err := c.cache.Value(key)
125+
if err == nil {
126+
entry := cacheItem.Data().(*repoAccessCacheEntry)
127+
if entry.ready {
128+
if cachedHasPush, known := entry.knownUsers[userKey]; known {
129+
c.logDebug("repo access cache hit", "owner", owner, "repo", repo, "user", username)
130+
return entry.isPrivate, cachedHasPush, nil
131+
}
113132
}
133+
// Entry exists but user not in knownUsers, need to query
114134
}
115135
c.logDebug("repo access cache miss", "owner", owner, "repo", repo, "user", username)
116136

117-
isPrivate, hasPush, err := c.queryRepoAccessInfo(ctx, username, owner, repo)
118-
if err != nil {
119-
return false, false, err
137+
isPrivate, hasPush, queryErr := c.queryRepoAccessInfo(ctx, username, owner, repo)
138+
if queryErr != nil {
139+
return false, false, queryErr
120140
}
121141

122-
entry = c.ensureEntry(key)
123-
entry.ready = true
124-
entry.isPrivate = isPrivate
125-
entry.knownUsers[userKey] = hasPush
126-
entry.scheduleExpiry(c, key)
127-
128-
return isPrivate, hasPush, nil
129-
}
130-
131-
func (c *RepoAccessCache) ensureEntry(key string) *repoAccessCacheEntry {
132-
if c.cache == nil {
133-
c.cache = make(map[string]*repoAccessCacheEntry)
134-
}
135-
entry, ok := c.cache[key]
136-
if !ok {
142+
// Get or create entry - don't use Value() here to avoid keeping alive unnecessarily
143+
var entry *repoAccessCacheEntry
144+
if err == nil && cacheItem != nil {
145+
// Entry already existed, just update it
146+
entry = cacheItem.Data().(*repoAccessCacheEntry)
147+
} else {
148+
// Create new entry
137149
entry = &repoAccessCacheEntry{
138150
knownUsers: make(map[string]bool),
139151
}
140-
c.cache[key] = entry
141152
}
142-
return entry
143-
}
144-
145-
func (entry *repoAccessCacheEntry) scheduleExpiry(c *RepoAccessCache, key string) {
146-
if entry.timer != nil {
147-
entry.timer.Stop()
148-
entry.timer = nil
149-
}
150-
151-
dur := c.ttl
152-
if dur <= 0 {
153-
return
154-
}
155-
156-
owner, repo := splitKey(key)
157-
entry.timer = time.AfterFunc(dur, func() {
158-
c.mu.Lock()
159-
defer c.mu.Unlock()
160-
161-
current, ok := c.cache[key]
162-
if !ok || current != entry {
163-
return
164-
}
153+
154+
entry.ready = true
155+
entry.isPrivate = isPrivate
156+
entry.knownUsers[userKey] = hasPush
157+
158+
// Add or update the entry in cache with TTL
159+
c.cache.Add(key, c.ttl, entry)
165160

166-
delete(c.cache, key)
167-
c.logDebug("repo access cache entry evicted", "owner", owner, "repo", repo)
168-
})
161+
return isPrivate, hasPush, nil
169162
}
170163

171164
func (c *RepoAccessCache) queryRepoAccessInfo(ctx context.Context, username, owner, repo string) (bool, bool, error) {

0 commit comments

Comments
 (0)