Skip to content

Commit

Permalink
cleanup snapshot resource management
Browse files Browse the repository at this point in the history
  • Loading branch information
yihuang committed Jan 30, 2023
1 parent f75c310 commit 5106499
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 258 deletions.
10 changes: 5 additions & 5 deletions versiondb/client/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ func VerifyChangeSetCmd() *cobra.Command {
}

var (
tree *memiavl.Tree
blobs *memiavl.PersistedBlobs
tree *memiavl.Tree
snapshot *memiavl.Snapshot
)
if len(loadSnapshot) > 0 {
tree, blobs, err = memiavl.LoadTreeFromSnapshot(loadSnapshot)
tree, snapshot, err = memiavl.LoadTreeFromSnapshot(loadSnapshot)
if err != nil {
return errors.Wrapf(err, "fail to load snapshot: %s", loadSnapshot)
}
if blobs != nil {
defer blobs.Close()
if snapshot != nil {
defer snapshot.Close()
}
fmt.Printf("snapshot loaded: %d %X\n", tree.Version(), tree.RootHash())
} else {
Expand Down
57 changes: 57 additions & 0 deletions versiondb/memiavl/mmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package memiavl

import (
"os"

"github.com/hashicorp/go-multierror"
"github.com/ledgerwatch/erigon-lib/mmap"
)

// MmapFile manage the resources of a mmap-ed file
type MmapFile struct {
file *os.File
data []byte
// mmap handle for windows (this is used to close mmap)
handle *[mmap.MaxMapSize]byte
}

// Open openes the file and create the mmap.
// the mmap is created with flags: PROT_READ, MAP_SHARED, MADV_RANDOM.
func NewMmap(path string) (*MmapFile, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}

data, handle, err := Mmap(file)
if err != nil {
_ = file.Close()
return nil, err
}

return &MmapFile{
file: file,
data: data,
handle: handle,
}, nil
}

// Close closes the file and mmap handles
func (m *MmapFile) Close() error {
var err error

if merr := m.file.Close(); merr != nil {
err = multierror.Append(err, merr)
}

if merr := mmap.Munmap(m.data, m.handle); merr != nil {
err = multierror.Append(err, merr)
}

return err
}

// Data returns the mmap-ed buffer
func (m *MmapFile) Data() []byte {
return m.data
}
99 changes: 19 additions & 80 deletions versiondb/memiavl/persisted_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ package memiavl

import (
"encoding/binary"
"os"

"github.com/hashicorp/go-multierror"
"github.com/ledgerwatch/erigon-lib/mmap"
)

const (
Expand All @@ -18,72 +14,15 @@ const (
OffsetValue = OffsetKey + 8
OffsetHash = OffsetValue + 8

SizeHash = 32
SizeNode = OffsetHash + SizeHash
SizeHash = 32
SizeNodeWithoutHash = OffsetHash
SizeNode = SizeNodeWithoutHash + SizeHash

// encoding key/value length as 4 bytes with little endianness.
SizeKeyLen = 4
SizeValueLen = 4
)

// PersistedBlobs manage the lifecycle of mmap-ed files for the snapshot,
// it must out live the objects that derived from it.
type PersistedBlobs struct {
nodesFile *os.File
keysFile *os.File
valuesFile *os.File

nodes []byte
keys []byte
values []byte

// mmap handle for windows (this is used to close mmap)
nodesHandle *[mmap.MaxMapSize]byte
keysHandle *[mmap.MaxMapSize]byte
valuesHandle *[mmap.MaxMapSize]byte
}

// Node returns the node by index
func (blobs *PersistedBlobs) Node(index uint32) PersistedNode {
return PersistedNode{
blobs: blobs,
offset: uint64(index) * SizeNode,
}
}

// NodesLen returns the number of nodes in the snapshot
func (blobs *PersistedBlobs) NodesLen() int {
return len(blobs.nodes) / SizeNode
}

func (blobs *PersistedBlobs) Close() error {
var err error
if merr := mmap.Munmap(blobs.nodes, blobs.nodesHandle); merr != nil {
err = multierror.Append(err, merr)
}
if merr := mmap.Munmap(blobs.keys, blobs.keysHandle); merr != nil {
err = multierror.Append(err, merr)
}
if merr := mmap.Munmap(blobs.values, blobs.valuesHandle); merr != nil {
err = multierror.Append(err, merr)
}

if merr := blobs.nodesFile.Close(); merr != nil {
err = multierror.Append(err, merr)
}
if merr := blobs.keysFile.Close(); merr != nil {
err = multierror.Append(err, merr)
}
if merr := blobs.valuesFile.Close(); merr != nil {
err = multierror.Append(err, merr)
}

// clear the fields
*blobs = PersistedBlobs{}

return err
}

// PersistedNode is backed by serialized byte array, usually mmap-ed from disk file.
// Encoding format (all integers are encoded in little endian):
// - height : int8 // padded to 4bytes
Expand All @@ -95,54 +34,54 @@ func (blobs *PersistedBlobs) Close() error {
// - value : int64 offset // leaf node only
// - hash : [32]byte
type PersistedNode struct {
blobs *PersistedBlobs
offset uint64
snapshot *Snapshot
offset uint64
}

var _ Node = PersistedNode{}

func (node PersistedNode) Height() int8 {
return int8(node.blobs.nodes[node.offset+OffsetHeight])
return int8(node.snapshot.nodes[node.offset+OffsetHeight])
}

func (node PersistedNode) Version() int64 {
return int64(binary.LittleEndian.Uint32(node.blobs.nodes[node.offset+OffsetVersion:]))
return int64(binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetVersion:]))
}

func (node PersistedNode) Size() int64 {
return int64(binary.LittleEndian.Uint64(node.blobs.nodes[node.offset+OffsetSize:]))
return int64(binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetSize:]))
}

