Skip to content

Commit

Permalink
memdb: take out read-lock during iteration (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker authored Mar 10, 2020
1 parent 766d71d commit 3ff68ea
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- [\#71](https://github.com/tendermint/tm-db/pull/71) Closed or written batches can no longer be reused, all non-`Close()` calls will panic

- [memdb] [\#74](https://github.com/tendermint/tm-db/pull/74) `Iterator()` and `ReverseIterator()` now take out database read locks for the duration of the iteration

- [memdb] [\#56](https://github.com/tendermint/tm-db/pull/56) Removed some exported methods that were mainly meant for internal use: `Mutex()`, `SetNoLock()`, `SetNoLockSync()`, `DeleteNoLock()`, and `DeleteNoLockSync()`

### Improvements
Expand Down
10 changes: 4 additions & 6 deletions memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,13 @@ func (db *MemDB) NewBatch() Batch {
}

// Iterator implements DB.
// Takes out a read-lock on the database until the iterator is closed.
func (db *MemDB) Iterator(start, end []byte) (Iterator, error) {
db.mtx.RLock()
defer db.mtx.RUnlock()
return newMemDBIterator(db.btree, start, end, false), nil
return newMemDBIterator(db, start, end, false), nil
}

// ReverseIterator implements DB.
// Takes out a read-lock on the database until the iterator is closed.
func (db *MemDB) ReverseIterator(start, end []byte) (Iterator, error) {
db.mtx.RLock()
defer db.mtx.RUnlock()
return newMemDBIterator(db.btree, start, end, true), nil
return newMemDBIterator(db, start, end, true), nil
}
16 changes: 9 additions & 7 deletions memdb_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type memDBIterator struct {
var _ Iterator = (*memDBIterator)(nil)

// newMemDBIterator creates a new memDBIterator.
func newMemDBIterator(bt *btree.BTree, start []byte, end []byte, reverse bool) *memDBIterator {
func newMemDBIterator(db *MemDB, start []byte, end []byte, reverse bool) *memDBIterator {
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan *item, chBufferSize)
iter := &memDBIterator{
Expand All @@ -36,7 +36,9 @@ func newMemDBIterator(bt *btree.BTree, start []byte, end []byte, reverse bool) *
end: end,
}

db.mtx.RLock()
go func() {
defer db.mtx.RUnlock()
// 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 (
Expand All @@ -61,23 +63,23 @@ func newMemDBIterator(bt *btree.BTree, start []byte, end []byte, reverse bool) *
}
switch {
case start == nil && end == nil && !reverse:
bt.Ascend(visitor)
db.btree.Ascend(visitor)
case start == nil && end == nil && reverse:
bt.Descend(visitor)
db.btree.Descend(visitor)
case end == nil && !reverse:
// must handle this specially, since nil is considered less than anything else
bt.AscendGreaterOrEqual(newKey(start), visitor)
db.btree.AscendGreaterOrEqual(newKey(start), visitor)
case !reverse:
bt.AscendRange(newKey(start), newKey(end), visitor)
db.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
bt.Descend(visitor)
db.btree.Descend(visitor)
default:
// skip end and abort after start, since we use [start, end) while btree uses (start, end]
skipEqual = end
abortLessThan = start
bt.DescendLessOrEqual(newKey(end), visitor)
db.btree.DescendLessOrEqual(newKey(end), visitor)
}
close(ch)
}()
Expand Down

0 comments on commit 3ff68ea

Please sign in to comment.