libs/db: boltdb: use slice instead of sync.Map (#3633)

for storing batch keys and values in boltDBBatch.

NOTE: batch does not have to be safe for concurrent access. Delete may
be slow, but given it should not be used often, it's ok.

Fixes #3631
This commit is contained in:
Anton Kaliaev 2019-05-07 14:06:20 +04:00 committed by GitHub
parent a7358bc69f
commit a076b48202
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"sync"
"github.com/etcd-io/bbolt" "github.com/etcd-io/bbolt"
) )
@ -150,36 +149,58 @@ func (bdb *BoltDB) Stats() map[string]string {
// boltDBBatch stores key values in sync.Map and dumps them to the underlying // boltDBBatch stores key values in sync.Map and dumps them to the underlying
// DB upon Write call. // DB upon Write call.
type boltDBBatch struct { type boltDBBatch struct {
buffer *sync.Map buffer []struct {
db *BoltDB k []byte
v []byte
}
db *BoltDB
} }
// NewBatch returns a new batch. // NewBatch returns a new batch.
func (bdb *BoltDB) NewBatch() Batch { func (bdb *BoltDB) NewBatch() Batch {
return &boltDBBatch{ return &boltDBBatch{
buffer: &sync.Map{}, buffer: make([]struct {
db: bdb, k []byte
v []byte
}, 0),
db: bdb,
} }
} }
// It is safe to modify the contents of the argument after Set returns but not
// before.
func (bdb *boltDBBatch) Set(key, value []byte) { func (bdb *boltDBBatch) Set(key, value []byte) {
bdb.buffer.Store(string(key), value) bdb.buffer = append(bdb.buffer, struct {
k []byte
v []byte
}{
key, value,
})
} }
// It is safe to modify the contents of the argument after Delete returns but
// not before.
func (bdb *boltDBBatch) Delete(key []byte) { func (bdb *boltDBBatch) Delete(key []byte) {
bdb.buffer.Delete(string(key)) for i, elem := range bdb.buffer {
if bytes.Equal(elem.k, key) {
// delete without preserving order
bdb.buffer[i] = bdb.buffer[len(bdb.buffer)-1]
bdb.buffer = bdb.buffer[:len(bdb.buffer)-1]
return
}
}
} }
// NOTE: the operation is synchronous (see BoltDB for reasons) // NOTE: the operation is synchronous (see BoltDB for reasons)
func (bdb *boltDBBatch) Write() { func (bdb *boltDBBatch) Write() {
err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { err := bdb.db.db.Batch(func(tx *bbolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
var putErr error for _, elem := range bdb.buffer {
bdb.buffer.Range(func(key, value interface{}) bool { if putErr := b.Put(elem.k, elem.v); putErr != nil {
putErr = b.Put([]byte(key.(string)), value.([]byte)) return putErr
return putErr == nil // stop if putErr is not nil }
}) }
return putErr return nil
}) })
if err != nil { if err != nil {
panic(err) panic(err)