diff --git a/internal/bloblru/cache.go b/internal/bloblru/cache.go index 4477e37a9..1ff52094b 100644 --- a/internal/bloblru/cache.go +++ b/internal/bloblru/cache.go @@ -114,10 +114,19 @@ func (c *Cache) GetOrCompute(id restic.ID, compute func() ([]byte, error)) ([]by if isDownloading { // wait for result of parallel download <-waitForResult - blob, ok := c.Get(id) - if ok { - return blob, nil - } + } + + // try again. This is necessary independent of whether isDownloading is true or not. + // The calls to `c.Get()` and checking/adding the entry in `c.inProgress` are not atomic, + // thus the item might have been computed in the meantime. + // The following scenario would compute() the value multiple times otherwise: + // Goroutine A does not find a value in the initial call to `c.Get`, then goroutine B + // takes over, caches the computed value and cleans up its channel in c.inProgress. + // Then goroutine A continues, does not detect a parallel computation and would try + // to call compute() again. + blob, ok = c.Get(id) + if ok { + return blob, nil } // download it