mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-07 13:55:17 +00:00
Merge pull request #3720 from Yawning/boltdb-fixes
Fix fundemental BoltDB backend correctness issues.
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
db *BoltDB
|
||||||
k []byte
|
ops []operation
|
||||||
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: make([]struct {
|
ops: nil,
|
||||||
k []byte
|
db: bdb,
|
||||||
v []byte
|
|
||||||
}, 0),
|
|
||||||
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 {
|
bdb.ops = append(bdb.ops, operation{opTypeSet, key, value})
|
||||||
k []byte
|
|
||||||
v []byte
|
|
||||||
}{
|
|
||||||
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 {
|
bdb.ops = append(bdb.ops, operation{opTypeDelete, key, nil})
|
||||||
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)
|
||||||
for _, elem := range bdb.buffer {
|
for _, op := range bdb.ops {
|
||||||
if putErr := b.Put(elem.k, elem.v); putErr != nil {
|
key := nonEmptyKey(nonNilBytes(op.key))
|
||||||
return putErr
|
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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user