aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/pkg.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/pkg.go')
-rw-r--r--internal/pkg/pkg.go105
1 files changed, 92 insertions, 13 deletions
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index 78e62e85..aadfa308 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -368,7 +368,7 @@ var _ error = InvalidLookupError{}
// GetArtifact returns the identifier pathname and checksum of an [Artifact].
// Calling Pathname with an [Artifact] not part of the slice returned by
-// [Artifact.Dependencies] panics.
+// [Artifact.Inputs] panics.
func (f *FContext) GetArtifact(a Artifact) (
pathname *check.Absolute,
checksum unique.Handle[Checksum],
@@ -678,6 +678,10 @@ const (
// CIgnoreSubstitutes disables content-based dependency substitution.
CIgnoreSubstitutes
+
+ // CExternShallow arranges for only non-flood inputs to be fetched when
+ // curing an [Artifact] available via the external cache.
+ CExternShallow
)
// toplevel holds [context.WithCancel] over caller-supplied context, where all
@@ -1914,25 +1918,57 @@ func (c *Cache) tryExtern(ctx context.Context, id unique.Handle[ID]) (
}
// cureMany concurrently collects outcome of multiple [Artifact].
-func (c *Cache) cureMany(inputs []Artifact, r map[Artifact]cureRes) error {
+func (c *Cache) cureMany(
+ inputs []Artifact,
+ r map[Artifact]cureRes,
+ shallow bool,
+) ([]bool, error) {
var wg sync.WaitGroup
wg.Add(len(inputs))
+ var mask []bool
res := make([]cureRes, len(inputs))
errs := make(DependencyCureError, 0, len(inputs))
var errsMu sync.Mutex
+ if shallow {
+ mask = make([]bool, len(inputs))
+ }
for i, d := range inputs {
+ if shallow {
+ if _, ok := d.(FloodArtifact); ok {
+ mask[i] = true
+ wg.Done()
+ continue
+ }
+
+ if kc, ok := d.(KnownChecksum); ok {
+ res[i].checksum = unique.Make(kc.Checksum())
+ wg.Done()
+ continue
+ }
+ }
pending := pendingArtifactDep{d, &res[i], &errs, &errsMu, &wg}
go pending.cure(c)
}
wg.Wait()
if len(errs) > 0 {
- return &errs
+ return mask, &errs
}
for i, p := range res {
+ if shallow && mask[i] {
+ continue
+ }
r[inputs[i]] = p
}
- return nil
+ return mask, nil
+}
+
+// HangingInputError describes an input of an [Artifact] on a sparse cache
+// without an outcome available locally or via [External].
+type HangingInputError unique.Handle[ID]
+
+func (e HangingInputError) Error() string {
+ return Encode(unique.Handle[ID](e).Value()) + " is unavailable"
}
// cure implements Cure without acquiring a read lock on abortMu. cure must not
@@ -2193,12 +2229,62 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
break
case FloodArtifact:
+ var externChecksum unique.Handle[Checksum]
+ if externChecksum, err = c.tryExtern(ctx, id); err != nil {
+ if c.msg.IsVerbose() {
+ c.msg.Verbosef("extern %s: %v", reportName(ca, id), err)
+ }
+ return
+ }
+ extern := externChecksum != zeroChecksum
+ shallow := extern && c.flags&CExternShallow != 0
+
inputs := a.Inputs()
f := FContext{t, make(map[Artifact]cureRes, len(inputs))}
- if err = c.cureMany(inputs, f.inputs); err != nil {
+ var mask []bool
+ if mask, err = c.cureMany(inputs, f.inputs, shallow); err != nil {
return
}
+ if shallow {
+ for i, d := range inputs {
+ if !mask[i] {
+ continue
+ }
+
+ if kc, ok := d.(KnownChecksum); ok {
+ f.inputs[d] = cureRes{checksum: unique.Make(kc.Checksum())}
+ continue
+ }
+
+ var sum unique.Handle[Checksum]
+ did := c.Ident(d)
+ sum, err = c.tryLocal(did)
+ if err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ return
+ }
+
+ sum, err = c.tryExtern(ctx, did)
+ if err != nil {
+ return
+ }
+ if sum == zeroChecksum {
+ if c.msg.IsVerbose() {
+ c.msg.Verbosef(
+ "input %s not available",
+ reportName(d, did),
+ )
+ }
+ err = HangingInputError(did)
+ return
+ }
+ }
+
+ f.inputs[d] = cureRes{checksum: sum}
+ }
+ }
+
sh := sha512.New384()
err = c.encode(sh, a, f.inputs)
if err != nil {
@@ -2245,14 +2331,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
defer f.destroy(&err)
- var externChecksum unique.Handle[Checksum]
- if externChecksum, err = c.tryExtern(ctx, id); err != nil {
- if c.msg.IsVerbose() {
- c.msg.Verbosef("extern %s: %v", reportName(ca, id), err)
- }
- return
- }
- if externChecksum != zeroChecksum {
+ if extern {
if checksum != zeroChecksum && externChecksum != checksum {
err = &ChecksumMismatchError{externChecksum.Value(), checksum.Value()}
if c.msg.IsVerbose() {