github.com/datachainlab/burrow@v0.25.0/storage/rwtree.go (about)

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/tendermint/iavl"
     7  
     8  	dbm "github.com/tendermint/tendermint/libs/db"
     9  	"github.com/xlab/treeprint"
    10  )
    11  
    12  type RWTree struct {
    13  	// Working tree accumulating writes
    14  	tree *MutableTree
    15  	// Read-only tree serving previous state
    16  	*ImmutableTree
    17  	// Have any writes occurred since last save
    18  	updated bool
    19  }
    20  
    21  // Creates a concurrency safe version of an IAVL tree whereby reads are routed to the last saved tree.
    22  // Writes must be serialised (as they are within a commit for example).
    23  func NewRWTree(db dbm.DB, cacheSize int) *RWTree {
    24  	tree := NewMutableTree(db, cacheSize)
    25  	return &RWTree{
    26  		tree:          tree,
    27  		ImmutableTree: &ImmutableTree{iavl.NewImmutableTree(db, cacheSize)},
    28  	}
    29  }
    30  
    31  // Tries to load the execution state from DB, returns nil with no error if no state found
    32  func (rwt *RWTree) Load(version int64, overwriting bool) error {
    33  	const errHeader = "RWTree.Load():"
    34  	if version <= 0 {
    35  		return fmt.Errorf("%s trying to load from non-positive version %d", errHeader, version)
    36  	}
    37  	err := rwt.tree.Load(version, overwriting)
    38  	if err != nil {
    39  		return fmt.Errorf("%s loading version %d: %v", errHeader, version, err)
    40  	}
    41  	// Set readTree at commit point == tree
    42  	rwt.ImmutableTree, err = rwt.tree.GetImmutable(version)
    43  	if err != nil {
    44  		return fmt.Errorf("%s loading version %d: %v", errHeader, version, err)
    45  	}
    46  	return nil
    47  }
    48  
    49  // Save the current write tree making writes accessible from read tree.
    50  func (rwt *RWTree) Save() ([]byte, int64, error) {
    51  	// save state at a new version may still be orphaned before we save the version against the hash
    52  	hash, version, err := rwt.tree.SaveVersion()
    53  	if err != nil {
    54  		return nil, 0, fmt.Errorf("could not save RWTree: %v", err)
    55  	}
    56  	// Take an immutable reference to the tree we just saved for querying
    57  	rwt.ImmutableTree, err = rwt.tree.GetImmutable(version)
    58  	if err != nil {
    59  		return nil, 0, fmt.Errorf("RWTree.Save() could not obtain ImmutableTree read tree: %v", err)
    60  	}
    61  	rwt.updated = false
    62  	return hash, version, nil
    63  }
    64  
    65  func (rwt *RWTree) Set(key, value []byte) bool {
    66  	rwt.updated = true
    67  	return rwt.tree.Set(key, value)
    68  }
    69  
    70  func (rwt *RWTree) Delete(key []byte) ([]byte, bool) {
    71  	rwt.updated = true
    72  	return rwt.tree.Remove(key)
    73  }
    74  
    75  // Returns true if there have been any writes since last save
    76  func (rwt *RWTree) Updated() bool {
    77  	return rwt.updated
    78  }
    79  
    80  func (rwt *RWTree) GetImmutable(version int64) (*ImmutableTree, error) {
    81  	return rwt.tree.GetImmutable(version)
    82  }
    83  
    84  func (rwt *RWTree) IterateWriteTree(start, end []byte, ascending bool, fn func(key []byte, value []byte) error) error {
    85  	return rwt.tree.IterateWriteTree(start, end, ascending, fn)
    86  }
    87  
    88  // Tree printing
    89  
    90  func (rwt *RWTree) Dump() string {
    91  	tree := treeprint.New()
    92  	AddTreePrintTree("ReadTree", tree, rwt)
    93  	AddTreePrintTree("WriteTree", tree, rwt.tree)
    94  	return tree.String()
    95  }
    96  
    97  func AddTreePrintTree(edge string, tree treeprint.Tree, rwt KVCallbackIterableReader) {
    98  	tree = tree.AddBranch(fmt.Sprintf("%q", edge))
    99  	rwt.Iterate(nil, nil, true, func(key []byte, value []byte) error {
   100  		tree.AddNode(fmt.Sprintf("%q -> %q", string(key), string(value)))
   101  		return nil
   102  	})
   103  }