156 lines
3 KiB
Go
156 lines
3 KiB
Go
|
package nodemgr
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/lbryio/claimtrie/claim"
|
||
|
"github.com/lbryio/claimtrie/trie"
|
||
|
"github.com/syndtr/goleveldb/leveldb"
|
||
|
)
|
||
|
|
||
|
// NodeMgr ...
|
||
|
type NodeMgr struct {
|
||
|
sync.RWMutex
|
||
|
|
||
|
db *leveldb.DB
|
||
|
nodes map[string]*claim.Node
|
||
|
dirty map[string]bool
|
||
|
nextUpdates todos
|
||
|
}
|
||
|
|
||
|
// New ...
|
||
|
func New(db *leveldb.DB) *NodeMgr {
|
||
|
nm := &NodeMgr{
|
||
|
db: db,
|
||
|
nodes: map[string]*claim.Node{},
|
||
|
dirty: map[string]bool{},
|
||
|
nextUpdates: todos{},
|
||
|
}
|
||
|
return nm
|
||
|
}
|
||
|
|
||
|
// Get ...
|
||
|
func (nm *NodeMgr) Get(key trie.Key) (trie.Value, error) {
|
||
|
nm.Lock()
|
||
|
defer nm.Unlock()
|
||
|
|
||
|
if n, ok := nm.nodes[string(key)]; ok {
|
||
|
return n, nil
|
||
|
}
|
||
|
if nm.db != nil {
|
||
|
b, err := nm.db.Get(key, nil)
|
||
|
if err == nil {
|
||
|
_ = b // TODO: Loaded. Deserialize it.
|
||
|
} else if err != leveldb.ErrNotFound {
|
||
|
// DB error. Propagated.
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
// New node.
|
||
|
n := claim.NewNode(string(key))
|
||
|
nm.nodes[string(key)] = n
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
// Set ...
|
||
|
func (nm *NodeMgr) Set(key trie.Key, val trie.Value) {
|
||
|
n := val.(*claim.Node)
|
||
|
|
||
|
nm.Lock()
|
||
|
defer nm.Unlock()
|
||
|
|
||
|
nm.nodes[string(key)] = n
|
||
|
nm.dirty[string(key)] = true
|
||
|
|
||
|
// TODO: flush to disk.
|
||
|
}
|
||
|
|
||
|
// Reset resets all nodes to specified height.
|
||
|
func (nm *NodeMgr) Reset(h claim.Height) error {
|
||
|
for _, n := range nm.nodes {
|
||
|
if err := n.Reset(h); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// NodeAt returns the node adjusted to specified height.
|
||
|
func (nm *NodeMgr) NodeAt(name string, h claim.Height) (*claim.Node, error) {
|
||
|
v, err := nm.Get(trie.Key(name))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
n := v.(*claim.Node)
|
||
|
if err = n.AdjustTo(h); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
// ModifyNode returns the node adjusted to specified height.
|
||
|
func (nm *NodeMgr) ModifyNode(name string, h claim.Height, modifier func(*claim.Node) error) error {
|
||
|
n, err := nm.NodeAt(name, h)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err = modifier(n); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
nm.nextUpdates.set(name, h+1)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CatchUp ...
|
||
|
func (nm *NodeMgr) CatchUp(h claim.Height, notifier func(key trie.Key) error) error {
|
||
|
for name := range nm.nextUpdates[h] {
|
||
|
n, err := nm.NodeAt(name, h)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err = notifier(trie.Key(name)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if next := n.NextUpdate(); next > h {
|
||
|
nm.nextUpdates.set(name, next)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Show ...
|
||
|
func (nm *NodeMgr) Show(name string) error {
|
||
|
if len(name) != 0 {
|
||
|
fmt.Printf("[%s] %s\n", name, nm.nodes[name])
|
||
|
return nil
|
||
|
}
|
||
|
names := []string{}
|
||
|
for name := range nm.nodes {
|
||
|
names = append(names, name)
|
||
|
}
|
||
|
sort.Strings(names)
|
||
|
for _, name := range names {
|
||
|
fmt.Printf("[%s] %s\n", name, nm.nodes[name])
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// UpdateAll ...
|
||
|
func (nm *NodeMgr) UpdateAll(m func(key trie.Key) error) error {
|
||
|
for name := range nm.nodes {
|
||
|
m(trie.Key(name))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type todos map[claim.Height]map[string]bool
|
||
|
|
||
|
func (t todos) set(name string, h claim.Height) {
|
||
|
if t[h] == nil {
|
||
|
t[h] = map[string]bool{}
|
||
|
}
|
||
|
t[h][name] = true
|
||
|
}
|