diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a8ad2795..f96956e72 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -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. ### 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) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index 6bf474c86..30501dd82 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -66,7 +66,9 @@ func (bdb *BoltDB) Get(key []byte) (value []byte) { key = nonEmptyKey(nonNilBytes(key)) err := bdb.db.View(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) - value = b.Get(key) + if v := b.Get(key); v != nil { + value = append([]byte{}, v...) + } return 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 // DB upon Write call. type boltDBBatch struct { - buffer []struct { - k []byte - v []byte - } - db *BoltDB + db *BoltDB + ops []operation } // NewBatch returns a new batch. func (bdb *BoltDB) NewBatch() Batch { 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 // before. 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 // not before. 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) func (bdb *boltDBBatch) Write() { err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { 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 @@ -322,12 +314,16 @@ func (itr *boltDBIterator) Next() { func (itr *boltDBIterator) Key() []byte { itr.assertIsValid() - return itr.currentKey + return append([]byte{}, itr.currentKey...) } func (itr *boltDBIterator) Value() []byte { itr.assertIsValid() - return itr.currentValue + var value []byte + if itr.currentValue != nil { + value = append([]byte{}, itr.currentValue...) + } + return value } func (itr *boltDBIterator) Close() {