store: use db iterators for pruning and range-based queries (#5848)

This commit is contained in:
Callum Waters
2021-01-08 13:12:54 +01:00
committed by GitHub
parent 66ba12d9bc
commit 385ea1db7d
13 changed files with 269 additions and 647 deletions
+3 -3
View File
@@ -37,7 +37,7 @@ func BenchmarkSequence(b *testing.B) {
},
benchmarkFullNode,
[]provider.Provider{benchmarkFullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.SequentialVerification(),
)
@@ -65,7 +65,7 @@ func BenchmarkBisection(b *testing.B) {
},
benchmarkFullNode,
[]provider.Provider{benchmarkFullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
if err != nil {
@@ -93,7 +93,7 @@ func BenchmarkBackwards(b *testing.B) {
},
benchmarkFullNode,
[]provider.Provider{benchmarkFullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
if err != nil {
+21 -21
View File
@@ -231,7 +231,7 @@ func TestClient_SequentialVerification(t *testing.T) {
tc.otherHeaders,
tc.vals,
)},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.SequentialVerification(),
light.Logger(log.TestingLogger()),
)
@@ -356,7 +356,7 @@ func TestClient_SkippingVerification(t *testing.T) {
tc.otherHeaders,
tc.vals,
)},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
light.Logger(log.TestingLogger()),
)
@@ -394,7 +394,7 @@ func TestClientLargeBisectionVerification(t *testing.T) {
},
veryLargeFullNode,
[]provider.Provider{veryLargeFullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
)
require.NoError(t, err)
@@ -416,7 +416,7 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
},
fullNode,
[]provider.Provider{fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
)
require.NoError(t, err)
@@ -440,7 +440,7 @@ func TestClient_Cleanup(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
@@ -460,7 +460,7 @@ func TestClient_Cleanup(t *testing.T) {
func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
// 1. options.Hash == trustedHeader.Hash
{
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -484,7 +484,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
// 2. options.Hash != trustedHeader.Hash
{
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -529,7 +529,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
// 1. options.Hash == trustedHeader.Hash
{
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -559,7 +559,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
// 2. options.Hash != trustedHeader.Hash
// This could happen if previous provider was lying to us.
{
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -606,7 +606,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
// 1. options.Hash == trustedHeader.Hash
{
// load the first three headers into the trusted store
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -644,7 +644,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
// 2. options.Hash != trustedHeader.Hash
// This could happen if previous provider was lying to us.
{
trustedStore := dbs.New(dbm.NewMemDB(), chainID)
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -704,7 +704,7 @@ func TestClient_Update(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
@@ -725,7 +725,7 @@ func TestClient_Concurrency(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
@@ -766,7 +766,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
trustOptions,
deadNode,
[]provider.Provider{fullNode, fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
@@ -792,7 +792,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
},
largeFullNode,
[]provider.Provider{largeFullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
@@ -874,7 +874,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
},
tc.provider,
[]provider.Provider{tc.provider},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err, idx)
@@ -887,7 +887,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
func TestClient_NewClientFromTrustedStore(t *testing.T) {
// 1) Initiate DB and fill with a "trusted" header
db := dbs.New(dbm.NewMemDB(), chainID)
db := dbs.New(dbm.NewMemDB())
err := db.SaveLightBlock(l1)
require.NoError(t, err)
@@ -944,7 +944,7 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{badProvider1, badProvider2},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
@@ -994,7 +994,7 @@ func TestClient_TrustedValidatorSet(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{badValSetNode, fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
@@ -1012,7 +1012,7 @@ func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
trustOptions,
fullNode,
[]provider.Provider{fullNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.PruningSize(1),
)
@@ -1085,7 +1085,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
trustOptions,
badNode,
[]provider.Provider{badNode, badNode},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
+5 -5
View File
@@ -54,7 +54,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
},
primary,
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
@@ -136,7 +136,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
},
primary,
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
verificationOption,
@@ -191,7 +191,7 @@ func TestClientDivergentTraces1(t *testing.T) {
},
primary,
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
@@ -215,7 +215,7 @@ func TestClientDivergentTraces2(t *testing.T) {
},
primary,
[]provider.Provider{deadNode, deadNode, primary},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
@@ -250,7 +250,7 @@ func TestClientDivergentTraces3(t *testing.T) {
},
primary,
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB(), chainID),
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxRetryAttempts(1),
)
+2 -2
View File
@@ -61,7 +61,7 @@ func ExampleClient_Update() {
},
primary,
[]provider.Provider{primary}, // NOTE: primary should not be used here
dbs.New(db, chainID),
dbs.New(db),
light.Logger(log.TestingLogger()),
)
if err != nil {
@@ -129,7 +129,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
},
primary,
[]provider.Provider{primary}, // NOTE: primary should not be used here
dbs.New(db, chainID),
dbs.New(db),
light.Logger(log.TestingLogger()),
)
if err != nil {
+29 -37
View File
@@ -19,18 +19,17 @@ const (
)
type dbs struct {
db dbm.DB
prefix string
db dbm.DB
mtx tmsync.RWMutex
size uint16
}
// New returns a Store that wraps any DB (with an optional prefix in case you
// want to use one DB with many light clients).
func New(db dbm.DB, prefix string) store.Store {
// New returns a Store that wraps any DB
// If you want to share one DB across many light clients consider using PrefixDB
func New(db dbm.DB) store.Store {
lightStore := &dbs{db: db, prefix: prefix}
lightStore := &dbs{db: db}
// retrieve the size of the db
size := uint16(0)
@@ -197,11 +196,17 @@ func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) {
defer itr.Close()
if itr.Valid() {
existingHeight, err := s.decodeLbKey(itr.Key())
var lbpb tmproto.LightBlock
err = lbpb.Unmarshal(itr.Value())
if err != nil {
return nil, err
return nil, fmt.Errorf("unmarshal error: %w", err)
}
return s.LightBlock(existingHeight)
lightBlock, err := types.LightBlockFromProto(&lbpb)
if err != nil {
return nil, fmt.Errorf("proto conversion error: %w", err)
}
return lightBlock, nil
}
if err = itr.Error(); err != nil {
return nil, err
@@ -238,39 +243,32 @@ func (s *dbs) Prune(size uint16) error {
b := s.db.NewBatch()
defer b.Close()
pruned := 0
for itr.Valid() && numToPrune > 0 {
key := itr.Key()
height, err := s.decodeLbKey(key)
if err != nil {
return err
}
if err = b.Delete(s.lbKey(height)); err != nil {
if err = b.Delete(itr.Key()); err != nil {
return err
}
itr.Next()
numToPrune--
pruned++
}
if err = itr.Error(); err != nil {
return err
}
// 3) // update size
s.mtx.Lock()
s.size = size
s.mtx.Unlock()
if wErr := b.Set(s.sizeKey(), marshalSize(size)); wErr != nil {
return fmt.Errorf("failed to persist size: %w", wErr)
}
// 4) write batch deletion to disk
err = b.WriteSync()
if err != nil {
return err
}
// 3) Update size.
s.mtx.Lock()
defer s.mtx.Unlock()
s.size -= uint16(pruned)
if wErr := s.db.SetSync(s.sizeKey(), marshalSize(size)); wErr != nil {
return fmt.Errorf("failed to persist size: %w", wErr)
}
return nil
}
@@ -284,7 +282,7 @@ func (s *dbs) Size() uint16 {
}
func (s *dbs) sizeKey() []byte {
key, err := orderedcode.Append(nil, s.prefix, prefixSize)
key, err := orderedcode.Append(nil, prefixSize)
if err != nil {
panic(err)
}
@@ -292,7 +290,7 @@ func (s *dbs) sizeKey() []byte {
}
func (s *dbs) lbKey(height int64) []byte {
key, err := orderedcode.Append(nil, s.prefix, prefixLightBlock, height)
key, err := orderedcode.Append(nil, prefixLightBlock, height)
if err != nil {
panic(err)
}
@@ -300,11 +298,8 @@ func (s *dbs) lbKey(height int64) []byte {
}
func (s *dbs) decodeLbKey(key []byte) (height int64, err error) {
var (
dbPrefix string
lightBlockPrefix int64
)
remaining, err := orderedcode.Parse(string(key), &dbPrefix, &lightBlockPrefix, &height)
var lightBlockPrefix int64
remaining, err := orderedcode.Parse(string(key), &lightBlockPrefix, &height)
if err != nil {
err = fmt.Errorf("failed to parse light block key: %w", err)
}
@@ -314,9 +309,6 @@ func (s *dbs) decodeLbKey(key []byte) (height int64, err error) {
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
}
+5 -5
View File
@@ -19,7 +19,7 @@ import (
)
func TestLast_FirstLightBlockHeight(t *testing.T) {
dbStore := New(dbm.NewMemDB(), "TestLast_FirstLightBlockHeight")
dbStore := New(dbm.NewMemDB())
// Empty store
height, err := dbStore.LastLightBlockHeight()
@@ -44,7 +44,7 @@ func TestLast_FirstLightBlockHeight(t *testing.T) {
}
func Test_SaveLightBlock(t *testing.T) {
dbStore := New(dbm.NewMemDB(), "Test_SaveLightBlockAndValidatorSet")
dbStore := New(dbm.NewMemDB())
// Empty store
h, err := dbStore.LightBlock(1)
@@ -74,7 +74,7 @@ func Test_SaveLightBlock(t *testing.T) {
}
func Test_LightBlockBefore(t *testing.T) {
dbStore := New(dbm.NewMemDB(), "Test_LightBlockBefore")
dbStore := New(dbm.NewMemDB())
assert.Panics(t, func() {
_, _ = dbStore.LightBlockBefore(0)
@@ -95,7 +95,7 @@ func Test_LightBlockBefore(t *testing.T) {
}
func Test_Prune(t *testing.T) {
dbStore := New(dbm.NewMemDB(), "Test_Prune")
dbStore := New(dbm.NewMemDB())
// Empty store
assert.EqualValues(t, 0, dbStore.Size())
@@ -132,7 +132,7 @@ func Test_Prune(t *testing.T) {
}
func Test_Concurrency(t *testing.T) {
dbStore := New(dbm.NewMemDB(), "Test_Prune")
dbStore := New(dbm.NewMemDB())
var wg sync.WaitGroup
for i := 1; i <= 100; i++ {