diff --git a/acp.go b/acp.go index e0dde53..5028bb4 100644 --- a/acp.go +++ b/acp.go @@ -9,9 +9,10 @@ import ( type Copyer struct { *option - running sync.WaitGroup - eventCh chan Event - getDevice func(in string) string + running sync.WaitGroup + eventCh chan Event + getDevice func(in string) string + getDiskUsageCache func(mountPoint string) *diskUsageCache } func New(ctx context.Context, opts ...Option) (*Copyer, error) { @@ -35,6 +36,9 @@ func New(ctx context.Context, opts ...Option) (*Copyer, error) { option: opt, eventCh: make(chan Event, 128), getDevice: getDevice, + getDiskUsageCache: Cache(func(mountPoint string) *diskUsageCache { + return newDiskUsageCache(mountPoint, defaultDiskUsageFreshInterval) + }), } c.running.Add(1) diff --git a/cache.go b/cache.go index 7da638a..0df7179 100644 --- a/cache.go +++ b/cache.go @@ -1,8 +1,8 @@ package acp -func Cache[i comparable, o any](f func(in i) o) func(in i) o { - cache := make(map[i]o, 0) - return func(in i) o { +func Cache[K comparable, V any](f func(in K) V) func(in K) V { + cache := make(map[K]V, 0) + return func(in K) V { cached, has := cache[in] if has { return cached diff --git a/copy.go b/copy.go index e9bb9a8..175d2de 100644 --- a/copy.go +++ b/copy.go @@ -16,7 +16,6 @@ import ( mapset "github.com/deckarep/golang-set/v2" sha256 "github.com/minio/sha256-simd" "github.com/samber/lo" - "github.com/samuelncui/godf" ) const ( @@ -117,14 +116,12 @@ func (c *Copyer) write(ctx context.Context, job *writeJob, ch chan<- *baseJob, c continue } - diskUsage, err := godf.NewDiskUsage(dev) - if err != nil { - job.fail(target, fmt.Errorf("read disk usage fail, dev= '%s', %w", dev, err)) - continue - } - if int64(diskUsage.Free()) < job.size { - noSpaceDevices.Add(dev) - job.fail(target, fmt.Errorf("%w, want= %d have= %d", ErrTargetNoSpace, job.size, diskUsage.Free())) + if err := c.getDiskUsageCache(dev).check(job.size); err != nil { + if errors.Is(err, ErrTargetNoSpace) { + noSpaceDevices.Add(dev) + } + + job.fail(target, fmt.Errorf("check disk usage have error, %w", err)) continue } diff --git a/disk_usage.go b/disk_usage.go new file mode 100644 index 0000000..fb2f46a --- /dev/null +++ b/disk_usage.go @@ -0,0 +1,51 @@ +package acp + +import ( + "fmt" + "sync" + + "github.com/samuelncui/godf" +) + +const ( + defaultDiskUsageFreshInterval = 1024 * 1024 * 1024 * 2 +) + +type diskUsageCache struct { + mountPoint string + freshInterval int64 + + lock sync.Mutex + freeSpace int64 + used int64 +} + +func newDiskUsageCache(mountPoint string, freshInterval int64) *diskUsageCache { + return &diskUsageCache{ + mountPoint: mountPoint, + freshInterval: freshInterval, + } +} + +func (m *diskUsageCache) check(need int64) error { + m.lock.Lock() + defer m.lock.Unlock() + + m.used += need + if m.used <= m.freeSpace && m.used < m.freshInterval { + return nil + } + + usage, err := godf.NewDiskUsage(m.mountPoint) + if err != nil { + return fmt.Errorf("get disk usage fail, mount_point= %s", m.mountPoint) + } + + m.freeSpace = int64(usage.Available()) + m.used = need + if m.used > m.freeSpace { + return fmt.Errorf("%w, want= %d have= %d", ErrTargetNoSpace, m.used, m.freeSpace) + } + + return nil +}