From d2b8b73ea342dae9062c0394d45e290fa4b97d21 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 24 Apr 2026 18:12:44 +0100 Subject: [PATCH] vfs/vfscache/downloaders: kick waiters periodically, not just once The background kicker goroutine had a bare select outside a for loop, so the 5s ticker fired at most once before the goroutine exited. The intent was to run every 5s for the lifetime of the Downloaders. This wraps the select in a for loop so the ticker fires repeatedly until ctx is cancelled. In practice this was benign because every downloader exit and every successful Write already calls kickWaiters, so the background kicker is only load-bearing when a waiter is queued, no downloader is running, and _ensureDownloader failed transiently. In that state, before this fix, the waiter would hang until another Download() call or Close() arrived; now it gets retried every 5s and will either recover or accumulate enough errors to trip maxErrorCount and error out cleanly. --- vfs/vfscache/downloaders/downloaders.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/vfs/vfscache/downloaders/downloaders.go b/vfs/vfscache/downloaders/downloaders.go index c4b66783f..495fdf2bf 100644 --- a/vfs/vfscache/downloaders/downloaders.go +++ b/vfs/vfscache/downloaders/downloaders.go @@ -117,16 +117,18 @@ func New(ctx context.Context, item Item, opt *vfscommon.Options, remote string, } dls.wg.Go(func() { ticker := time.NewTicker(backgroundKickerInterval) - select { - case <-ticker.C: - err := dls.kickWaiters() - if err != nil { - fs.Errorf(dls.src, "vfs cache: failed to kick waiters: %v", err) + defer ticker.Stop() + for { + select { + case <-ticker.C: + err := dls.kickWaiters() + if err != nil { + fs.Errorf(dls.src, "vfs cache: failed to kick waiters: %v", err) + } + case <-ctx.Done(): + return } - case <-ctx.Done(): - break } - ticker.Stop() }) return dls