From 51b804ab0e274a7ea714e462f68974ef32b40a2c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 11 Feb 2022 18:00:45 +0300 Subject: [PATCH] storage: generalize Level/Bolt seek implementations Too much in common. Just refactoring. no functional changes. --- pkg/core/storage/boltdb_store.go | 53 +++++++++++-------------------- pkg/core/storage/leveldb_store.go | 38 +++++++--------------- pkg/core/storage/store.go | 20 ++++++++++++ 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/pkg/core/storage/boltdb_store.go b/pkg/core/storage/boltdb_store.go index 59a1acbb1..8157e84eb 100644 --- a/pkg/core/storage/boltdb_store.go +++ b/pkg/core/storage/boltdb_store.go @@ -7,7 +7,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util/slice" - "github.com/syndtr/goleveldb/leveldb/util" "go.etcd.io/bbolt" ) @@ -110,44 +109,30 @@ func (s *BoltDBStore) PutChangeSet(puts map[string][]byte) error { // Seek implements the Store interface. func (s *BoltDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) { - start := make([]byte, len(rng.Prefix)+len(rng.Start)) - copy(start, rng.Prefix) - copy(start[len(rng.Prefix):], rng.Start) - if rng.Backwards { - s.seekBackwards(rng.Prefix, start, f) - } else { - s.seek(rng.Prefix, start, f) - } -} - -func (s *BoltDBStore) seek(key []byte, start []byte, f func(k, v []byte) bool) { - prefix := util.BytesPrefix(key) - prefix.Start = start + rang := seekRangeToPrefixes(rng) err := s.db.View(func(tx *bbolt.Tx) error { + var ( + k, v []byte + next func() ([]byte, []byte) + ) + c := tx.Bucket(Bucket).Cursor() - for k, v := c.Seek(prefix.Start); k != nil && (len(prefix.Limit) == 0 || bytes.Compare(k, prefix.Limit) <= 0); k, v = c.Next() { - if !f(k, v) { - break + + if !rng.Backwards { + k, v = c.Seek(rang.Start) + next = c.Next + } else { + if len(rang.Limit) == 0 { + lastKey, _ := c.Last() + k, v = c.Seek(lastKey) + } else { + c.Seek(rang.Limit) + k, v = c.Prev() } + next = c.Prev } - return nil - }) - if err != nil { - panic(err) - } -} -func (s *BoltDBStore) seekBackwards(key []byte, start []byte, f func(k, v []byte) bool) { - err := s.db.View(func(tx *bbolt.Tx) error { - c := tx.Bucket(Bucket).Cursor() - // Move cursor to the first kv pair which is followed by the pair matching the specified prefix. - if len(start) == 0 { - lastKey, _ := c.Last() - start = lastKey - } - rng := util.BytesPrefix(start) // in fact, we only need limit based on start slice to iterate backwards starting from this limit - c.Seek(rng.Limit) - for k, v := c.Prev(); k != nil && bytes.HasPrefix(k, key); k, v = c.Prev() { + for ; k != nil && bytes.HasPrefix(k, rng.Prefix) && (len(rang.Limit) == 0 || bytes.Compare(k, rang.Limit) <= 0); k, v = next() { if !f(k, v) { break } diff --git a/pkg/core/storage/leveldb_store.go b/pkg/core/storage/leveldb_store.go index 6d5439711..f38109931 100644 --- a/pkg/core/storage/leveldb_store.go +++ b/pkg/core/storage/leveldb_store.go @@ -4,7 +4,6 @@ import ( "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/syndtr/goleveldb/leveldb/util" ) // LevelDBOptions configuration for LevelDB. @@ -83,34 +82,21 @@ func (s *LevelDBStore) PutChangeSet(puts map[string][]byte) error { // Seek implements the Store interface. func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) { - start := make([]byte, len(rng.Prefix)+len(rng.Start)) - copy(start, rng.Prefix) - copy(start[len(rng.Prefix):], rng.Start) - if rng.Backwards { - s.seekBackwards(rng.Prefix, start, f) + var ( + next func() bool + ok bool + iter = s.db.NewIterator(seekRangeToPrefixes(rng), nil) + ) + + if !rng.Backwards { + ok = iter.Next() + next = iter.Next } else { - s.seek(rng.Prefix, start, f) + ok = iter.Last() + next = iter.Prev } -} -func (s *LevelDBStore) seek(key []byte, start []byte, f func(k, v []byte) bool) { - prefix := util.BytesPrefix(key) - prefix.Start = start - iter := s.db.NewIterator(prefix, nil) - for iter.Next() { - if !f(iter.Key(), iter.Value()) { - break - } - } - iter.Release() -} - -func (s *LevelDBStore) seekBackwards(key []byte, start []byte, f func(k, v []byte) bool) { - iRange := util.BytesPrefix(start) - iRange.Start = key - - iter := s.db.NewIterator(iRange, nil) - for ok := iter.Last(); ok; ok = iter.Prev() { + for ; ok; ok = next() { if !f(iter.Key(), iter.Value()) { break } diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index ff1997234..5759f9b17 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -4,6 +4,8 @@ import ( "encoding/binary" "errors" "fmt" + + "github.com/syndtr/goleveldb/leveldb/util" ) // KeyPrefix constants. @@ -133,6 +135,24 @@ func AppendPrefixInt(k KeyPrefix, n int) []byte { return AppendPrefix(k, b) } +func seekRangeToPrefixes(sr SeekRange) *util.Range { + var ( + rang *util.Range + start = make([]byte, len(sr.Prefix)+len(sr.Start)) + ) + copy(start, sr.Prefix) + copy(start[len(sr.Prefix):], sr.Start) + + if !sr.Backwards { + rang = util.BytesPrefix(sr.Prefix) + rang.Start = start + } else { + rang = util.BytesPrefix(start) + rang.Start = sr.Prefix + } + return rang +} + // NewStore creates storage with preselected in configuration database type. func NewStore(cfg DBConfiguration) (Store, error) { var store Store