Skip to content

Commit

Permalink
[wip] adds start and stop implementation
Browse files Browse the repository at this point in the history
* fulfills the Interruptable module interface to allow for control over
  the node stores lifecycle to enable save and load functionality
* adds tests for the Start and Stop functionality
* adds nil checks to GetTree function to make testing start and stop
  behavior predictable
  • Loading branch information
dylanlott committed Aug 1, 2023
1 parent 0d34af1 commit aba7feb
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 28 deletions.
21 changes: 16 additions & 5 deletions persistence/trees/atomic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ func TestTreeStore_SaveAndLoad(t *testing.T) {
logger: &zerolog.Logger{},
treeStoreDir: tmpDir,
}
ts.setupTrees()
require.NotEmpty(t, ts)
require.NoError(t, ts.Start())
require.NotNil(t, ts.rootTree.tree)

for _, treeName := range stateTreeNames {
err := ts.merkleTrees[treeName].tree.Update([]byte("foo"), []byte("bar"))
Expand All @@ -132,18 +132,29 @@ func TestTreeStore_SaveAndLoad(t *testing.T) {
hash1 := ts.getStateHash()
require.NotEmpty(t, hash1)

require.NoError(t, ts.Savepoint())
w, err := ts.save()
require.NoError(t, err)
require.NotNil(t, w)
require.NotNil(t, w.rootHash)
require.NotNil(t, w.merkleRoots)

// Stop the first tree store so that it's databases are no longer used
require.NoError(t, ts.Stop())

// declare a second TreeStore with no trees then load the first worldstate into it
ts2 := &treeStore{
logger: &zerolog.Logger{},
logger: logger.Global.CreateLoggerForModule(modules.TreeStoreSubmoduleName),
treeStoreDir: tmpDir,
}
// TODO IN THIS COMMIT do we need to start this treestore?
// require.NoError(t, ts2.Start())

// Load sets a tree store to the provided worldstate
err = ts2.Load(ts.prevState)
err = ts2.Load(w)
require.NoError(t, err)

hash2 := ts2.getStateHash()

// Assert that hash is unchanged from save and load
require.Equal(t, hash1, hash2)
}
27 changes: 23 additions & 4 deletions persistence/trees/module.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package trees

import (
"encoding/hex"
"fmt"

"github.com/pokt-network/pocket/persistence/kvstore"
"github.com/pokt-network/pocket/shared/modules"
"github.com/pokt-network/smt"
"go.uber.org/multierr"
)

var _ modules.TreeStoreModule = &treeStore{}
Expand All @@ -19,10 +21,6 @@ func (*treeStore) Create(bus modules.Bus, options ...modules.TreeStoreOption) (m

bus.RegisterModule(m)

if err := m.setupTrees(); err != nil {
return nil, err
}

return m, nil
}

Expand Down Expand Up @@ -50,6 +48,27 @@ func WithTreeStoreDirectory(path string) modules.TreeStoreOption {
}
}

// Start loads up the trees from the configured tree store directory.
func (t *treeStore) Start() error {
return t.setupTrees()
}

// Stop shuts down the database connection to the nodestore for the root tree and then for each merkle tree.
// If Commit has not been called before Stop is called, data will be lost.
func (t *treeStore) Stop() error {
t.logger.Debug().Msgf("🛑 tree store stop initiated at %s 🛑", hex.EncodeToString(t.rootTree.tree.Root()))
errs := []error{}
if err := t.rootTree.nodeStore.Stop(); err != nil {
errs = append(errs, err)
}
for _, st := range t.merkleTrees {
if err := st.nodeStore.Stop(); err != nil {
errs = append(errs, err)
}
}
return multierr.Combine(errs...)
}

func (t *treeStore) GetModuleName() string { return modules.TreeStoreSubmoduleName }

