disable disk-usage when export is root mount path (#6091)

disk usage crawling is not needed when a tenant
is not sharing the same disk for multiple other
tenants. This PR adds an optimization when we
see a setup uses entire disk, we simply rely on
statvfs() to give us total usage.

This PR also additionally adds low priority
scheduling for usage check routine, such that
other go-routines blocked will be automatically
unblocked and prioritized before usage.
master
Harshavardhana 6 years ago committed by Dee Koder
parent abf209b1dd
commit 25de775560
  1. 56
      cmd/fs-v1.go
  2. 7
      cmd/http/server.go
  3. 30
      cmd/posix.go
  4. 24
      pkg/mountinfo/mountinfo_linux.go
  5. 9
      pkg/mountinfo/mountinfo_others.go
  6. 59
      pkg/mountinfo/mountinfo_windows.go

@ -34,6 +34,7 @@ import (
"github.com/minio/minio/pkg/lock"
"github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/mimedb"
"github.com/minio/minio/pkg/mountinfo"
"github.com/minio/minio/pkg/policy"
)
@ -64,6 +65,8 @@ type FSObjects struct {
// ListObjects pool management.
listPool *treeWalkPool
diskMount bool
appendFileMap map[string]*fsAppendFile
appendFileMapMu sync.Mutex
@ -138,6 +141,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
nsMutex: newNSLock(false),
listPool: newTreeWalkPool(globalLookupTimeout),
appendFileMap: make(map[string]*fsAppendFile),
diskMount: mountinfo.IsLikelyMountPoint(fsPath),
}
// Once the filesystem has initialized hold the read lock for
@ -156,7 +160,10 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
return nil, uiErrUnableToReadFromBackend(err).Msg("Unable to initialize policy system")
}
go fs.diskUsage(globalServiceDoneCh)
if !fs.diskMount {
go fs.diskUsage(globalServiceDoneCh)
}
go fs.cleanupStaleMultipartUploads(ctx, globalMultipartCleanupInterval, globalMultipartExpiry, globalServiceDoneCh)
// Return successfully initialized object layer.
@ -177,17 +184,29 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
defer ticker.Stop()
usageFn := func(ctx context.Context, entry string) error {
var fi os.FileInfo
var err error
if hasSuffix(entry, slashSeparator) {
fi, err = fsStatDir(ctx, entry)
} else {
fi, err = fsStatFile(ctx, entry)
if globalHTTPServer != nil {
// Any requests in progress, delay the usage.
for globalHTTPServer.GetRequestCount() > 0 {
time.Sleep(1 * time.Second)
}
}
if err != nil {
return err
select {
case <-doneCh:
return errWalkAbort
default:
var fi os.FileInfo
var err error
if hasSuffix(entry, slashSeparator) {
fi, err = fsStatDir(ctx, entry)
} else {
fi, err = fsStatFile(ctx, entry)
}
if err != nil {
return err
}
atomic.AddUint64(&fs.totalUsed, uint64(fi.Size()))
}
atomic.AddUint64(&fs.totalUsed, uint64(fi.Size()))
return nil
}
@ -216,6 +235,13 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
var usage uint64
usageFn = func(ctx context.Context, entry string) error {
if globalHTTPServer != nil {
// Any requests in progress, delay the usage.
for globalHTTPServer.GetRequestCount() > 0 {
time.Sleep(1 * time.Second)
}
}
var fi os.FileInfo
var err error
if hasSuffix(entry, slashSeparator) {
@ -243,8 +269,16 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
// StorageInfo - returns underlying storage statistics.
func (fs *FSObjects) StorageInfo(ctx context.Context) StorageInfo {
di, err := getDiskInfo(fs.fsPath)
if err != nil {
return StorageInfo{}
}
used := di.Total - di.Free
if !fs.diskMount {
used = atomic.LoadUint64(&fs.totalUsed)
}
storageInfo := StorageInfo{
Used: atomic.LoadUint64(&fs.totalUsed),
Used: used,
}
storageInfo.Backend.Type = FS
return storageInfo

@ -59,7 +59,12 @@ type Server struct {
listenerMutex *sync.Mutex // to guard 'listener' field.
listener *httpListener // HTTP listener for all 'Addrs' field.
inShutdown uint32 // indicates whether the server is in shutdown or not
requestCount int32 // counter holds no. of request in process.
requestCount int32 // counter holds no. of request in progress.
}
// GetRequestCount - returns number of request in progress.
func (srv *Server) GetRequestCount() int32 {
return atomic.LoadInt32(&srv.requestCount)
}
// Start - start HTTP server

@ -34,6 +34,7 @@ import (
humanize "github.com/dustin/go-humanize"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/disk"
"github.com/minio/minio/pkg/mountinfo"
)
const (
@ -69,6 +70,8 @@ type posix struct {
pool sync.Pool
connected bool
diskMount bool // indicates if the path is an actual mount.
// Disk usage metrics
stopUsageCh chan struct{}
}
@ -183,9 +186,12 @@ func newPosix(path string) (*posix, error) {
},
},
stopUsageCh: make(chan struct{}),
diskMount: mountinfo.IsLikelyMountPoint(path),
}
go p.diskUsage(globalServiceDoneCh)
if !p.diskMount {
go p.diskUsage(globalServiceDoneCh)
}
// Success.
return p, nil
@ -293,10 +299,14 @@ func (s *posix) DiskInfo() (info DiskInfo, err error) {
if err != nil {
return info, err
}
used := di.Total - di.Free
if !s.diskMount {
used = atomic.LoadUint64(&s.totalUsed)
}
return DiskInfo{
Total: di.Total,
Free: di.Free,
Used: atomic.LoadUint64(&s.totalUsed),
Used: used,
}, nil
}
@ -336,7 +346,16 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
defer ticker.Stop()
usageFn := func(ctx context.Context, entry string) error {
if globalHTTPServer != nil {
// Any requests in progress, delay the usage.
for globalHTTPServer.GetRequestCount() > 0 {
time.Sleep(1 * time.Second)
}
}
select {
case <-doneCh:
return errWalkAbort
case <-s.stopUsageCh:
return errWalkAbort
default:
@ -377,6 +396,13 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
var usage uint64
usageFn = func(ctx context.Context, entry string) error {
if globalHTTPServer != nil {
// Any requests in progress, delay the usage.
for globalHTTPServer.GetRequestCount() > 0 {
time.Sleep(1 * time.Second)
}
}
select {
case <-s.stopUsageCh:
return errWalkAbort

@ -1,7 +1,7 @@
// +build linux
/*
* Minio Cloud Storage, (C) 2017 Minio, Inc.
* Minio Cloud Storage, (C) 2017, 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,6 +26,7 @@ import (
"path/filepath"
"strconv"
"strings"
"syscall"
)
const (
@ -35,6 +36,27 @@ const (
procMountsPath = "/proc/mounts"
)
// IsLikelyMountPoint determines if a directory is a mountpoint.
// It is fast but not necessarily ALWAYS correct. If the path is in fact
// a bind mount from one part of a mount to another it will not be detected.
// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyMountPoint("/tmp/b")
// will return false. When in fact /tmp/b is a mount point. If this situation
// if of interest to you, don't use this function...
func IsLikelyMountPoint(file string) bool {
stat, err := os.Stat(file)
if err != nil {
return false
}
rootStat, err := os.Lstat(filepath.Dir(strings.TrimSuffix(file, "/")))
if err != nil {
return false
}
// If the directory has a different device as parent, then it is a mountpoint.
return stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev
}
// CheckCrossDevice - check if any list of paths has any sub-mounts at /proc/mounts.
func CheckCrossDevice(absPaths []string) error {
return checkCrossDevice(absPaths, procMountsPath)

@ -1,7 +1,7 @@
// +build !linux
// +build !linux,!windows
/*
* Minio Cloud Storage, (C) 2017 Minio, Inc.
* Minio Cloud Storage, (C) 2017, 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,3 +23,8 @@ package mountinfo
func CheckCrossDevice(paths []string) error {
return nil
}
// IsLikelyMountPoint determines if a directory is a mountpoint.
func IsLikelyMountPoint(file string) bool {
return false
}

@ -0,0 +1,59 @@
// +build windows
/*
* Minio Cloud Storage, (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mountinfo
import (
"os"
)
// CheckCrossDevice - check if any input path has multiple sub-mounts.
// this is a dummy function and returns nil for now.
func CheckCrossDevice(paths []string) error {
return nil
}
// fileExists checks if specified file exists.
func fileExists(filename string) (bool, error) {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return false, nil
} else if err != nil {
return false, err
}
return true, nil
}
// IsLikelyMountPoint determines if a directory is a mountpoint.
func IsLikelyMountPoint(file string) bool {
stat, err := os.Lstat(file)
if err != nil {
return false
}
// If current file is a symlink, then it is a mountpoint.
if stat.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(file)
if err != nil {
return false
}
exists, _ := fileExists(target)
return exists
}
return false
}
Loading…
Cancel
Save