Skip to content

Commit 7b630eb

Browse files
committed
contenthash: add test using counter metric in scanPath
While we now have tests ensuring that needsScan does not regress and indicate that a scan is neccessary, it seems prudent to also include checks that scanPath is definitely not running on any new paths when we don't expect it. Suggested-by: Tonis Tiigi <[email protected]> Signed-off-by: Aleksa Sarai <[email protected]>
1 parent 0d99599 commit 7b630eb

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

cache/contenthash/checksum.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"strings"
1212
"sync"
13+
"sync/atomic"
1314

1415
iradix "github.com/hashicorp/go-immutable-radix/v2"
1516
simplelru "github.com/hashicorp/golang-lru/v2/simplelru"
@@ -995,6 +996,13 @@ func (cc *cacheContext) needsScan(root *iradix.Node[*CacheRecord], path string,
995996
return cr == nil && !hasParentInTree, nil
996997
}
997998

999+
// Only used by TestNeedScanChecksumRegression to make sure scanPath is not
1000+
// called for paths we have already scanned.
1001+
var (
1002+
scanCounterEnable bool
1003+
scanCounter atomic.Uint64
1004+
)
1005+
9981006
func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string, followTrailing bool) (retErr error) {
9991007
p = path.Join("/", p)
10001008

@@ -1027,6 +1035,9 @@ func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string, follow
10271035
}
10281036

10291037
err = filepath.Walk(scanPath, func(itemPath string, fi os.FileInfo, err error) error {
1038+
if scanCounterEnable {
1039+
scanCounter.Add(1)
1040+
}
10301041
if err != nil {
10311042
// If the root doesn't exist, ignore the error.
10321043
if itemPath == scanPath && errors.Is(err, os.ErrNotExist) {

cache/contenthash/checksum_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ func TestChecksumSymlinkNoParentScan(t *testing.T) {
112112

113113
// https://github.com/moby/buildkit/issues/5042
114114
func TestNeedScanChecksumRegression(t *testing.T) {
115+
// This test cannot be run in parallel because we use scanCounter.
116+
scanCounterEnable = true
117+
defer func() {
118+
scanCounterEnable = false
119+
}()
120+
115121
tmpdir := t.TempDir()
116122

117123
snapshotter, err := native.NewSnapshotter(filepath.Join(tmpdir, "snapshots"))
@@ -166,11 +172,22 @@ func TestNeedScanChecksumRegression(t *testing.T) {
166172
require.Equalf(t, test.expectNeedsScan, needs, "needsScan(%q, followTrailing=%v)", test.path, test.followTrailing)
167173
}
168174

175+
// Make sure trying to checksum a subpath results in no further scans.
176+
initialScanCounter := scanCounter.Load()
177+
_, err = cc.Checksum(context.TODO(), ref, "/bb/cc", ChecksumOpts{FollowLinks: true}, nil)
178+
require.NoError(t, err)
179+
require.Equal(t, initialScanCounter, scanCounter.Load())
180+
_, err = cc.Checksum(context.TODO(), ref, "/bb/non-existent", ChecksumOpts{FollowLinks: true}, nil)
181+
require.Error(t, err)
182+
require.Equal(t, initialScanCounter, scanCounter.Load())
183+
169184
// Looking up a non-existent path in / will checksum the whole tree. See
170185
// <https://github.com/moby/buildkit/issues/5042> for more information.
171186
// This means that needsScan will return true for any path.
172187
_, err = cc.Checksum(context.TODO(), ref, "/non-existent", ChecksumOpts{FollowLinks: true}, nil)
173188
require.Error(t, err)
189+
fullScanCounter := scanCounter.Load()
190+
require.NotEqual(t, fullScanCounter, initialScanCounter)
174191

175192
root = cc.tree.Root()
176193
for _, path := range []string{
@@ -184,6 +201,21 @@ func TestNeedScanChecksumRegression(t *testing.T) {
184201
require.NoErrorf(t, err, "needsScan(%q, followTrailing=true)", path)
185202
require.Falsef(t, needs2, "needsScan(%q, followTrailing=true)", path)
186203
}
204+
205+
// Looking up any more paths should not result in any more scans because we
206+
// already know / was scanned.
207+
_, err = cc.Checksum(context.TODO(), ref, "/non-existent", ChecksumOpts{FollowLinks: true}, nil)
208+
require.Error(t, err)
209+
require.Equal(t, fullScanCounter, scanCounter.Load())
210+
_, err = cc.Checksum(context.TODO(), ref, "/different/non/existent", ChecksumOpts{FollowLinks: true}, nil)
211+
require.Error(t, err)
212+
require.Equal(t, fullScanCounter, scanCounter.Load())
213+
_, err = cc.Checksum(context.TODO(), ref, "/aa/root/aa/non-exist", ChecksumOpts{FollowLinks: true}, nil)
214+
require.Error(t, err)
215+
require.Equal(t, fullScanCounter, scanCounter.Load())
216+
_, err = cc.Checksum(context.TODO(), ref, "/aa/root/bb/cc", ChecksumOpts{FollowLinks: true}, nil)
217+
require.NoError(t, err)
218+
require.Equal(t, fullScanCounter, scanCounter.Load())
187219
}
188220

189221
func TestChecksumNonLexicalSymlinks(t *testing.T) {

0 commit comments

Comments
 (0)