func (t *treeStore) setupTrees() error {
Expand Down
67 changes: 50 additions & 17 deletions persistence/trees/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/golang/mock/gomock"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand All @@ -24,30 +25,31 @@ const (
serviceURLFormat = "node%d.consensus:42069"
)

// DISCUSS: This is duplicated from inside trees package. Is it worth exporting or is it better as duplicate code?
var stateTreeNames = []string{
"root",
"app",
"val",
"fish",
"servicer",
"account",
"pool",
"transactions",
"params",
"flags",
"ibc",
}

func TestTreeStore_Create(t *testing.T) {
ctrl := gomock.NewController(t)
mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl)
mockBus := createMockBus(t, mockRuntimeMgr)
genesisStateMock := createMockGenesisState(nil)
persistenceMock := preparePersistenceMock(t, mockBus, genesisStateMock)

mockBus.EXPECT().
GetPersistenceModule().
Return(persistenceMock).
AnyTimes()
persistenceMock.EXPECT().
GetBus().
AnyTimes().
Return(mockBus)
persistenceMock.EXPECT().
NewRWContext(int64(0)).
AnyTimes()
persistenceMock.EXPECT().
GetTxIndexer().
AnyTimes()

treemod, err := trees.Create(mockBus, trees.WithTreeStoreDirectory(":memory:"))
assert.NoError(t, err)

require.NoError(t, treemod.Start())

got := treemod.GetBus()
assert.Equal(t, got, mockBus)

Expand All @@ -61,6 +63,37 @@ func TestTreeStore_Create(t *testing.T) {
require.Empty(t, keys, vals)
}

func TestTreeStore_StartAndStop(t *testing.T) {
ctrl := gomock.NewController(t)
mockRuntimeMgr := mockModules.NewMockRuntimeMgr(ctrl)
mockBus := createMockBus(t, mockRuntimeMgr)

treemod, err := trees.Create(
mockBus,
trees.WithTreeStoreDirectory(":memory:"),
trees.WithLogger(&zerolog.Logger{}))
assert.NoError(t, err)

// GetTree should return nil for each tree if Start has not been called
for _, v := range stateTreeNames {
root, ns := treemod.GetTree(v)
require.Empty(t, root)
require.Empty(t, ns)
}
// Should start without error
require.NoError(t, treemod.Start())

// Should stop without error
require.NoError(t, treemod.Stop())

// Should error if node store is called after Stop
for _, treeName := range stateTreeNames {
_, nodestore := treemod.GetTree(treeName)
_, _, err = nodestore.GetAll([]byte(""), false)
require.Error(t, err, "%s tree failed to return an error when expected", treeName)
}
}

func TestTreeStore_DebugClearAll(t *testing.T) {
// TODO: Write test case for the DebugClearAll method
t.Skip("TODO: Write test case for DebugClearAll method")
Expand Down
13 changes: 11 additions & 2 deletions persistence/trees/trees.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,19 @@ type worldState struct {
// `Commit()` to write to the nodestore.
func (t *treeStore) GetTree(name string) ([]byte, kvstore.KVStore) {
if name == RootTreeName {
return t.rootTree.tree.Root(), t.rootTree.nodeStore
if t.rootTree != nil {
if t.rootTree.tree != nil {
return t.rootTree.tree.Root(), t.rootTree.nodeStore
}
return nil, nil
}
return nil, nil
}
if tree, ok := t.merkleTrees[name]; ok {
return tree.tree.Root(), tree.nodeStore
if tree != nil {
return tree.tree.Root(), tree.nodeStore
}
return nil, nil
}
return nil, nil
}
Expand Down
1 change: 1 addition & 0 deletions shared/modules/treestore_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type treeStoreFactory = FactoryWithOptions[TreeStoreModule, TreeStoreOption]
// merkle trees that compose the state hash of pocket.
type TreeStoreModule interface {
Submodule
InterruptableModule
treeStoreFactory

AtomicStore
Expand Down

0 comments on commit aba7feb

Please sign in to comment.