github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/export.go (about)

     1  package iavl
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/pkg/errors"
     7  )
     8  
     9  // exportBufferSize is the number of nodes to buffer in the exporter. It improves throughput by
    10  // processing multiple nodes per context switch, but take care to avoid excessive memory usage,
    11  // especially since callers may export several IAVL stores in parallel (e.g. the Cosmos SDK).
    12  const exportBufferSize = 32
    13  
    14  // ExportDone is returned by Exporter.Next() when all items have been exported.
    15  var ExportDone = errors.New("export is complete") // nolint:golint
    16  
    17  // ExportNode contains exported node data.
    18  type ExportNode struct {
    19  	Key     []byte
    20  	Value   []byte
    21  	Version int64
    22  	Height  int8
    23  }
    24  
    25  // Exporter exports nodes from an ImmutableTree. It is created by ImmutableTree.Export().
    26  //
    27  // Exported nodes can be imported into an empty tree with MutableTree.Import(). Nodes are exported
    28  // depth-first post-order (LRN), this order must be preserved when importing in order to recreate
    29  // the same tree structure.
    30  type Exporter struct {
    31  	tree   *ImmutableTree
    32  	ch     chan *ExportNode
    33  	cancel context.CancelFunc
    34  }
    35  
    36  // NewExporter creates a new Exporter. Callers must call Close() when done.
    37  func newExporter(tree *ImmutableTree) *Exporter {
    38  	ctx, cancel := context.WithCancel(context.Background())
    39  	exporter := &Exporter{
    40  		tree:   tree,
    41  		ch:     make(chan *ExportNode, exportBufferSize),
    42  		cancel: cancel,
    43  	}
    44  
    45  	tree.ndb.incrVersionReaders(tree.version)
    46  	go exporter.export(ctx)
    47  
    48  	return exporter
    49  }
    50  
    51  // export exports nodes
    52  func (e *Exporter) export(ctx context.Context) {
    53  	e.tree.root.traversePost(e.tree, true, func(node *Node) bool {
    54  		exportNode := &ExportNode{
    55  			Key:     node.key,
    56  			Value:   node.value,
    57  			Version: node.version,
    58  			Height:  node.height,
    59  		}
    60  
    61  		select {
    62  		case e.ch <- exportNode:
    63  			return false
    64  		case <-ctx.Done():
    65  			return true
    66  		}
    67  	})
    68  	close(e.ch)
    69  }
    70  
    71  // Next fetches the next exported node, or returns ExportDone when done.
    72  func (e *Exporter) Next() (*ExportNode, error) {
    73  	if exportNode, ok := <-e.ch; ok {
    74  		return exportNode, nil
    75  	}
    76  	return nil, ExportDone
    77  }
    78  
    79  // Close closes the exporter. It is safe to call multiple times.
    80  func (e *Exporter) Close() {
    81  	e.cancel()
    82  	for range e.ch { // drain channel
    83  	}
    84  	if e.tree != nil {
    85  		e.tree.ndb.decrVersionReaders(e.tree.version)
    86  	}
    87  	e.tree = nil
    88  }