store: order-preserving varint key encoding (#5771)

This commit is contained in:
Callum Waters
2021-01-05 16:53:26 +01:00
committed by GitHub
parent 0555772d3a
commit 9b9222f461
9 changed files with 195 additions and 127 deletions
+57 -56
View File
@@ -3,9 +3,8 @@ package db
import (
"encoding/binary"
"fmt"
"regexp"
"strconv"
"github.com/google/orderedcode"
dbm "github.com/tendermint/tm-db"
tmsync "github.com/tendermint/tendermint/libs/sync"
@@ -14,8 +13,9 @@ import (
"github.com/tendermint/tendermint/types"
)
var (
sizeKey = []byte("size")
const (
prefixLightBlock = int64(0x0a)
prefixSize = int64(0x0b)
)
type dbs struct {
@@ -30,13 +30,17 @@ type dbs struct {
// want to use one DB with many light clients).
func New(db dbm.DB, prefix string) store.Store {
lightStore := &dbs{db: db, prefix: prefix}
// retrieve the size of the db
size := uint16(0)
bz, err := db.Get(sizeKey)
bz, err := lightStore.db.Get(lightStore.sizeKey())
if err == nil && len(bz) > 0 {
size = unmarshalSize(bz)
}
lightStore.size = size
return &dbs{db: db, prefix: prefix, size: size}
return lightStore
}
// SaveLightBlock persists LightBlock to the db.
@@ -65,7 +69,7 @@ func (s *dbs) SaveLightBlock(lb *types.LightBlock) error {
if err = b.Set(s.lbKey(lb.Height), lbBz); err != nil {
return err
}
if err = b.Set(sizeKey, marshalSize(s.size+1)); err != nil {
if err = b.Set(s.sizeKey(), marshalSize(s.size+1)); err != nil {
return err
}
if err = b.WriteSync(); err != nil {
@@ -93,7 +97,7 @@ func (s *dbs) DeleteLightBlock(height int64) error {
if err := b.Delete(s.lbKey(height)); err != nil {
return err
}
if err := b.Set(sizeKey, marshalSize(s.size-1)); err != nil {
if err := b.Set(s.sizeKey(), marshalSize(s.size-1)); err != nil {
return err
}
if err := b.WriteSync(); err != nil {
@@ -147,13 +151,8 @@ func (s *dbs) LastLightBlockHeight() (int64, error) {
}
defer itr.Close()
for itr.Valid() {
key := itr.Key()
_, height, ok := parseLbKey(key)
if ok {
return height, nil
}
itr.Next()
if itr.Valid() {
return s.decodeLbKey(itr.Key())
}
return -1, itr.Error()
@@ -172,13 +171,8 @@ func (s *dbs) FirstLightBlockHeight() (int64, error) {
}
defer itr.Close()
for itr.Valid() {
key := itr.Key()
_, height, ok := parseLbKey(key)
if ok {
return height, nil
}
itr.Next()
if itr.Valid() {
return s.decodeLbKey(itr.Key())
}
return -1, itr.Error()
@@ -202,13 +196,12 @@ func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) {
}
defer itr.Close()
for itr.Valid() {
key := itr.Key()
_, existingHeight, ok := parseLbKey(key)
if ok {
return s.LightBlock(existingHeight)
if itr.Valid() {
existingHeight, err := s.decodeLbKey(itr.Key())
if err != nil {
return nil, err
}
itr.Next()
return s.LightBlock(existingHeight)
}
if err = itr.Error(); err != nil {
return nil, err
@@ -248,11 +241,12 @@ func (s *dbs) Prune(size uint16) error {
pruned := 0
for itr.Valid() && numToPrune > 0 {
key := itr.Key()
_, height, ok := parseLbKey(key)
if ok {
if err = b.Delete(s.lbKey(height)); err != nil {
return err
}
height, err := s.decodeLbKey(key)
if err != nil {
return err
}
if err = b.Delete(s.lbKey(height)); err != nil {
return err
}
itr.Next()
numToPrune--
@@ -273,7 +267,7 @@ func (s *dbs) Prune(size uint16) error {
s.size -= uint16(pruned)
if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil {
if wErr := s.db.SetSync(s.sizeKey(), marshalSize(size)); wErr != nil {
return fmt.Errorf("failed to persist size: %w", wErr)
}
@@ -289,32 +283,39 @@ func (s *dbs) Size() uint16 {
return s.size
}
func (s *dbs) lbKey(height int64) []byte {
return []byte(fmt.Sprintf("lb/%s/%020d", s.prefix, height))
}
var keyPattern = regexp.MustCompile(`^(lb)/([^/]*)/([0-9]+)$`)
func parseKey(key []byte) (part string, prefix string, height int64, ok bool) {
submatch := keyPattern.FindSubmatch(key)
if submatch == nil {
return "", "", 0, false
}
part = string(submatch[1])
prefix = string(submatch[2])
height, err := strconv.ParseInt(string(submatch[3]), 10, 64)
func (s *dbs) sizeKey() []byte {
key, err := orderedcode.Append(nil, s.prefix, prefixSize)
if err != nil {
return "", "", 0, false
panic(err)
}
ok = true // good!
return
return key
}
func parseLbKey(key []byte) (prefix string, height int64, ok bool) {
var part string
part, prefix, height, ok = parseKey(key)
if part != "lb" {
return "", 0, false
func (s *dbs) lbKey(height int64) []byte {
key, err := orderedcode.Append(nil, s.prefix, prefixLightBlock, height)
if err != nil {
panic(err)
}
return key
}
func (s *dbs) decodeLbKey(key []byte) (height int64, err error) {
var (
dbPrefix string
lightBlockPrefix int64
)
remaining, err := orderedcode.Parse(string(key), &dbPrefix, &lightBlockPrefix, &height)
if err != nil {
err = fmt.Errorf("failed to parse light block key: %w", err)
}
if len(remaining) != 0 {
err = fmt.Errorf("expected no remainder when parsing light block key but got: %s", remaining)
}
if lightBlockPrefix != prefixLightBlock {
err = fmt.Errorf("expected light block prefix but got: %d", lightBlockPrefix)
}
if dbPrefix != s.prefix {
err = fmt.Errorf("parsed key has a different prefix. Expected: %s, got: %s", s.prefix, dbPrefix)
}
return
}
+3
View File
@@ -89,6 +89,9 @@ func Test_LightBlockBefore(t *testing.T) {
if assert.NotNil(t, h) {
assert.EqualValues(t, 2, h.Height)
}
_, err = dbStore.LightBlockBefore(2)
require.Error(t, err)
}
func Test_Prune(t *testing.T) {