func (node PersistedNode) Key() []byte {
keyOffset := binary.LittleEndian.Uint64(node.blobs.nodes[node.offset+OffsetKey:])
keyLen := uint64(binary.LittleEndian.Uint32(node.blobs.keys[keyOffset:]))
keyOffset := binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetKey:])
keyLen := uint64(binary.LittleEndian.Uint32(node.snapshot.keys[keyOffset:]))
keyOffset += SizeKeyLen
return node.blobs.keys[keyOffset : keyOffset+keyLen]
return node.snapshot.keys[keyOffset : keyOffset+keyLen]
}

// Value result is not defined for non-leaf node.
func (node PersistedNode) Value() []byte {
valueOffset := binary.LittleEndian.Uint64(node.blobs.nodes[node.offset+OffsetValue:])
valueLen := uint64(binary.LittleEndian.Uint32(node.blobs.values[valueOffset:]))
valueOffset := binary.LittleEndian.Uint64(node.snapshot.nodes[node.offset+OffsetValue:])
valueLen := uint64(binary.LittleEndian.Uint32(node.snapshot.values[valueOffset:]))
valueOffset += SizeValueLen
return node.blobs.values[valueOffset : valueOffset+valueLen]
return node.snapshot.values[valueOffset : valueOffset+valueLen]
}

// Left result is not defined for leaf nodes.
func (node PersistedNode) Left() Node {
nodeIndex := binary.LittleEndian.Uint32(node.blobs.nodes[node.offset+OffsetLeft:])
return PersistedNode{blobs: node.blobs, offset: uint64(nodeIndex) * SizeNode}
nodeIndex := binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetLeft:])
return PersistedNode{snapshot: node.snapshot, offset: uint64(nodeIndex) * SizeNode}
}

// Right result is not defined for leaf nodes.
func (node PersistedNode) Right() Node {
nodeIndex := binary.LittleEndian.Uint32(node.blobs.nodes[node.offset+OffsetRight:])
return PersistedNode{blobs: node.blobs, offset: uint64(nodeIndex) * SizeNode}
nodeIndex := binary.LittleEndian.Uint32(node.snapshot.nodes[node.offset+OffsetRight:])
return PersistedNode{snapshot: node.snapshot, offset: uint64(nodeIndex) * SizeNode}
}

func (node PersistedNode) Hash() []byte {
offset := node.offset + OffsetHash
return node.blobs.nodes[offset : offset+SizeHash]
return node.snapshot.nodes[offset : offset+SizeHash]
}

func (node PersistedNode) Mutate(version int64) *MemNode {
Expand Down
Loading

0 comments on commit 5106499

Please sign in to comment.