fileutil: Sync on HFS/OSX needs to be handled differently.

A call file.Sync on OSX doesn't guarantee actual persistence on
physical drive media as the data can be cached in physical drive's
buffers. Hence calls to file.Sync need to be replaced with
fcntl(F_FULLFSYNC).
This commit is contained in:
Ajit Yagaty
2016-04-18 00:25:21 -07:00
parent ea6a747fc1
commit 8b6de5f85d
7 changed files with 59 additions and 8 deletions

View File

@ -28,6 +28,7 @@ import (
"github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/etcdserver/membership" "github.com/coreos/etcd/etcdserver/membership"
"github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft" "github.com/coreos/etcd/raft"
"github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/raft/raftpb"
@ -121,7 +122,7 @@ func snapshotSaveCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitInterrupted, rerr) ExitWithError(ExitInterrupted, rerr)
} }
f.Sync() fileutil.Fsync(f)
if rerr := os.Rename(partpath, path); rerr != nil { if rerr := os.Rename(partpath, path); rerr != nil {
exiterr := fmt.Errorf("could not rename %s to %s (%v)", partpath, path, rerr) exiterr := fmt.Errorf("could not rename %s to %s (%v)", partpath, path, rerr)

View File

@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// +build !linux // +build !linux,!darwin
package fileutil package fileutil
import "os" import "os"
// Fdatasync is similar to fsync(), but does not flush modified metadata // Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
// unless that metadata is needed in order to allow a subsequent data retrieval func Fsync(f *os.File) error {
// to be correctly handled. return f.Sync()
}
// Fdatasync is a wrapper around file.Sync(). Special handling is needed on linux platform.
func Fdatasync(f *os.File) error { func Fdatasync(f *os.File) error {
return f.Sync() return f.Sync()
} }

View File

@ -0,0 +1,40 @@
// Copyright 2016 CoreOS, 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.
// +build darwin
package fileutil
import (
"os"
"syscall"
)
// Fsync on HFS/OSX flushes the data on to the physical drive but the drive
// may not write it to the persistent media for quite sometime and it may be
// written in out-of-order sequence. Using F_FULLFSYNC ensures that the
// physical drive's buffer will also get flushed to the media.
func Fsync(f *os.File) error {
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_FULLFSYNC), uintptr(0))
if errno == 0 {
return nil
}
return errno
}
// Fdatasync on darwin platform invokes fcntl(F_FULLFSYNC) for actual persistence
// on physical drive media.
func Fdatasync(f *os.File) error {
return Fsync(f)
}

View File

@ -21,6 +21,11 @@ import (
"syscall" "syscall"
) )
// Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
func Fsync(f *os.File) error {
return f.Sync()
}
// Fdatasync is similar to fsync(), but does not flush modified metadata // Fdatasync is similar to fsync(), but does not flush modified metadata
// unless that metadata is needed in order to allow a subsequent data retrieval // unless that metadata is needed in order to allow a subsequent data retrieval
// to be correctly handled. // to be correctly handled.

View File

@ -17,6 +17,8 @@ package ioutil
import ( import (
"io" "io"
"os" "os"
"github.com/coreos/etcd/pkg/fileutil"
) )
// WriteAndSyncFile behaves just like ioutil.WriteFile in the standard library, // WriteAndSyncFile behaves just like ioutil.WriteFile in the standard library,
@ -32,7 +34,7 @@ func WriteAndSyncFile(filename string, data []byte, perm os.FileMode) error {
err = io.ErrShortWrite err = io.ErrShortWrite
} }
if err == nil { if err == nil {
err = f.Sync() err = fileutil.Fsync(f)
} }
if err1 := f.Close(); err == nil { if err1 := f.Close(); err == nil {
err = err1 err = err1

View File

@ -34,7 +34,7 @@ func (s *Snapshotter) SaveDBFrom(r io.Reader, id uint64) error {
var n int64 var n int64
n, err = io.Copy(f, r) n, err = io.Copy(f, r)
if err == nil { if err == nil {
err = f.Sync() err = fileutil.Fsync(f)
} }
f.Close() f.Close()
if err != nil { if err != nil {

View File

@ -78,7 +78,7 @@ func Repair(dirpath string) bool {
plog.Errorf("could not repair %v, failed to truncate file", f.Name()) plog.Errorf("could not repair %v, failed to truncate file", f.Name())
return false return false
} }
if err = f.Sync(); err != nil { if err = fileutil.Fsync(f); err != nil {
plog.Errorf("could not repair %v, failed to sync file", f.Name()) plog.Errorf("could not repair %v, failed to sync file", f.Name())
return false return false
} }