Skip to content

Commit

Permalink
go/storage/mkvs: Make Tree an interface
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Feb 18, 2020
1 parent 6073757 commit 23f96f6
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 92 deletions.
1 change: 1 addition & 0 deletions .changelog/2691.internal.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/storage/mkvs: Make Tree an interface
2 changes: 1 addition & 1 deletion go/oasis-node/cmd/debug/byzantine/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type computeBatchContext struct {

ioTree *transaction.Tree
txs []*transaction.Transaction
stateTree *urkel.Tree
stateTree urkel.Tree

stateWriteLog writelog.WriteLog
newStateRoot hash.Hash
Expand Down
2 changes: 1 addition & 1 deletion go/runtime/transaction/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (t Transaction) asOutputArtifacts() outputArtifacts {
// Tree is a Merkle tree containing transaction artifacts.
type Tree struct {
ioRoot node.Root
tree *urkel.Tree
tree urkel.Tree
}

// NewTree creates a new transaction artifacts tree.
Expand Down
2 changes: 1 addition & 1 deletion go/storage/api/root_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type RootCache struct {

// GetTree gets a tree entry from the cache by the root iff present, or creates
// a new tree with the specified root in the node database.
func (rc *RootCache) GetTree(ctx context.Context, root Root) (*urkel.Tree, error) {
func (rc *RootCache) GetTree(ctx context.Context, root Root) (urkel.Tree, error) {
return urkel.NewWithRoot(rc.remoteSyncer, rc.localDB, root, rc.persistEverything), nil
}

Expand Down
2 changes: 1 addition & 1 deletion go/storage/mkvs/urkel/checkpoint/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

func createChunk(
ctx context.Context,
tree *urkel.Tree,
tree urkel.Tree,
root node.Root,
offset node.Key,
chunkSize uint64,
Expand Down
16 changes: 5 additions & 11 deletions go/storage/mkvs/urkel/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,8 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/writelog"
)

// CommitKnown checks that the computed root matches a known root and
// if so, commits tree updates to the underlying database and returns
// the write log.
//
// In case the computed root doesn't match the known root, the update
// is NOT committed and ErrKnownRootMismatch is returned.
func (t *Tree) CommitKnown(ctx context.Context, root node.Root) (writelog.WriteLog, error) {
// Implements Tree.
func (t *tree) CommitKnown(ctx context.Context, root node.Root) (writelog.WriteLog, error) {
writeLog, _, err := t.commitWithHooks(ctx, root.Namespace, root.Round, func(rootHash hash.Hash) error {
if !rootHash.Equal(&root.Hash) {
return ErrKnownRootMismatch
Expand All @@ -27,13 +22,12 @@ func (t *Tree) CommitKnown(ctx context.Context, root node.Root) (writelog.WriteL
return writeLog, err
}

// Commit commits tree updates to the underlying database and returns
// the write log and new merkle root.
func (t *Tree) Commit(ctx context.Context, namespace common.Namespace, round uint64) (writelog.WriteLog, hash.Hash, error) {
// Implements Tree.
func (t *tree) Commit(ctx context.Context, namespace common.Namespace, round uint64) (writelog.WriteLog, hash.Hash, error) {
return t.commitWithHooks(ctx, namespace, round, nil)
}

func (t *Tree) commitWithHooks(
func (t *tree) commitWithHooks(
ctx context.Context,
namespace common.Namespace,
round uint64,
Expand Down
6 changes: 3 additions & 3 deletions go/storage/mkvs/urkel/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/node"
)

// DumpLocal dumps the tree in the local memory into the given writer.
func (t *Tree) DumpLocal(ctx context.Context, w io.Writer, maxDepth node.Depth) {
// Implements Tree.
func (t *tree) DumpLocal(ctx context.Context, w io.Writer, maxDepth node.Depth) {
t.doDumpLocal(ctx, w, t.cache.pendingRoot, 0, maxDepth)
}

func (t *Tree) doDumpLocal(ctx context.Context, w io.Writer, ptr *node.Pointer, depth node.Depth, maxDepth node.Depth) {
func (t *tree) doDumpLocal(ctx context.Context, w io.Writer, ptr *node.Pointer, depth node.Depth, maxDepth node.Depth) {
prefix := strings.Repeat(" ", int(depth)*2)
if ptr == nil {
fmt.Fprint(w, prefix+"<nil>")
Expand Down
6 changes: 3 additions & 3 deletions go/storage/mkvs/urkel/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/node"
)

// Insert inserts a key/value pair into the tree.
func (t *Tree) Insert(ctx context.Context, key []byte, value []byte) error {
// Implements Tree.
func (t *tree) Insert(ctx context.Context, key []byte, value []byte) error {
t.cache.Lock()
defer t.cache.Unlock()

Expand Down Expand Up @@ -49,7 +49,7 @@ type insertResult struct {
existed bool
}

func (t *Tree) doInsert(
func (t *tree) doInsert(
ctx context.Context,
ptr *node.Pointer,
bitDepth node.Depth,
Expand Down
12 changes: 5 additions & 7 deletions go/storage/mkvs/urkel/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import (

var errClosed = errors.New("iterator: use of closed iterator")

// SyncIterate seeks to a given key and then fetches the specified
// number of following items based on key iteration order.
func (t *Tree) SyncIterate(ctx context.Context, request *syncer.IterateRequest) (*syncer.ProofResponse, error) {
// Implements syncer.ReadSyncer.
func (t *tree) SyncIterate(ctx context.Context, request *syncer.IterateRequest) (*syncer.ProofResponse, error) {
t.cache.Lock()
defer t.cache.Unlock()

Expand Down Expand Up @@ -58,7 +57,7 @@ func (t *Tree) SyncIterate(ctx context.Context, request *syncer.IterateRequest)
}, nil
}

func (t *Tree) newFetcherSyncIterate(key node.Key, prefetch uint16) readSyncFetcher {
func (t *tree) newFetcherSyncIterate(key node.Key, prefetch uint16) readSyncFetcher {
return func(ctx context.Context, ptr *node.Pointer, rs syncer.ReadSyncer) (*syncer.Proof, error) {
rsp, err := rs.SyncIterate(ctx, &syncer.IterateRequest{
Tree: syncer.TreeID{
Expand Down Expand Up @@ -125,7 +124,7 @@ type pathAtom struct {

type treeIterator struct {
ctx context.Context
tree *Tree
tree *tree
prefetch uint16
err error
pos []pathAtom
Expand Down Expand Up @@ -155,8 +154,7 @@ func WithProof(root hash.Hash) IteratorOption {
}
}

// NewIterator creates a new iterator over the given tree.
func NewIterator(ctx context.Context, tree *Tree, options ...IteratorOption) Iterator {
func newTreeIterator(ctx context.Context, tree *tree, options ...IteratorOption) Iterator {
it := &treeIterator{
ctx: ctx,
tree: tree,
Expand Down
12 changes: 6 additions & 6 deletions go/storage/mkvs/urkel/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/syncer"
)

// Get looks up an existing key.
func (t *Tree) Get(ctx context.Context, key []byte) ([]byte, error) {
// Implements Tree.
func (t *tree) Get(ctx context.Context, key []byte) ([]byte, error) {
t.cache.Lock()
defer t.cache.Unlock()

Expand All @@ -23,8 +23,8 @@ func (t *Tree) Get(ctx context.Context, key []byte) ([]byte, error) {
return t.doGet(ctx, t.cache.pendingRoot, 0, key, doGetOptions{}, false)
}

// SyncGet fetches a single key and returns the corresponding proof.
func (t *Tree) SyncGet(ctx context.Context, request *syncer.GetRequest) (*syncer.ProofResponse, error) {
// Implements syncer.ReadSyncer.
func (t *tree) SyncGet(ctx context.Context, request *syncer.GetRequest) (*syncer.ProofResponse, error) {
t.cache.Lock()
defer t.cache.Unlock()

Expand Down Expand Up @@ -59,7 +59,7 @@ func (t *Tree) SyncGet(ctx context.Context, request *syncer.GetRequest) (*syncer
}, nil
}

func (t *Tree) newFetcherSyncGet(key node.Key, includeSiblings bool) readSyncFetcher {
func (t *tree) newFetcherSyncGet(key node.Key, includeSiblings bool) readSyncFetcher {
return func(ctx context.Context, ptr *node.Pointer, rs syncer.ReadSyncer) (*syncer.Proof, error) {
rsp, err := rs.SyncGet(ctx, &syncer.GetRequest{
Tree: syncer.TreeID{
Expand All @@ -81,7 +81,7 @@ type doGetOptions struct {
includeSiblings bool
}

func (t *Tree) doGet(
func (t *tree) doGet(
ctx context.Context,
ptr *node.Pointer,
bitDepth node.Depth,
Expand Down
12 changes: 5 additions & 7 deletions go/storage/mkvs/urkel/prefetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/syncer"
)

// PrefetchPrefixes populates the in-memory tree with nodes for keys
// starting with given prefixes.
func (t *Tree) PrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit uint16) error {
// Implements Tree.
func (t *tree) PrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit uint16) error {
t.cache.Lock()
defer t.cache.Unlock()

Expand All @@ -25,7 +24,7 @@ func (t *Tree) PrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit ui
return t.doPrefetchPrefixes(ctx, prefixes, limit)
}

func (t *Tree) doPrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit uint16) error {
func (t *tree) doPrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit uint16) error {
// TODO: Can we avoid fetching items that we already have?

return t.cache.remoteSync(
Expand All @@ -48,9 +47,8 @@ func (t *Tree) doPrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit
)
}

// SyncGetPrefixes fetches all keys under the given prefixes and returns
// the corresponding proofs.
func (t *Tree) SyncGetPrefixes(ctx context.Context, request *syncer.GetPrefixesRequest) (*syncer.ProofResponse, error) {
// Implements syncer.ReadSyncer.
func (t *tree) SyncGetPrefixes(ctx context.Context, request *syncer.GetPrefixesRequest) (*syncer.ProofResponse, error) {
t.cache.Lock()
defer t.cache.Unlock()

Expand Down
10 changes: 5 additions & 5 deletions go/storage/mkvs/urkel/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/node"
)

// RemoveExisting removes a key from the tree and returns the previous value.
func (t *Tree) RemoveExisting(ctx context.Context, key []byte) ([]byte, error) {
// Implements Tree.
func (t *tree) RemoveExisting(ctx context.Context, key []byte) ([]byte, error) {
t.cache.Lock()
defer t.cache.Unlock()

Expand Down Expand Up @@ -36,13 +36,13 @@ func (t *Tree) RemoveExisting(ctx context.Context, key []byte) ([]byte, error) {
return existing, nil
}

// Remove removes a key from the tree.
func (t *Tree) Remove(ctx context.Context, key []byte) error {
// Implements Tree.
func (t *tree) Remove(ctx context.Context, key []byte) error {
_, err := t.RemoveExisting(ctx, key)
return err
}

func (t *Tree) doRemove(
func (t *tree) doRemove(
ctx context.Context,
ptr *node.Pointer,
bitDepth node.Depth,
Expand Down
2 changes: 1 addition & 1 deletion go/storage/mkvs/urkel/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestProof(t *testing.T) {
keys, values := generateKeyValuePairsEx("", 10)
var ns common.Namespace

tree := New(nil, nil)
tree := New(nil, nil).(*tree)
for i, key := range keys {
err := tree.Insert(ctx, key, values[i])
require.NoError(err, "Insert")
Expand Down
77 changes: 77 additions & 0 deletions go/storage/mkvs/urkel/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package urkel

import (
"context"
"errors"
"io"

"github.com/oasislabs/oasis-core/go/common"
"github.com/oasislabs/oasis-core/go/common/crypto/hash"
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/node"
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/syncer"
"github.com/oasislabs/oasis-core/go/storage/mkvs/urkel/writelog"
)

var (
// ErrClosed is the error returned when methods are used after Close is called.
ErrClosed = errors.New("urkel: tree is closed")

// ErrKnownRootMismatch is the error returned by CommitKnown when the known
// root mismatches.
ErrKnownRootMismatch = errors.New("urkel: known root mismatch")
)

// Tree is a MKVS tree interface.
type Tree interface {
syncer.ReadSyncer

// Insert inserts a key/value pair into the tree.
Insert(ctx context.Context, key []byte, value []byte) error

// Get looks up an existing key.
Get(ctx context.Context, key []byte) ([]byte, error)

// PrefetchPrefixes populates the in-memory tree with nodes for keys
// starting with given prefixes.
PrefetchPrefixes(ctx context.Context, prefixes [][]byte, limit uint16) error

// RemoveExisting removes a key from the tree and returns the previous value.
RemoveExisting(ctx context.Context, key []byte) ([]byte, error)

// Remove removes a key from the tree.
Remove(ctx context.Context, key []byte) error

// NewIterator returns a new iterator over the tree.
NewIterator(ctx context.Context, options ...IteratorOption) Iterator

// ApplyWriteLog applies the operations from a write log to the current tree.
//
// The caller is responsible for calling Commit.
ApplyWriteLog(ctx context.Context, wl writelog.Iterator) error

// Close releases resources associated with this tree. After calling this
// method the tree MUST NOT be used anymore and all methods will return
// the ErrClosed error.
//
// Any pending write operations are discarded. If you need to persist them
// you need to call Commit before calling this method.
Close()

// Size calculates the size of the tree in bytes.
Size() uint64

// CommitKnown checks that the computed root matches a known root and
// if so, commits tree updates to the underlying database and returns
// the write log.
//
// In case the computed root doesn't match the known root, the update
// is NOT committed and ErrKnownRootMismatch is returned.
CommitKnown(ctx context.Context, root node.Root) (writelog.WriteLog, error)

// Commit commits tree updates to the underlying database and returns
// the write log and new merkle root.
Commit(ctx context.Context, namespace common.Namespace, round uint64) (writelog.WriteLog, hash.Hash, error)

// DumpLocal dumps the tree in the local memory into the given writer.
DumpLocal(ctx context.Context, w io.Writer, maxDepth node.Depth)
}
Loading

0 comments on commit 23f96f6

Please sign in to comment.