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