From 79e0b9e69a7a7c90238d10429e7b4d61e5f452a2 Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Tue, 7 Mar 2017 21:25:40 +0100 Subject: [PATCH] Relax minio server start when disk threshold is reached and adds space check in FS (#3865) * fs: Rename tempObjPath variable in fsCreateFile() * fs/posix: Factor checkDiskFree() function * fs: Add disk free check in fsCreateFile() * posix: Move free disk check to createFile() * xl: Relax free disk check in POSIX initialization * fs: checkDiskFree checks for space to store data --- cmd/fs-v1-helpers.go | 14 +++++++++----- cmd/fs-v1.go | 46 ++------------------------------------------ cmd/posix.go | 30 ++++++++++++++--------------- 3 files changed, 25 insertions(+), 65 deletions(-) diff --git a/cmd/fs-v1-helpers.go b/cmd/fs-v1-helpers.go index a64ad4c71..85fcce0c1 100644 --- a/cmd/fs-v1-helpers.go +++ b/cmd/fs-v1-helpers.go @@ -228,20 +228,24 @@ func fsOpenFile(readPath string, offset int64) (io.ReadCloser, int64, error) { } // Creates a file and copies data from incoming reader. Staging buffer is used by io.CopyBuffer. -func fsCreateFile(tempObjPath string, reader io.Reader, buf []byte, fallocSize int64) (int64, error) { - if tempObjPath == "" || reader == nil || buf == nil { +func fsCreateFile(filePath string, reader io.Reader, buf []byte, fallocSize int64) (int64, error) { + if filePath == "" || reader == nil || buf == nil { return 0, traceError(errInvalidArgument) } - if err := checkPathLength(tempObjPath); err != nil { + if err := checkPathLength(filePath); err != nil { return 0, traceError(err) } - if err := mkdirAll(pathutil.Dir(tempObjPath), 0777); err != nil { + if err := mkdirAll(pathutil.Dir(filePath), 0777); err != nil { return 0, traceError(err) } - writer, err := os.OpenFile(preparePath(tempObjPath), os.O_CREATE|os.O_WRONLY, 0666) + if err := checkDiskFree(pathutil.Dir(filePath), fallocSize); err != nil { + return 0, traceError(err) + } + + writer, err := os.OpenFile(preparePath(filePath), os.O_CREATE|os.O_WRONLY, 0666) if err != nil { // File path cannot be verified since one of the parents is a file. if isSysErrNotDir(err) { diff --git a/cmd/fs-v1.go b/cmd/fs-v1.go index 238720b14..924fba23d 100644 --- a/cmd/fs-v1.go +++ b/cmd/fs-v1.go @@ -24,11 +24,9 @@ import ( "io" "os" "path/filepath" - "runtime" "sort" "syscall" - "github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/lock" "github.com/minio/sha256-simd" ) @@ -42,9 +40,6 @@ type fsObjects struct { // temporary transactions. fsUUID string - minFreeSpace int64 - minFreeInodes int64 - // FS rw pool. rwPool *fsIOPool @@ -139,10 +134,8 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) { // Initialize fs objects. fs := &fsObjects{ - fsPath: fsPath, - fsUUID: fsUUID, - minFreeSpace: fsMinFreeSpace, - minFreeInodes: fsMinFreeInodes, + fsPath: fsPath, + fsUUID: fsUUID, rwPool: &fsIOPool{ readersMap: make(map[string]*lock.RLockedFile), }, @@ -168,41 +161,6 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) { return fs, nil } -// checkDiskFree verifies if disk path has sufficient minimum free disk space and files. -func (fs fsObjects) checkDiskFree() (err error) { - // We don't validate disk space or inode utilization on windows. - // Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds. - if runtime.GOOS == globalWindowsOSName { - return nil - } - - var di disk.Info - di, err = getDiskInfo(preparePath(fs.fsPath)) - if err != nil { - return err - } - - // Remove 5% from free space for cumulative disk space used for journalling, inodes etc. - availableDiskSpace := float64(di.Free) * 0.95 - if int64(availableDiskSpace) <= fs.minFreeSpace { - return errDiskFull - } - - // Some filesystems do not implement a way to provide total inodes available, instead inodes - // are allocated based on available disk space. For example CephFS, StoreNext CVFS, AzureFile driver. - // Allow for the available disk to be separately validate and we will validate inodes only if - // total inodes are provided by the underlying filesystem. - if di.Files != 0 && di.FSType != "NFS" { - availableFiles := int64(di.Ffree) - if availableFiles <= fs.minFreeInodes { - return errDiskFull - } - } - - // Success. - return nil -} - // Should be called when process shuts down. func (fs fsObjects) Shutdown() error { // Cleanup and delete tmp uuid. diff --git a/cmd/posix.go b/cmd/posix.go index 12d9045ae..377aef646 100644 --- a/cmd/posix.go +++ b/cmd/posix.go @@ -40,11 +40,9 @@ const ( // posix - implements StorageAPI interface. type posix struct { - ioErrCount int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - diskPath string - minFreeSpace int64 - minFreeInodes int64 - pool sync.Pool + ioErrCount int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG + diskPath string + pool sync.Pool } // checkPathLength - returns error if given path name length more than 255 @@ -109,9 +107,7 @@ func newPosix(path string) (StorageAPI, error) { return nil, err } fs := &posix{ - diskPath: diskPath, - minFreeSpace: fsMinFreeSpace, - minFreeInodes: fsMinFreeInodes, + diskPath: diskPath, // 1MiB buffer pool for posix internal operations. pool: sync.Pool{ New: func() interface{} { @@ -133,9 +129,6 @@ func newPosix(path string) (StorageAPI, error) { return nil, err } } - if err = fs.checkDiskFree(); err != nil { - return nil, err - } return fs, nil } @@ -161,7 +154,7 @@ var ignoreDiskFreeOS = []string{ } // checkDiskFree verifies if disk path has sufficient minimum free disk space and files. -func (s *posix) checkDiskFree() (err error) { +func checkDiskFree(diskPath string, neededSpace int64) (err error) { // We don't validate disk space or inode utilization on windows. // Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds. // And StatFS is not supported by Go for solaris and netbsd. @@ -170,14 +163,14 @@ func (s *posix) checkDiskFree() (err error) { } var di disk.Info - di, err = getDiskInfo(preparePath(s.diskPath)) + di, err = getDiskInfo(preparePath(diskPath)) if err != nil { return err } // Remove 5% from free space for cumulative disk space used for journalling, inodes etc. availableDiskSpace := float64(di.Free) * 0.95 - if int64(availableDiskSpace) <= s.minFreeSpace { + if int64(availableDiskSpace) <= fsMinFreeSpace { return errDiskFull } @@ -187,11 +180,16 @@ func (s *posix) checkDiskFree() (err error) { // total inodes are provided by the underlying filesystem. if di.Files != 0 && di.FSType != "NFS" { availableFiles := int64(di.Ffree) - if availableFiles <= s.minFreeInodes { + if availableFiles <= fsMinFreeInodes { return errDiskFull } } + // Check if we have enough space to store data + if neededSpace > int64(availableDiskSpace) { + return errDiskFull + } + // Success. return nil } @@ -676,7 +674,7 @@ func (s *posix) PrepareFile(volume, path string, fileSize int64) (err error) { } // Validate if disk is indeed free. - if err = s.checkDiskFree(); err != nil { + if err = checkDiskFree(s.diskPath, fileSize); err != nil { return err }