From ae2ea4a62f60c72dae81ca6642944ca28cf59889 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 22 Jan 2018 05:32:54 -0800 Subject: [PATCH] VersionedTree.SaveVersion() succeeds on idempontent --- nodedb.go | 7 ++++--- versioned_tree.go | 11 ++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/nodedb.go b/nodedb.go index 06bd45e28..55f2bfff3 100644 --- a/nodedb.go +++ b/nodedb.go @@ -129,9 +129,10 @@ func (ndb *nodeDB) Has(hash []byte) bool { return ndb.db.Get(key) != nil } -// SaveBranch saves the given node and all of its descendants. NOTE: This -// function clears leftNode/rigthNode recursively and calls hashWithCount on -// the given node. +// SaveBranch saves the given node and all of its descendants. +// NOTE: This function clears leftNode/rigthNode recursively and +// calls _hash() on the given node. +// TODO refactor, maybe use hashWithCount() but provide a callback. func (ndb *nodeDB) SaveBranch(node *Node) []byte { if node.persisted { return node.hash diff --git a/versioned_tree.go b/versioned_tree.go index ac72e5d6d..191d9700e 100644 --- a/versioned_tree.go +++ b/versioned_tree.go @@ -1,6 +1,7 @@ package iavl import ( + "bytes" "fmt" "github.com/pkg/errors" @@ -149,7 +150,15 @@ func (tree *VersionedTree) SaveVersion() ([]byte, int64, error) { version := tree.version + 1 if _, ok := tree.versions[version]; ok { - return nil, version, errors.Errorf("version %d was already saved", version) + // Same hash means idempotent. Return success. + var existingHash = tree.versions[version].Hash() + var newHash = tree.orphaningTree.Hash() + if bytes.Equal(existingHash, newHash) { + tree.orphaningTree = newOrphaningTree(tree.versions[version].clone()) + return existingHash, version, nil + } + return nil, version, errors.Errorf("version %d was already saved to different hash %X (existing hash %X)", + version, newHash, existingHash) } // Persist version and stash to .versions.