diff --git a/bolt_linux.go b/bolt_linux.go index 7e3e539..e9d1c90 100644 --- a/bolt_linux.go +++ b/bolt_linux.go @@ -1,13 +1,12 @@ package bolt import ( - "os" "syscall" ) var odirect = syscall.O_DIRECT // fdatasync flushes written data to a file descriptor. -func fdatasync(f *os.File) error { - return syscall.Fdatasync(int(f.Fd())) +func fdatasync(db *DB) error { + return syscall.Fdatasync(int(db.file.Fd())) } diff --git a/bolt_openbsd.go b/bolt_openbsd.go new file mode 100644 index 0000000..7c1bef1 --- /dev/null +++ b/bolt_openbsd.go @@ -0,0 +1,29 @@ +package bolt + +import ( + "syscall" + "unsafe" +) + +const ( + msAsync = 1 << iota // perform asynchronous writes + msSync // perform synchronous writes + msInvalidate // invalidate cached data +) + +var odirect int + +func msync(db *DB) error { + _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(db.data)), uintptr(db.datasz), msInvalidate) + if errno != 0 { + return errno + } + return nil +} + +func fdatasync(db *DB) error { + if db.data != nil { + return msync(db) + } + return db.file.Sync() +} diff --git a/bolt_windows.go b/bolt_windows.go index 6b6e419..c8539d4 100644 --- a/bolt_windows.go +++ b/bolt_windows.go @@ -11,8 +11,8 @@ import ( var odirect int // fdatasync flushes written data to a file descriptor. -func fdatasync(f *os.File) error { - return f.Sync() +func fdatasync(db *DB) error { + return db.file.Sync() } // flock acquires an advisory lock on a file descriptor. diff --git a/boltsync_unix.go b/boltsync_unix.go index 3c54dd5..8db8977 100644 --- a/boltsync_unix.go +++ b/boltsync_unix.go @@ -1,14 +1,10 @@ -// +build !windows,!plan9,!linux +// +build !windows,!plan9,!linux,!openbsd package bolt -import ( - "os" -) - var odirect int // fdatasync flushes written data to a file descriptor. -func fdatasync(f *os.File) error { - return f.Sync() +func fdatasync(db *DB) error { + return db.file.Sync() } diff --git a/db.go b/db.go index 7364454..6c45736 100644 --- a/db.go +++ b/db.go @@ -4,6 +4,7 @@ import ( "fmt" "hash/fnv" "os" + "runtime" "runtime/debug" "strings" "sync" @@ -23,6 +24,12 @@ const version = 2 // Represents a marker value to indicate that a file is a Bolt DB. const magic uint32 = 0xED0CDAED +// IgnoreNoSync specifies whether the NoSync field of a DB is ignored when +// syncing changes to a file. This is required as some operating systems, +// such as OpenBSD, do not have a unified buffer cache (UBC) and writes +// must be synchronzied using the msync(2) syscall. +const IgnoreNoSync = runtime.GOOS == "openbsd" + // DB represents a collection of buckets persisted to a file on disk. // All data access is performed through transactions which can be obtained through the DB. // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called. @@ -39,6 +46,9 @@ type DB struct { // a system failure or database corruption. Do not set this flag for // normal use. // + // If the package global IgnoreNoSync constant is true, this value is + // ignored. See the comment on that constant for more details. + // // THIS IS UNSAFE. PLEASE USE WITH CAUTION. NoSync bool @@ -263,7 +273,7 @@ func (db *DB) init() error { if _, err := db.ops.writeAt(buf, 0); err != nil { return err } - if err := fdatasync(db.file); err != nil { + if err := fdatasync(db); err != nil { return err } diff --git a/tx.go b/tx.go index 62b9be6..c041d73 100644 --- a/tx.go +++ b/tx.go @@ -425,8 +425,8 @@ func (tx *Tx) write() error { // Update statistics. tx.stats.Write++ } - if !tx.db.NoSync { - if err := fdatasync(tx.db.file); err != nil { + if !tx.db.NoSync || IgnoreNoSync { + if err := fdatasync(tx.db); err != nil { return err } } @@ -448,8 +448,8 @@ func (tx *Tx) writeMeta() error { if _, err := tx.db.ops.writeAt(buf, int64(p.id)*int64(tx.db.pageSize)); err != nil { return err } - if !tx.db.NoSync { - if err := fdatasync(tx.db.file); err != nil { + if !tx.db.NoSync || IgnoreNoSync { + if err := fdatasync(tx.db); err != nil { return err } }