Serve deb virtual indexes by hash and bound cached suites
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

This commit is contained in:
2026-10-17 12:46:14 +11:00
parent eca686b7bb
commit ed6216d78b
5 changed files with 197 additions and 27 deletions
+26 -4
View File
@@ -51,11 +51,14 @@ func (r *DebRepo) files() map[string][]byte { return r.Files }
// holding the previous index can still fetch the files it references.
type repoGen struct {
cur, prev mergedRepo
at time.Time
at, used time.Time
}
const rpmMergeTTL = 60 * time.Second
// maxDebSuites bounds the client-chosen suites cached per deb virtual.
const maxDebSuites = 32
func NewEngine(db *database.DB, proxyEngine *proxy.Engine) *Engine {
e := &Engine{db: db, proxyEngine: proxyEngine, getRemote: db.GetRemote, mergeTTL: rpmMergeTTL}
e.rpmMember = e.fetchRPMMember
@@ -276,6 +279,7 @@ func (e *Engine) merged(ctx context.Context, key string, merge func(context.Cont
e.mu.Lock()
g := e.gens[key]
if g != nil && time.Since(g.at) < e.mergeTTL {
g.used = time.Now()
e.mu.Unlock()
return g.cur, nil
}
@@ -294,12 +298,12 @@ func (e *Engine) merged(ctx context.Context, key string, merge func(context.Cont
g := e.gens[key]
switch {
case g == nil:
e.gens[key] = &repoGen{cur: repo, at: time.Now()}
e.gens[key] = &repoGen{cur: repo, at: time.Now(), used: time.Now()}
case string(g.cur.index()) == string(repo.index()):
g.at = time.Now()
g.at, g.used = time.Now(), time.Now()
repo = g.cur
default:
g.prev, g.cur, g.at = g.cur, repo, time.Now()
g.prev, g.cur, g.at, g.used = g.cur, repo, time.Now(), time.Now()
}
return repo, nil
})
@@ -368,6 +372,7 @@ func (e *Engine) fetchDeb(ctx context.Context, virt models.Virtual, reqPath stri
if err != nil {
return nil, "", err
}
e.evictLRU("deb\x00"+virt.Name+"\x00", maxDebSuites)
if name == "Release" {
return repo.index(), "text/plain", nil
}
@@ -408,6 +413,22 @@ func (e *Engine) fetchDebMember(ctx context.Context, name, base string) (*DebMem
return m, nil
}
// evictLRU drops the least recently used merges under prefix beyond keep.
func (e *Engine) evictLRU(prefix string, keep int) {
e.mu.Lock()
defer e.mu.Unlock()
var keys []string
for k := range e.gens {
if strings.HasPrefix(k, prefix) {
keys = append(keys, k)
}
}
slices.SortFunc(keys, func(a, b string) int { return e.gens[b].used.Compare(e.gens[a].used) })
for _, k := range keys[min(len(keys), keep):] {
delete(e.gens, k)
}
}
func (e *Engine) cachedFile(key, path string) ([]byte, bool) {
e.mu.Lock()
defer e.mu.Unlock()
@@ -420,6 +441,7 @@ func (e *Engine) cachedFile(key, path string) ([]byte, bool) {
continue
}
if body, ok := r.files()[path]; ok {
g.used = time.Now()
return body, true
}
}