mirror of https://github.com/hak5/bolt.git
use a shared lock in read-only mode
https://github.com/boltdb/bolt/pull/371#issuecomment-103119486master
parent
019bf5b010
commit
fda75748b5
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// flock acquires an advisory lock on a file descriptor.
|
||||
func flock(f *os.File, timeout time.Duration) error {
|
||||
func flock(f *os.File, exclusive bool, timeout time.Duration) error {
|
||||
var t time.Time
|
||||
for {
|
||||
// If we're beyond our timeout then return an error.
|
||||
|
@ -21,9 +21,13 @@ func flock(f *os.File, timeout time.Duration) error {
|
|||
} else if timeout > 0 && time.Since(t) > timeout {
|
||||
return ErrTimeout
|
||||
}
|
||||
flag := syscall.LOCK_SH
|
||||
if exclusive {
|
||||
flag = syscall.LOCK_EX
|
||||
}
|
||||
|
||||
// Otherwise attempt to obtain an exclusive lock.
|
||||
err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
err := syscall.Flock(int(f.Fd()), flag|syscall.LOCK_NB)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if err != syscall.EWOULDBLOCK {
|
||||
|
|
|
@ -16,7 +16,7 @@ func fdatasync(db *DB) error {
|
|||
}
|
||||
|
||||
// flock acquires an advisory lock on a file descriptor.
|
||||
func flock(f *os.File, _ time.Duration) error {
|
||||
func flock(f *os.File, _ bool, _ time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
33
db.go
33
db.go
|
@ -140,14 +140,8 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
|||
db.MaxBatchSize = DefaultMaxBatchSize
|
||||
db.MaxBatchDelay = DefaultMaxBatchDelay
|
||||
|
||||
// Get file stats.
|
||||
s, err := os.Stat(path)
|
||||
flag := os.O_RDWR
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
} else if err == nil && (s.Mode().Perm()&0222) == 0 {
|
||||
// remove www from mode as well.
|
||||
mode ^= (mode & 0222)
|
||||
if options.ReadOnly {
|
||||
flag = os.O_RDONLY
|
||||
db.readOnly = true
|
||||
// Ignore truncations.
|
||||
|
@ -156,21 +150,26 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
|||
|
||||
// Open data file and separate sync handler for metadata writes.
|
||||
db.path = path
|
||||
var err error
|
||||
if db.file, err = os.OpenFile(db.path, flag|os.O_CREATE, mode); err != nil {
|
||||
_ = db.close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No need to lock read-only file.
|
||||
if !db.readOnly {
|
||||
db.ops.Truncate = db.file.Truncate
|
||||
// Lock file so that other processes using Bolt cannot use the database
|
||||
// at the same time. This would cause corruption since the two processes
|
||||
// would write meta pages and free pages separately.
|
||||
if err := flock(db.file, options.Timeout); err != nil {
|
||||
_ = db.close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Lock file so that other processes using Bolt in read-write mode cannot
|
||||
// use the database at the same time. This would cause corruption since
|
||||
// the two processes would write meta pages and free pages separately.
|
||||
// The database file is locked exclusively (only one process can grab the lock)
|
||||
// if !options.ReadOnly.
|
||||
// The database file is locked using the shared lock (more than one process may
|
||||
// hold a lock at the same time) otherwise (options.ReadOnly is set).
|
||||
if err := flock(db.file, !db.readOnly, options.Timeout); err != nil {
|
||||
_ = db.close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Default values for test hooks
|
||||
|
@ -660,6 +659,10 @@ type Options struct {
|
|||
|
||||
// Sets the DB.NoGrowSync flag before memory mapping the file.
|
||||
NoGrowSync bool
|
||||
|
||||
// Open database in read-only mode. Uses flock(..., LOCK_SH |LOCK_NB) to
|
||||
// grab a shared lock (UNIX).
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
// DefaultOptions represent the options used if nil options are passed into Open().
|
||||
|
|
17
db_test.go
17
db_test.go
|
@ -224,7 +224,9 @@ func TestDB_Open_FileTooSmall(t *testing.T) {
|
|||
equals(t, errors.New("file size too small"), err)
|
||||
}
|
||||
|
||||
// Ensure that a database can be opened in read-only mode.
|
||||
// Ensure that a database can be opened in read-only mode by multiple processes
|
||||
// and that a database can not be opened in read-write mode and in read-only
|
||||
// mode at the same time.
|
||||
func TestOpen_ReadOnly(t *testing.T) {
|
||||
var bucket = []byte(`bucket`)
|
||||
var key = []byte(`key`)
|
||||
|
@ -243,14 +245,15 @@ func TestOpen_ReadOnly(t *testing.T) {
|
|||
assert(t, !db.IsReadOnly(), "")
|
||||
ok(t, err)
|
||||
ok(t, db.Close())
|
||||
// Make it read-only.
|
||||
ok(t, os.Chmod(path, 0444))
|
||||
// Open again.
|
||||
db0, err := bolt.Open(path, 0666, nil)
|
||||
// Open in read-only mode.
|
||||
db0, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true})
|
||||
ok(t, err)
|
||||
defer db0.Close()
|
||||
// And again.
|
||||
db1, err := bolt.Open(path, 0666, nil)
|
||||
// Try opening in regular mode.
|
||||
_, err = bolt.Open(path, 0666, &bolt.Options{Timeout: time.Millisecond * 100})
|
||||
assert(t, err != nil, "")
|
||||
// And again (in read-only mode).
|
||||
db1, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true})
|
||||
ok(t, err)
|
||||
defer db1.Close()
|
||||
for _, db := range []*bolt.DB{db0, db1} {
|
||||
|
|
Loading…
Reference in New Issue