internal/pkg: optional shallow external cure
Test / Create distribution (push) Successful in 1m0s
Test / Sandbox (push) Successful in 2m54s
Test / ShareFS (push) Successful in 4m6s
Test / Hakurei (push) Successful in 4m12s
Test / Sandbox (race detector) (push) Successful in 5m49s
Test / Hakurei (race detector) (push) Successful in 6m51s
Test / Flake checks (push) Successful in 1m22s

This saves time and is often more practical even for package maintainers.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-07-07 16:35:08 +09:00
parent b08feac26c
commit aa5d38981d
3 changed files with 258 additions and 32 deletions
+92 -13
View File
@@ -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() {