Create virtual member stale copies only after a successful serve_stale lookup
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-25 19:16:16 +11:00
parent de80fd84c2
commit f1fd1a442c
2 changed files with 36 additions and 2 deletions
+7 -2
View File
@@ -548,8 +548,8 @@ func (e *Engine) fetchAlpineMember(ctx context.Context, name, dir string) (*Alpi
// the background, one refresh per member at a time; the next merge picks it up.
func staleMember[T any](ctx context.Context, e *Engine, key, name string, fetch func(context.Context) (*T, error)) (*T, error) {
sk := key + "\x00" + name
// A failed lookup keeps the copy: a transient DB error must not force a blocking fetch.
if remote, err := e.getRemote(ctx, name); err == nil && !remote.ServeStale {
remote, lookupErr := e.getRemote(ctx, name)
if lookupErr == nil && !remote.ServeStale {
e.mu.Lock()
delete(e.stale, sk)
e.mu.Unlock()
@@ -560,6 +560,11 @@ func staleMember[T any](ctx context.Context, e *Engine, key, name string, fetch
e.stale = map[string]*staleEntry{}
}
prev := e.stale[sk]
// A failed lookup serves an existing copy but never creates one.
if lookupErr != nil && (prev == nil || prev.m == nil) {
e.mu.Unlock()
return fetch(ctx)
}
// A cold fetch holds a placeholder so eviction or toggle-off during it is seen.
token := prev
if prev == nil || prev.m == nil {
+29
View File
@@ -343,3 +343,32 @@ func TestServeStaleLookupErrorKeepsCopy(t *testing.T) {
t.Fatal("a failed remote lookup must not drop the copy")
}
}
func TestServeStaleLookupErrorColdFetchStoresNothing(t *testing.T) {
e, calls := staleEngine(true, func() *RPMMember { return rpmPkgMember("a", "1") })
e.getRemote = func(context.Context, string) (*models.Remote, error) { return nil, errors.New("db down") }
fetchRepomd(t, e)
if calls.Load() != 1 {
t.Fatalf("cold member with a failed lookup should block on one fetch, got %d", calls.Load())
}
e.mu.Lock()
defer e.mu.Unlock()
if _, ok := e.stale[rpmStaleKey]; ok {
t.Fatal("a failed lookup must not create a copy")
}
}
func TestNonServeStaleLookupErrorStoresNothing(t *testing.T) {
e, calls := staleEngine(false, func() *RPMMember { return rpmPkgMember("a", "1") })
fetchRepomd(t, e)
e.getRemote = func(context.Context, string) (*models.Remote, error) { return nil, errors.New("db down") }
fetchRepomd(t, e)
if calls.Load() != 2 {
t.Fatalf("each merge should fetch the member, got %d", calls.Load())
}
e.mu.Lock()
defer e.mu.Unlock()
if _, ok := e.stale[rpmStaleKey]; ok {
t.Fatal("a failed lookup must not create a copy for a non-serve_stale member")
}
}