mirror of
https://github.com/minio/minio.git
synced 2025-04-28 05:38:00 -04: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
36
cmd/fs-v1.go
36
cmd/fs-v1.go
@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/minio/minio/pkg/lock"
|
"github.com/minio/minio/pkg/lock"
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
"github.com/minio/minio/pkg/mimedb"
|
"github.com/minio/minio/pkg/mimedb"
|
||||||
|
"github.com/minio/minio/pkg/mountinfo"
|
||||||
"github.com/minio/minio/pkg/policy"
|
"github.com/minio/minio/pkg/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,6 +65,8 @@ type FSObjects struct {
|
|||||||
// ListObjects pool management.
|
// ListObjects pool management.
|
||||||
listPool *treeWalkPool
|
listPool *treeWalkPool
|
||||||
|
|
||||||
|
diskMount bool
|
||||||
|
|
||||||
appendFileMap map[string]*fsAppendFile
|
appendFileMap map[string]*fsAppendFile
|
||||||
appendFileMapMu sync.Mutex
|
appendFileMapMu sync.Mutex
|
||||||
|
|
||||||
@ -138,6 +141,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
|||||||
nsMutex: newNSLock(false),
|
nsMutex: newNSLock(false),
|
||||||
listPool: newTreeWalkPool(globalLookupTimeout),
|
listPool: newTreeWalkPool(globalLookupTimeout),
|
||||||
appendFileMap: make(map[string]*fsAppendFile),
|
appendFileMap: make(map[string]*fsAppendFile),
|
||||||
|
diskMount: mountinfo.IsLikelyMountPoint(fsPath),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once the filesystem has initialized hold the read lock for
|
// 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")
|
return nil, uiErrUnableToReadFromBackend(err).Msg("Unable to initialize policy system")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !fs.diskMount {
|
||||||
go fs.diskUsage(globalServiceDoneCh)
|
go fs.diskUsage(globalServiceDoneCh)
|
||||||
|
}
|
||||||
|
|
||||||
go fs.cleanupStaleMultipartUploads(ctx, globalMultipartCleanupInterval, globalMultipartExpiry, globalServiceDoneCh)
|
go fs.cleanupStaleMultipartUploads(ctx, globalMultipartCleanupInterval, globalMultipartExpiry, globalServiceDoneCh)
|
||||||
|
|
||||||
// Return successfully initialized object layer.
|
// Return successfully initialized object layer.
|
||||||
@ -177,6 +184,17 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
|
|||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
usageFn := func(ctx context.Context, entry string) error {
|
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
|
||||||
|
default:
|
||||||
var fi os.FileInfo
|
var fi os.FileInfo
|
||||||
var err error
|
var err error
|
||||||
if hasSuffix(entry, slashSeparator) {
|
if hasSuffix(entry, slashSeparator) {
|
||||||
@ -188,6 +206,7 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
atomic.AddUint64(&fs.totalUsed, uint64(fi.Size()))
|
atomic.AddUint64(&fs.totalUsed, uint64(fi.Size()))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +235,13 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
|
|||||||
|
|
||||||
var usage uint64
|
var usage uint64
|
||||||
usageFn = func(ctx context.Context, entry string) error {
|
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 fi os.FileInfo
|
||||||
var err error
|
var err error
|
||||||
if hasSuffix(entry, slashSeparator) {
|
if hasSuffix(entry, slashSeparator) {
|
||||||
@ -243,8 +269,16 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
|
|||||||
|
|
||||||
// StorageInfo - returns underlying storage statistics.
|
// StorageInfo - returns underlying storage statistics.
|
||||||
func (fs *FSObjects) StorageInfo(ctx context.Context) StorageInfo {
|
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{
|
storageInfo := StorageInfo{
|
||||||
Used: atomic.LoadUint64(&fs.totalUsed),
|
Used: used,
|
||||||
}
|
}
|
||||||
storageInfo.Backend.Type = FS
|
storageInfo.Backend.Type = FS
|
||||||
return storageInfo
|
return storageInfo
|
||||||
|
@ -59,7 +59,12 @@ type Server struct {
|
|||||||
listenerMutex *sync.Mutex // to guard 'listener' field.
|
listenerMutex *sync.Mutex // to guard 'listener' field.
|
||||||
listener *httpListener // HTTP listener for all 'Addrs' field.
|
listener *httpListener // HTTP listener for all 'Addrs' field.
|
||||||
inShutdown uint32 // indicates whether the server is in shutdown or not
|
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
|
// Start - start HTTP server
|
||||||
|
28
cmd/posix.go
28
cmd/posix.go
@ -34,6 +34,7 @@ import (
|
|||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/disk"
|
"github.com/minio/minio/pkg/disk"
|
||||||
|
"github.com/minio/minio/pkg/mountinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -69,6 +70,8 @@ type posix struct {
|
|||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
connected bool
|
connected bool
|
||||||
|
|
||||||
|
diskMount bool // indicates if the path is an actual mount.
|
||||||
|
|
||||||
// Disk usage metrics
|
// Disk usage metrics
|
||||||
stopUsageCh chan struct{}
|
stopUsageCh chan struct{}
|
||||||
}
|
}
|
||||||
@ -183,9 +186,12 @@ func newPosix(path string) (*posix, error) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
stopUsageCh: make(chan struct{}),
|
stopUsageCh: make(chan struct{}),
|
||||||
|
diskMount: mountinfo.IsLikelyMountPoint(path),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !p.diskMount {
|
||||||
go p.diskUsage(globalServiceDoneCh)
|
go p.diskUsage(globalServiceDoneCh)
|
||||||
|
}
|
||||||
|
|
||||||
// Success.
|
// Success.
|
||||||
return p, nil
|
return p, nil
|
||||||
@ -293,10 +299,14 @@ func (s *posix) DiskInfo() (info DiskInfo, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return info, err
|
return info, err
|
||||||
}
|
}
|
||||||
|
used := di.Total - di.Free
|
||||||
|
if !s.diskMount {
|
||||||
|
used = atomic.LoadUint64(&s.totalUsed)
|
||||||
|
}
|
||||||
return DiskInfo{
|
return DiskInfo{
|
||||||
Total: di.Total,
|
Total: di.Total,
|
||||||
Free: di.Free,
|
Free: di.Free,
|
||||||
Used: atomic.LoadUint64(&s.totalUsed),
|
Used: used,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -336,7 +346,16 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
|
|||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
usageFn := func(ctx context.Context, entry string) error {
|
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 {
|
select {
|
||||||
|
case <-doneCh:
|
||||||
|
return errWalkAbort
|
||||||
case <-s.stopUsageCh:
|
case <-s.stopUsageCh:
|
||||||
return errWalkAbort
|
return errWalkAbort
|
||||||
default:
|
default:
|
||||||
@ -377,6 +396,13 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
|
|||||||
|
|
||||||
var usage uint64
|
var usage uint64
|
||||||
usageFn = func(ctx context.Context, entry string) error {
|
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 {
|
select {
|
||||||
case <-s.stopUsageCh:
|
case <-s.stopUsageCh:
|
||||||
return errWalkAbort
|
return errWalkAbort
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// +build linux
|
// +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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -26,6 +26,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -35,6 +36,27 @@ const (
|
|||||||
procMountsPath = "/proc/mounts"
|
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.
|
// CheckCrossDevice - check if any list of paths has any sub-mounts at /proc/mounts.
|
||||||
func CheckCrossDevice(absPaths []string) error {
|
func CheckCrossDevice(absPaths []string) error {
|
||||||
return checkCrossDevice(absPaths, procMountsPath)
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 {
|
func CheckCrossDevice(paths []string) error {
|
||||||
return nil
|
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…
x
Reference in New Issue
Block a user