Browse Source

Merge pull request #3720 from Yawning/boltdb-fixes

Fix fundemental BoltDB backend correctness issues.
pull/3722/head
Ismail Khoffi 6 years ago
committed by GitHub
parent
commit
ed896f508b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 30 deletions
  1. +2
    -0
      CHANGELOG_PENDING.md
  2. +26
    -30
      libs/db/boltdb.go

+ 2
- 0
CHANGELOG_PENDING.md View File

@ -28,3 +28,5 @@
* [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. * [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code.
### BUG FIXES: ### BUG FIXES:
- [libs/db] Fixed the BoltDB backend's Batch.Delete implementation (@Yawning)
- [libs/db] Fixed the BoltDB backend's Get and Iterator implementation (@Yawning)

+ 26
- 30
libs/db/boltdb.go View File

@ -66,7 +66,9 @@ func (bdb *BoltDB) Get(key []byte) (value []byte) {
key = nonEmptyKey(nonNilBytes(key)) key = nonEmptyKey(nonNilBytes(key))
err := bdb.db.View(func(tx *bbolt.Tx) error { err := bdb.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket(bucket) b := tx.Bucket(bucket)
value = b.Get(key)
if v := b.Get(key); v != nil {
value = append([]byte{}, v...)
}
return nil return nil
}) })
if err != nil { if err != nil {
@ -149,55 +151,45 @@ 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 []struct {
k []byte
v []byte
}
db *BoltDB
db *BoltDB
ops []operation
} }
// 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: make([]struct {
k []byte
v []byte
}, 0),
db: bdb,
ops: nil,
db: bdb,
} }
} }
// It is safe to modify the contents of the argument after Set returns but not // It is safe to modify the contents of the argument after Set returns but not
// before. // before.
func (bdb *boltDBBatch) Set(key, value []byte) { func (bdb *boltDBBatch) Set(key, value []byte) {
bdb.buffer = append(bdb.buffer, struct {
k []byte
v []byte
}{
key, value,
})
bdb.ops = append(bdb.ops, operation{opTypeSet, key, value})
} }
// It is safe to modify the contents of the argument after Delete returns but // It is safe to modify the contents of the argument after Delete returns but
// not before. // not before.
func (bdb *boltDBBatch) Delete(key []byte) { func (bdb *boltDBBatch) Delete(key []byte) {
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
}
}
bdb.ops = append(bdb.ops, operation{opTypeDelete, key, nil})
} }
// 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)
for _, elem := range bdb.buffer {
if putErr := b.Put(elem.k, elem.v); putErr != nil {
return putErr
for _, op := range bdb.ops {
key := nonEmptyKey(nonNilBytes(op.key))
switch op.opType {
case opTypeSet:
if putErr := b.Put(key, op.value); putErr != nil {
return putErr
}
case opTypeDelete:
if delErr := b.Delete(key); delErr != nil {
return delErr
}
} }
} }
return nil return nil
@ -322,12 +314,16 @@ func (itr *boltDBIterator) Next() {
func (itr *boltDBIterator) Key() []byte { func (itr *boltDBIterator) Key() []byte {
itr.assertIsValid() itr.assertIsValid()
return itr.currentKey
return append([]byte{}, itr.currentKey...)
} }
func (itr *boltDBIterator) Value() []byte { func (itr *boltDBIterator) Value() []byte {
itr.assertIsValid() itr.assertIsValid()
return itr.currentValue
var value []byte
if itr.currentValue != nil {
value = append([]byte{}, itr.currentValue...)
}
return value
} }
func (itr *boltDBIterator) Close() { func (itr *boltDBIterator) Close() {


Loading…
Cancel
Save