diff --git a/bitcask.go b/bitcask.go index 88d3ecc..020ec48 100644 --- a/bitcask.go +++ b/bitcask.go @@ -155,6 +155,12 @@ func (b *Bitcask) Put(key, value []byte) error { return err } + if b.config.sync { + if err := b.curr.Sync(); err != nil { + return err + } + } + item := b.keydir.Add(key, b.curr.FileID(), offset, n) b.trie.Add(string(key), item) diff --git a/bitcask_test.go b/bitcask_test.go index df79488..8cd340c 100644 --- a/bitcask_test.go +++ b/bitcask_test.go @@ -566,18 +566,6 @@ func BenchmarkPut(b *testing.B) { b.Fatal(err) } - testdir, err := ioutil.TempDir(currentDir, "bitcask_bench") - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(testdir) - - db, err := Open(testdir) - if err != nil { - b.Fatal(err) - } - defer db.Close() - tests := []benchmarkTestCase{ {"128B", 128}, {"256B", 256}, @@ -589,20 +577,43 @@ func BenchmarkPut(b *testing.B) { {"32K", 32768}, } - for _, tt := range tests { - b.Run(tt.name, func(b *testing.B) { - b.SetBytes(int64(tt.size)) + variants := map[string][]Option{ + "NoSync": []Option{ + WithSync(false), + }, + "Sync": []Option{ + WithSync(true), + }, + } - key := []byte("foo") - value := []byte(strings.Repeat(" ", tt.size)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := db.Put(key, value) - if err != nil { - b.Fatal(err) + for name, options := range variants { + testdir, err := ioutil.TempDir(currentDir, "bitcask_bench") + if err != nil { + b.Fatal(err) + } + defer os.RemoveAll(testdir) + + db, err := Open(testdir, options...) + if err != nil { + b.Fatal(err) + } + defer db.Close() + + for _, tt := range tests { + b.Run(tt.name+name, func(b *testing.B) { + b.SetBytes(int64(tt.size)) + + key := []byte("foo") + value := []byte(strings.Repeat(" ", tt.size)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := db.Put(key, value) + if err != nil { + b.Fatal(err) + } } - } - }) + }) + } } } diff --git a/options.go b/options.go index 74dc2a4..64c17f3 100644 --- a/options.go +++ b/options.go @@ -24,25 +24,29 @@ type config struct { maxDatafileSize int maxKeySize int maxValueSize int + sync bool } func (c *config) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - MaxDatafileSize int `json:"max_datafile_size"` - MaxKeySize int `json:"max_key_size"` - MaxValueSize int `json:"max_value_size"` + MaxDatafileSize int `json:"max_datafile_size"` + MaxKeySize int `json:"max_key_size"` + MaxValueSize int `json:"max_value_size"` + Sync bool `json:"sync"` }{ MaxDatafileSize: c.maxDatafileSize, MaxKeySize: c.maxKeySize, MaxValueSize: c.maxValueSize, + Sync: c.sync, }) } func getConfig(path string) (*config, error) { type Config struct { - MaxDatafileSize int `json:"max_datafile_size"` - MaxKeySize int `json:"max_key_size"` - MaxValueSize int `json:"max_value_size"` + MaxDatafileSize int `json:"max_datafile_size"` + MaxKeySize int `json:"max_key_size"` + MaxValueSize int `json:"max_value_size"` + Sync bool `json:"sync"` } var cfg Config @@ -60,6 +64,7 @@ func getConfig(path string) (*config, error) { maxDatafileSize: cfg.MaxDatafileSize, maxKeySize: cfg.MaxKeySize, maxValueSize: cfg.MaxValueSize, + sync: cfg.Sync, }, nil } @@ -94,3 +99,12 @@ func WithMaxValueSize(size int) Option { return nil } } + +// WithSync causes Sync() to be called on every key/value written increasing +// durability and saftey at the expense of performance +func WithSync(sync bool) Option { + return func(cfg *config) error { + cfg.sync = sync + return nil + } +}