## Description A follow up to https://github.com/cosmos/gorocksdb/pull/4 (see description there). Also: * remove import aliasing in rocksdb package. Closes: #10915 TODO: - [x] merge https://github.com/cosmos/gorocksdb/pull/4 - [x] tag new release in https://github.com/cosmos/gorocksdb - [x] Update this PR to include new release - [ ] tag `db/v1.0.0` (or `db/v0.1.0)` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
140 lines
3.4 KiB
Go
140 lines
3.4 KiB
Go
package memdb
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
|
|
"github.com/cosmos/cosmos-sdk/db"
|
|
"github.com/google/btree"
|
|
)
|
|
|
|
const (
|
|
// Size of the channel buffer between traversal goroutine and iterator. Using an unbuffered
|
|
// channel causes two context switches per item sent, while buffering allows more work per
|
|
// context switch. Tuned with benchmarks.
|
|
chBufferSize = 64
|
|
)
|
|
|
|
// memDBIterator is a memDB iterator.
|
|
type memDBIterator struct {
|
|
ch <-chan *item
|
|
cancel context.CancelFunc
|
|
item *item
|
|
start []byte
|
|
end []byte
|
|
}
|
|
|
|
var _ db.Iterator = (*memDBIterator)(nil)
|
|
|
|
// newMemDBIterator creates a new memDBIterator.
|
|
// A visitor is passed to the btree which streams items to the iterator over a channel. Advancing
|
|
// the iterator pulls items from the channel, returning execution to the visitor.
|
|
// The reverse case needs some special handling, since we use [start, end) while btree uses (start, end]
|
|
func newMemDBIterator(tx *dbTxn, start []byte, end []byte, reverse bool) *memDBIterator {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
ch := make(chan *item, chBufferSize)
|
|
iter := &memDBIterator{
|
|
ch: ch,
|
|
cancel: cancel,
|
|
start: start,
|
|
end: end,
|
|
}
|
|
|
|
go func() {
|
|
defer close(ch)
|
|
// Because we use [start, end) for reverse ranges, while btree uses (start, end], we need
|
|
// the following variables to handle some reverse iteration conditions ourselves.
|
|
var (
|
|
skipEqual []byte
|
|
abortLessThan []byte
|
|
)
|
|
visitor := func(i btree.Item) bool {
|
|
item := i.(*item)
|
|
if skipEqual != nil && bytes.Equal(item.key, skipEqual) {
|
|
skipEqual = nil
|
|
return true
|
|
}
|
|
if abortLessThan != nil && bytes.Compare(item.key, abortLessThan) == -1 {
|
|
return false
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return false
|
|
case ch <- item:
|
|
return true
|
|
}
|
|
}
|
|
switch {
|
|
case start == nil && end == nil && !reverse:
|
|
tx.btree.Ascend(visitor)
|
|
case start == nil && end == nil && reverse:
|
|
tx.btree.Descend(visitor)
|
|
case end == nil && !reverse:
|
|
// must handle this specially, since nil is considered less than anything else
|
|
tx.btree.AscendGreaterOrEqual(newKey(start), visitor)
|
|
case !reverse:
|
|
tx.btree.AscendRange(newKey(start), newKey(end), visitor)
|
|
case end == nil:
|
|
// abort after start, since we use [start, end) while btree uses (start, end]
|
|
abortLessThan = start
|
|
tx.btree.Descend(visitor)
|
|
default:
|
|
// skip end and abort after start, since we use [start, end) while btree uses (start, end]
|
|
skipEqual = end
|
|
abortLessThan = start
|
|
tx.btree.DescendLessOrEqual(newKey(end), visitor)
|
|
}
|
|
}()
|
|
|
|
return iter
|
|
}
|
|
|
|
// Close implements Iterator.
|
|
func (i *memDBIterator) Close() error {
|
|
i.cancel()
|
|
for range i.ch { // drain channel
|
|
}
|
|
i.item = nil
|
|
return nil
|
|
}
|
|
|
|
// Domain implements Iterator.
|
|
func (i *memDBIterator) Domain() ([]byte, []byte) {
|
|
return i.start, i.end
|
|
}
|
|
|
|
// Next implements Iterator.
|
|
func (i *memDBIterator) Next() bool {
|
|
item, ok := <-i.ch
|
|
switch {
|
|
case ok:
|
|
i.item = item
|
|
default:
|
|
i.item = nil
|
|
}
|
|
return i.item != nil
|
|
}
|
|
|
|
// Error implements Iterator.
|
|
func (i *memDBIterator) Error() error {
|
|
return nil
|
|
}
|
|
|
|
// Key implements Iterator.
|
|
func (i *memDBIterator) Key() []byte {
|
|
i.assertIsValid()
|
|
return i.item.key
|
|
}
|
|
|
|
// Value implements Iterator.
|
|
func (i *memDBIterator) Value() []byte {
|
|
i.assertIsValid()
|
|
return i.item.value
|
|
}
|
|
|
|
func (i *memDBIterator) assertIsValid() {
|
|
if i.item == nil {
|
|
panic("iterator is invalid")
|
|
}
|
|
}
|