Skip to content
Merged
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
13 changes: 5 additions & 8 deletions packages/orchestrator/pkg/sandbox/block/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,18 +661,15 @@ func (c *Cache) WriteZeroesAt(off, length int64) (int, error) {
// The size might differ from the dirty size, as it may not be fully on disk.
func (c *Cache) FileSize(_ context.Context) (int64, error) {
var stat syscall.Stat_t
err := syscall.Stat(c.filePath, &stat)
if err != nil {
if err := syscall.Stat(c.filePath, &stat); err != nil {
return 0, fmt.Errorf("failed to get file stats: %w", err)
}

var fsStat syscall.Statfs_t
err = syscall.Statfs(c.filePath, &fsStat)
if err != nil {
return 0, fmt.Errorf("failed to get disk stats for path %s: %w", c.filePath, err)
}
// Per POSIX (and Linux man 2 stat), stat.Blocks is always reported in
// 512-byte units, regardless of the underlying filesystem's block size.
const stBlockSize = 512

return stat.Blocks * fsStat.Bsize, nil
return stat.Blocks * stBlockSize, nil
}

func (c *Cache) address(off int64) (*byte, error) {
Expand Down
40 changes: 40 additions & 0 deletions packages/orchestrator/pkg/sandbox/block/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1140,3 +1140,43 @@ func TestCacheDedup_BestEffortPerPageCacheCheck(t *testing.T) {
require.True(t, meta.Dirty.Contains(1))
require.True(t, meta.Dirty.Contains(3))
}

// TestCache_FileSize_MatchesActualAllocation writes a known number of full
// blocks through Cache and verifies that FileSize reports the on-disk
// allocation in bytes — i.e. one block written ⇒ one block on disk.
//
// This is a regression test for using the wrong block-size constant in the
// stat.Blocks math. Before the fix, FileSize multiplied stat.Blocks (POSIX
// 512-byte units) by statfs.Bsize (4096 on ext4), inflating the reported
// usage by 8× and stalling the build-cache disk-pressure eviction loop.
func TestCache_FileSize_MatchesActualAllocation(t *testing.T) {
t.Parallel()

const (
blockSize = int64(4096)
nBlocks = int64(4)
fileSize = blockSize * 64 // sparse, only nBlocks actually allocated
)

cache, err := NewCache(fileSize, blockSize, t.TempDir()+"/cache", false)
require.NoError(t, err)
t.Cleanup(func() { _ = cache.Close() })

buf := make([]byte, blockSize)
_, err = rand.Read(buf)
require.NoError(t, err)
for i := range nBlocks {
_, err = cache.WriteAt(buf, i*blockSize)
require.NoError(t, err)
}

got, err := cache.FileSize(t.Context())
require.NoError(t, err)

expected := nBlocks * blockSize
t.Logf("wrote %d blocks of %d B; FileSize=%d B (expected %d B)", nBlocks, blockSize, got, expected)

require.Equal(t, expected, got,
"FileSize must report on-disk allocation in bytes; a value ~%d× expected suggests stat.Blocks was multiplied by statfs.Bsize instead of the POSIX 512",
int64(4096)/int64(512))
}
Loading