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
This commit is contained in:
Anis Elleuch 2017-03-07 21:25:40 +01:00 committed by Harshavardhana
parent 29ff9674a0
commit 79e0b9e69a
3 changed files with 25 additions and 65 deletions

View File

@ -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. // 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) { func fsCreateFile(filePath string, reader io.Reader, buf []byte, fallocSize int64) (int64, error) {
if tempObjPath == "" || reader == nil || buf == nil { if filePath == "" || reader == nil || buf == nil {
return 0, traceError(errInvalidArgument) return 0, traceError(errInvalidArgument)
} }
if err := checkPathLength(tempObjPath); err != nil { if err := checkPathLength(filePath); err != nil {
return 0, traceError(err) 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) 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 { if err != nil {
// File path cannot be verified since one of the parents is a file. // File path cannot be verified since one of the parents is a file.
if isSysErrNotDir(err) { if isSysErrNotDir(err) {

View File

@ -24,11 +24,9 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"syscall" "syscall"
"github.com/minio/minio/pkg/disk"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
"github.com/minio/sha256-simd" "github.com/minio/sha256-simd"
) )
@ -42,9 +40,6 @@ type fsObjects struct {
// temporary transactions. // temporary transactions.
fsUUID string fsUUID string
minFreeSpace int64
minFreeInodes int64
// FS rw pool. // FS rw pool.
rwPool *fsIOPool rwPool *fsIOPool
@ -141,8 +136,6 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) {
fs := &fsObjects{ fs := &fsObjects{
fsPath: fsPath, fsPath: fsPath,
fsUUID: fsUUID, fsUUID: fsUUID,
minFreeSpace: fsMinFreeSpace,
minFreeInodes: fsMinFreeInodes,
rwPool: &fsIOPool{ rwPool: &fsIOPool{
readersMap: make(map[string]*lock.RLockedFile), readersMap: make(map[string]*lock.RLockedFile),
}, },
@ -168,41 +161,6 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) {
return fs, nil 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. // Should be called when process shuts down.
func (fs fsObjects) Shutdown() error { func (fs fsObjects) Shutdown() error {
// Cleanup and delete tmp uuid. // Cleanup and delete tmp uuid.

View File

@ -42,8 +42,6 @@ const (
type posix struct { type posix struct {
ioErrCount int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG ioErrCount int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
diskPath string diskPath string
minFreeSpace int64
minFreeInodes int64
pool sync.Pool pool sync.Pool
} }
@ -110,8 +108,6 @@ func newPosix(path string) (StorageAPI, error) {
} }
fs := &posix{ fs := &posix{
diskPath: diskPath, diskPath: diskPath,
minFreeSpace: fsMinFreeSpace,
minFreeInodes: fsMinFreeInodes,
// 1MiB buffer pool for posix internal operations. // 1MiB buffer pool for posix internal operations.
pool: sync.Pool{ pool: sync.Pool{
New: func() interface{} { New: func() interface{} {
@ -133,9 +129,6 @@ func newPosix(path string) (StorageAPI, error) {
return nil, err return nil, err
} }
} }
if err = fs.checkDiskFree(); err != nil {
return nil, err
}
return fs, nil return fs, nil
} }
@ -161,7 +154,7 @@ var ignoreDiskFreeOS = []string{
} }
// checkDiskFree verifies if disk path has sufficient minimum free disk space and files. // 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. // We don't validate disk space or inode utilization on windows.
// Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds. // Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds.
// And StatFS is not supported by Go for solaris and netbsd. // 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 var di disk.Info
di, err = getDiskInfo(preparePath(s.diskPath)) di, err = getDiskInfo(preparePath(diskPath))
if err != nil { if err != nil {
return err return err
} }
// Remove 5% from free space for cumulative disk space used for journalling, inodes etc. // Remove 5% from free space for cumulative disk space used for journalling, inodes etc.
availableDiskSpace := float64(di.Free) * 0.95 availableDiskSpace := float64(di.Free) * 0.95
if int64(availableDiskSpace) <= s.minFreeSpace { if int64(availableDiskSpace) <= fsMinFreeSpace {
return errDiskFull return errDiskFull
} }
@ -187,11 +180,16 @@ func (s *posix) checkDiskFree() (err error) {
// total inodes are provided by the underlying filesystem. // total inodes are provided by the underlying filesystem.
if di.Files != 0 && di.FSType != "NFS" { if di.Files != 0 && di.FSType != "NFS" {
availableFiles := int64(di.Ffree) availableFiles := int64(di.Ffree)
if availableFiles <= s.minFreeInodes { if availableFiles <= fsMinFreeInodes {
return errDiskFull return errDiskFull
} }
} }
// Check if we have enough space to store data
if neededSpace > int64(availableDiskSpace) {
return errDiskFull
}
// Success. // Success.
return nil return nil
} }
@ -676,7 +674,7 @@ func (s *posix) PrepareFile(volume, path string, fileSize int64) (err error) {
} }
// Validate if disk is indeed free. // Validate if disk is indeed free.
if err = s.checkDiskFree(); err != nil { if err = checkDiskFree(s.diskPath, fileSize); err != nil {
return err return err
} }