mirror of
https://github.com/minio/minio.git
synced 2025-01-11 15:03:22 -05:00
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.
This commit is contained in:
parent
abf209b1dd
commit
25de775560
56
cmd/fs-v1.go
56
cmd/fs-v1.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
|
||||
|
30
cmd/posix.go
30
cmd/posix.go
@ -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
|
||||
}
|
||||
|
59
pkg/mountinfo/mountinfo_windows.go
Normal file
59
pkg/mountinfo/mountinfo_windows.go
Normal file
@ -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…
Reference in New Issue
Block a user