Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions core/itg/graph/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,23 @@ func computeAvailableHashes(

// computeHashes computes hashes recursively for the given target ID.
func (g *OptimizedGraph) computeHashes(ctx context.Context, id int) ([]byte, error) {
computed := make(map[int][]byte)
hash, err := g.computeHashesRecursively(ctx, id, NewIntSet(), computed)
if err != nil {
return nil, err
}
for targetID, targetHash := range computed {
g.OptimizedTargets[targetID].Hash = targetHash
}
return hash, nil
}

func (g *OptimizedGraph) computeHashesRecursively(
ctx context.Context,
id int,
visiting IntSet,
computed map[int][]byte,
) ([]byte, error) {
if ctx.Err() != nil {
return nil, context.Cause(ctx)
}
Expand All @@ -201,9 +218,16 @@ func (g *OptimizedGraph) computeHashes(ctx context.Context, id int) ([]byte, err
if target.Hash != nil {
return target.Hash, nil
}
if hash, ok := computed[id]; ok {
return hash, nil
}
if visiting.Contains(id) {
return []byte{}, nil
}

visiting.Insert(id)
defer visiting.Delete(id)

// mark as visiting to handle cycles
target.Hash = []byte{}
var hash []byte
switch g.RuleTypeIDToString[target.RuleType] {
case targethasher.SourceFileType, targethasher.PackageGroup:
Expand All @@ -214,7 +238,7 @@ func (g *OptimizedGraph) computeHashes(ctx context.Context, id int) ([]byte, err
singleDep = dep
break
}
dephash, err := g.computeHashes(ctx, singleDep)
dephash, err := g.computeHashesRecursively(ctx, singleDep, visiting, computed)
if err != nil {
return nil, err
}
Expand All @@ -230,7 +254,7 @@ func (g *OptimizedGraph) computeHashes(ctx context.Context, id int) ([]byte, err
return strings.Compare(g.TargetIDToString[i], g.TargetIDToString[j])
})
for _, dep := range depIDs {
dephash, err := g.computeHashes(ctx, dep)
dephash, err := g.computeHashesRecursively(ctx, dep, visiting, computed)
if err != nil {
return nil, err
}
Expand All @@ -239,7 +263,7 @@ func (g *OptimizedGraph) computeHashes(ctx context.Context, id int) ([]byte, err
hash = h.Sum(nil)
}
if hash != nil {
target.Hash = hash
computed[id] = hash
}
return hash, nil
}
64 changes: 64 additions & 0 deletions core/itg/graph/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package graph

import (
"bytes"
"context"
"crypto/sha1"
"encoding/gob"
"testing"

buildpb "github.com/bazelbuild/buildtools/build_proto"
Expand Down Expand Up @@ -231,4 +233,66 @@ func TestComputeHashes(t *testing.T) {
h.Write(depHash)
assert.Equal(t, h.Sum(nil), got)
})

t.Run("failed traversal restores hashes before retry", func(t *testing.T) {
t.Parallel()
goodName := "//pkg:a_good"
badName := "//pkg:z_bad.go"
rootName := "//pkg:root"
goodHashWithoutDeps := []byte{0x01, 0x02}
rootHashWithoutDeps := []byte{0x03, 0x04}
g := OptimizeGraph(map[string]*targethasher.Target{
goodName: {
Name: goodName,
RuleType: "go_library",
HashWithoutDeps: goodHashWithoutDeps,
},
badName: {
Name: badName,
RuleType: targethasher.SourceFileType,
},
rootName: {
Name: rootName,
RuleType: "go_library",
HashWithoutDeps: rootHashWithoutDeps,
Deps: []string{badName, goodName},
},
})
goodID := g.TargetNameToID[goodName]
badID := g.TargetNameToID[badName]
rootID := g.TargetNameToID[rootName]

_, err := g.computeHashes(context.Background(), rootID)
require.Error(t, err)
assert.Nil(t, g.OptimizedTargets[rootID].Hash)
assert.Nil(t, g.OptimizedTargets[goodID].Hash)
assert.Nil(t, g.OptimizedTargets[badID].Hash)

var persisted bytes.Buffer
require.NoError(t, gob.NewEncoder(&persisted).Encode(g))
var restored OptimizedGraph
require.NoError(t, gob.NewDecoder(&persisted).Decode(&restored))
assert.Nil(t, restored.OptimizedTargets[rootID].Hash)
assert.Nil(t, restored.OptimizedTargets[goodID].Hash)

badHash := []byte{0x05, 0x06}
g.OptimizedTargets[badID].Hash = badHash

got, err := g.computeHashes(context.Background(), rootID)
require.NoError(t, err)

goodHasher := sha1.New()
goodHasher.Write(goodHashWithoutDeps)
goodHash := goodHasher.Sum(nil)
rootHasher := sha1.New()
rootHasher.Write(rootHashWithoutDeps)
rootHasher.Write(goodHash)
rootHasher.Write(badHash)
expected := rootHasher.Sum(nil)

assert.Equal(t, goodHash, g.OptimizedTargets[goodID].Hash)
assert.Equal(t, expected, got)
assert.Equal(t, expected, g.OptimizedTargets[rootID].Hash)
assert.NotEmpty(t, got)
})
}
Loading