Skip to content

Commit

Permalink
Check allocated vs provisioned file size in metrics.
Browse files Browse the repository at this point in the history
Thin allocated/sparse files will report full virtual/max
size in ls but allocated in du.  Comparable stat output
is in %s and %b respectively.  Populate DiskMetric.UsedBytes
with file allocated bytes to avoid rolling over negative when
eve node is populated with persistent volume instances with
little data allocated.  For example an empty system with
a single unattached 1GB persistent volume instance
defined would see the following in
publishMetrics() of handlemetrics.go:

persistUsage = ~1 MB
persistAppUsage = 1 GB
runtimeStorageOverhead = (persistUsage-persistAppUsage)
	= ~17592186042378 MB

ls vs du:
ls -l /persist/clear/volumes/9599c42f-42b0-4807-96a7-2d01cb424e8f\#0.raw
-rw-r--r-- 1 root root 1073741824 Apr 19 17:31 /persist/clear/volumes/...
du -h /persist/clear/volumes/9599c42f-42b0-4807-96a7-2d01cb424e8f\#0.raw
4.0K	/persist/clear/volumes/9599c42f-42b0-4807-96a7-2d01cb424e8f#0.raw

stat virt vs. allocated:
stat -c '%s' /persist/clear/volumes/9599c42f-42b0-4807-96a7-2d01cb424e8f\#0.raw
1073741824
stat -c '%b' /persist/clear/volumes/9599c42f-42b0-4807-96a7-2d01cb424e8f\#0.raw
8

Signed-off-by: Andrew Durbin <andrewd@zededa.com>
(cherry picked from commit 8be1999)
  • Loading branch information
andrewd-zededa committed Apr 29, 2024
1 parent 79c41d2 commit ef3b393
Showing 1 changed file with 27 additions and 2 deletions.
29 changes: 27 additions & 2 deletions pkg/pillar/diskmetrics/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"strconv"
"strings"
"syscall"

"github.com/containerd/containerd/mount"
"github.com/lf-edge/eve/pkg/pillar/base"
Expand All @@ -18,6 +19,17 @@ import (
"github.com/shirou/gopsutil/disk"
)

// StatAllocatedBytes returns the allocated size of a file in bytes
// This value will be less than fileInfo.Size() for thinly allocated files
func StatAllocatedBytes(path string) (uint64, error) {
var stat syscall.Stat_t
err := syscall.Stat(path, &stat)
if err != nil {
return uint64(0), err
}
return uint64(stat.Blocks * int64(stat.Blksize)), nil
}

// SizeFromDir performs a du -s equivalent operation.
// Didn't use ioutil.ReadDir and filepath.Walk because they sort (quick_sort) all files per directory
// which is an unnecessary costly operation.
Expand Down Expand Up @@ -57,8 +69,21 @@ func SizeFromDir(log *base.LogObject, dirname string) (uint64, error) {
log.Tracef("Dir %s size %d\n", filename, size)
totalUsed += size
} else {
log.Tracef("File %s Size %d\n", filename, location.Size())
totalUsed += uint64(location.Size())
// FileInfo.Size() returns the provisioned size
// Sparse files will have a smaller allocated size than provisioned
// Use full syscall.Stat_t to get the allocated size
allocatedBytes, err := StatAllocatedBytes(filename)
if err != nil {
log.Errorf("StatAllocatedBytes: %s failed %s treating as fully allocated\n", filename, err)
allocatedBytes = uint64(location.Size())
}
// Fully Allocated: don't use allocated bytes
// stat math of %b*%B as it will over-account space
if allocatedBytes >= uint64(location.Size()) {
allocatedBytes = uint64(location.Size())
}
log.Tracef("File %s Size %d\n", filename, allocatedBytes)
totalUsed += allocatedBytes
}
}
}
Expand Down

0 comments on commit ef3b393

Please sign in to comment.