diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go index f1e6279d..d8e059bd 100644 --- a/internal/pkg/pkg.go +++ b/internal/pkg/pkg.go @@ -770,6 +770,8 @@ type Cache struct { // Optional external cache implementation. extern External + // Caches responses from extern. + externCache map[unique.Handle[ID]]unique.Handle[Checksum] // Synchronises access to extern. externMu sync.RWMutex @@ -930,6 +932,7 @@ func readlinkChecksum(a *check.Absolute, buf *Checksum) error { // SetExternal sets e as the [External] implementation of c. func (c *Cache) SetExternal(e External) { c.externMu.Lock() + c.externCache = make(map[unique.Handle[ID]]unique.Handle[Checksum]) c.extern = e c.externMu.Unlock() } @@ -1849,37 +1852,45 @@ func (r *RContext) NewMeasuredReader( } // tryExtern attempts to obtain an [Artifact] outcome from extern. -func (c *Cache) tryExtern(ctx context.Context, id unique.Handle[ID]) ( +func (c *Cache) tryExtern( + ctx context.Context, + id unique.Handle[ID], + status *io.ReadCloser, +) ( unique.Handle[Checksum], - io.ReadCloser, error, ) { c.externMu.RLock() defer c.externMu.RUnlock() - if c.extern == nil { - return zeroChecksum, nil, nil + checksum, ok := c.externCache[id] + if !ok { + if c.extern == nil { + return zeroChecksum, nil + } + + v, err := c.extern.Artifact(ctx, id) + if err != nil { + return zeroChecksum, err + } + if v == nil { + return zeroChecksum, nil + } + checksum = unique.Make(*v) + + var got unique.Handle[Checksum] + if _, got, err = c.Cure(c.extern.Checksum(checksum)); err != nil { + return checksum, err + } else if got != checksum { + return zeroChecksum, &ChecksumMismatchError{got.Value(), checksum.Value()} + } } - v, err := c.extern.Artifact(ctx, id) - if err != nil { - return zeroChecksum, nil, err + var err error + if status != nil { + *status, err = c.extern.Status(&RContext{common{ctx, c}}, id) } - if v == nil { - return zeroChecksum, nil, nil - } - checksum := unique.Make(*v) - - var got unique.Handle[Checksum] - if _, got, err = c.Cure(c.extern.Checksum(checksum)); err != nil { - return checksum, nil, err - } else if got != checksum { - return zeroChecksum, nil, &ChecksumMismatchError{got.Value(), checksum.Value()} - } - - var status io.ReadCloser - status, err = c.extern.Status(&RContext{common{ctx, c}}, id) - return checksum, status, err + return checksum, err } // cure implements Cure without acquiring a read lock on abortMu. cure must not @@ -2225,7 +2236,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( externChecksum unique.Handle[Checksum] externStatus io.ReadCloser ) - if externChecksum, externStatus, err = c.tryExtern(ctx, id); err != nil { + if externChecksum, err = c.tryExtern(ctx, id, &externStatus); err != nil { if c.msg.IsVerbose() { c.msg.Verbosef("extern %s: %v", reportName(ca, id), err) }