From 3d0a0b3785137d6eb6925745982bf59339921757 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Thu, 4 Jun 2020 13:13:00 +0300 Subject: [PATCH] lib/fs: optimize MustGetFreeSpace performance by caching the results for up to 2 seconds --- app/vmselect/netstorage/tmp_blocks_file.go | 2 +- lib/fs/fs.go | 31 ++++++++++++++++++++++ lib/storage/partition.go | 31 +--------------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/app/vmselect/netstorage/tmp_blocks_file.go b/app/vmselect/netstorage/tmp_blocks_file.go index 947870023..0fe754a43 100644 --- a/app/vmselect/netstorage/tmp_blocks_file.go +++ b/app/vmselect/netstorage/tmp_blocks_file.go @@ -87,7 +87,7 @@ func (addr tmpBlockAddr) String() string { var ( tmpBlocksFilesCreated = metrics.NewCounter(`vm_tmp_blocks_files_created_total`) - _ = metrics.NewGauge(`vm_tmp_blocks_files_directory_free_bytes`, func() float64 { + _ = metrics.NewGauge(`vm_tmp_blocks_files_directory_free_bytes`, func() float64 { return float64(fs.MustGetFreeSpace(tmpBlocksDir)) }) ) diff --git a/lib/fs/fs.go b/lib/fs/fs.go index c8ef483b4..a911e9c1c 100644 --- a/lib/fs/fs.go +++ b/lib/fs/fs.go @@ -6,8 +6,10 @@ import ( "os" "path/filepath" "regexp" + "sync" "sync/atomic" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" "github.com/VictoriaMetrics/VictoriaMetrics/lib/filestream" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "golang.org/x/sys/unix" @@ -296,6 +298,35 @@ func CreateFlockFile(dir string) (*os.File, error) { // MustGetFreeSpace returns free space for the given directory path. func MustGetFreeSpace(path string) uint64 { + // Try obtaining cached value at first. + freeSpaceMapLock.Lock() + defer freeSpaceMapLock.Unlock() + + e, ok := freeSpaceMap[path] + if ok && fasttime.UnixTimestamp()-e.updateTime < 2 { + // Fast path - the entry is fresh. + return e.freeSpace + } + + // Slow path. + // Determine the amount of free space at path. + e.freeSpace = mustGetFreeSpace(path) + e.updateTime = fasttime.UnixTimestamp() + freeSpaceMap[path] = e + return e.freeSpace +} + +var ( + freeSpaceMap = make(map[string]freeSpaceEntry) + freeSpaceMapLock sync.Mutex +) + +type freeSpaceEntry struct { + updateTime uint64 + freeSpace uint64 +} + +func mustGetFreeSpace(path string) uint64 { d, err := os.Open(path) if err != nil { logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err) diff --git a/lib/storage/partition.go b/lib/storage/partition.go index 37635771b..939884cce 100644 --- a/lib/storage/partition.go +++ b/lib/storage/partition.go @@ -927,7 +927,7 @@ func (pt *partition) partsMerger(mergerFunc func(isFinal bool) error) error { } func maxRowsByPath(path string) uint64 { - freeSpace := mustGetFreeDiskSpace(path) + freeSpace := fs.MustGetFreeSpace(path) // Calculate the maximum number of rows in the output merge part // by dividing the freeSpace by the number of concurrent @@ -942,35 +942,6 @@ func maxRowsByPath(path string) uint64 { return maxRows } -func mustGetFreeDiskSpace(path string) uint64 { - // Try obtaining the cache value at first. - freeSpaceMapLock.Lock() - defer freeSpaceMapLock.Unlock() - - e, ok := freeSpaceMap[path] - if ok && fasttime.UnixTimestamp()-e.updateTime < 2 { - // Fast path - the entry is fresh. - return e.freeSpace - } - - // Slow path. - // Determine the amount of free space on bigPartsPath. - e.freeSpace = fs.MustGetFreeSpace(path) - e.updateTime = fasttime.UnixTimestamp() - freeSpaceMap[path] = e - return e.freeSpace -} - -var ( - freeSpaceMap = make(map[string]freeSpaceEntry) - freeSpaceMapLock sync.Mutex -) - -type freeSpaceEntry struct { - updateTime uint64 - freeSpace uint64 -} - func (pt *partition) mergeBigParts(isFinal bool) error { bigMergeConcurrencyLimitCh <- struct{}{} defer func() {