|
|
@ -11,7 +11,6 @@ import (
|
|
|
|
|
|
|
|
|
|
|
|
// IDs are how nodes are uniquely identified, and can be serialized for the database
|
|
|
|
// IDs are how nodes are uniquely identified, and can be serialized for the database
|
|
|
|
type NodeID string
|
|
|
|
type NodeID string
|
|
|
|
|
|
|
|
|
|
|
|
func (id NodeID) Serialize() []byte {
|
|
|
|
func (id NodeID) Serialize() []byte {
|
|
|
|
return []byte(id)
|
|
|
|
return []byte(id)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -26,7 +25,7 @@ func (node_type NodeType) Hash() uint64 {
|
|
|
|
return binary.BigEndian.Uint64(bytes[(len(bytes)-9):(len(bytes)-1)])
|
|
|
|
return binary.BigEndian.Uint64(bytes[(len(bytes)-9):(len(bytes)-1)])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Generate a random id
|
|
|
|
// Generate a random NodeID
|
|
|
|
func RandID() NodeID {
|
|
|
|
func RandID() NodeID {
|
|
|
|
uuid_str := uuid.New().String()
|
|
|
|
uuid_str := uuid.New().String()
|
|
|
|
return NodeID(uuid_str)
|
|
|
|
return NodeID(uuid_str)
|
|
|
@ -38,11 +37,15 @@ type Node interface {
|
|
|
|
sync.Locker
|
|
|
|
sync.Locker
|
|
|
|
RLock()
|
|
|
|
RLock()
|
|
|
|
RUnlock()
|
|
|
|
RUnlock()
|
|
|
|
|
|
|
|
// Serialize the Node for the database
|
|
|
|
Serialize() ([]byte, error)
|
|
|
|
Serialize() ([]byte, error)
|
|
|
|
ID() NodeID
|
|
|
|
ID() NodeID
|
|
|
|
Type() NodeType
|
|
|
|
Type() NodeType
|
|
|
|
|
|
|
|
// Send a GraphSignal to the node, requires that the node is locked for read so that it can propagate
|
|
|
|
Signal(ctx *Context, signal GraphSignal, nodes NodeMap) error
|
|
|
|
Signal(ctx *Context, signal GraphSignal, nodes NodeMap) error
|
|
|
|
|
|
|
|
// Register a channel to receive updates sent to the node
|
|
|
|
RegisterChannel(id NodeID, listener chan GraphSignal)
|
|
|
|
RegisterChannel(id NodeID, listener chan GraphSignal)
|
|
|
|
|
|
|
|
// Unregister a channel from receiving updates sent to the node
|
|
|
|
UnregisterChannel(id NodeID)
|
|
|
|
UnregisterChannel(id NodeID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -76,6 +79,8 @@ func (node * GraphNode) Type() NodeType {
|
|
|
|
return NodeType("graph_node")
|
|
|
|
return NodeType("graph_node")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Propagate the signal to registered listeners, if a listener isn't ready to receive the update
|
|
|
|
|
|
|
|
// send it a notification that it was closed and then close it
|
|
|
|
func (node * GraphNode) Signal(ctx *Context, signal GraphSignal, nodes NodeMap) error {
|
|
|
|
func (node * GraphNode) Signal(ctx *Context, signal GraphSignal, nodes NodeMap) error {
|
|
|
|
ctx.Log.Logf("signal", "SIGNAL: %s - %s", node.ID(), signal.String())
|
|
|
|
ctx.Log.Logf("signal", "SIGNAL: %s - %s", node.ID(), signal.String())
|
|
|
|
node.listeners_lock.Lock()
|
|
|
|
node.listeners_lock.Lock()
|
|
|
@ -129,8 +134,11 @@ func NewGraphNode(id NodeID) GraphNode {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Magic first four bytes of serialized DB content, stored big endian
|
|
|
|
const NODE_DB_MAGIC = 0x2491df14
|
|
|
|
const NODE_DB_MAGIC = 0x2491df14
|
|
|
|
|
|
|
|
// Total length of the node database header, has magic to verify and type_hash to map to load function
|
|
|
|
const NODE_DB_HEADER_LEN = 12
|
|
|
|
const NODE_DB_HEADER_LEN = 12
|
|
|
|
|
|
|
|
// A DBHeader is parsed from the first NODE_DB_HEADER_LEN bytes of a serialized DB node
|
|
|
|
type DBHeader struct {
|
|
|
|
type DBHeader struct {
|
|
|
|
Magic uint32
|
|
|
|
Magic uint32
|
|
|
|
TypeHash uint64
|
|
|
|
TypeHash uint64
|
|
|
@ -154,7 +162,8 @@ func NewDBHeader(node_type NodeType) DBHeader {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func getNodeBytes(ctx * Context, node Node) ([]byte, error) {
|
|
|
|
// Internal function to serialize a node and wrap it with the DB Header
|
|
|
|
|
|
|
|
func getNodeBytes(node Node) ([]byte, error) {
|
|
|
|
if node == nil {
|
|
|
|
if node == nil {
|
|
|
|
return nil, fmt.Errorf("DB_SERIALIZE_ERROR: cannot serialize nil node")
|
|
|
|
return nil, fmt.Errorf("DB_SERIALIZE_ERROR: cannot serialize nil node")
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -170,25 +179,6 @@ func getNodeBytes(ctx * Context, node Node) ([]byte, error) {
|
|
|
|
return db_data, nil
|
|
|
|
return db_data, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write a node to the database
|
|
|
|
|
|
|
|
func WriteNode(ctx * Context, node Node) error {
|
|
|
|
|
|
|
|
ctx.Log.Logf("db", "DB_WRITE: %+v", node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
node_bytes, err := getNodeBytes(ctx, node)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
id_ser := node.ID().Serialize()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = ctx.DB.Update(func(txn *badger.Txn) error {
|
|
|
|
|
|
|
|
err := txn.Set(id_ser, node_bytes)
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Write multiple nodes to the database in a single transaction
|
|
|
|
// Write multiple nodes to the database in a single transaction
|
|
|
|
func WriteNodes(ctx * Context, nodes NodeMap) error {
|
|
|
|
func WriteNodes(ctx * Context, nodes NodeMap) error {
|
|
|
|
ctx.Log.Logf("db", "DB_WRITES: %d", len(nodes))
|
|
|
|
ctx.Log.Logf("db", "DB_WRITES: %d", len(nodes))
|
|
|
@ -200,7 +190,7 @@ func WriteNodes(ctx * Context, nodes NodeMap) error {
|
|
|
|
serialized_ids := make([][]byte, len(nodes))
|
|
|
|
serialized_ids := make([][]byte, len(nodes))
|
|
|
|
i := 0
|
|
|
|
i := 0
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
node_bytes, err := getNodeBytes(ctx, node)
|
|
|
|
node_bytes, err := getNodeBytes(node)
|
|
|
|
ctx.Log.Logf("db", "DB_WRITE: %+v", node)
|
|
|
|
ctx.Log.Logf("db", "DB_WRITE: %+v", node)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -227,7 +217,7 @@ func WriteNodes(ctx * Context, nodes NodeMap) error {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get the bytes associates with `id` in the database, or error
|
|
|
|
// Get the bytes associates with `id` from the database after unwrapping the header, or error
|
|
|
|
func readNodeBytes(ctx * Context, id NodeID) (uint64, []byte, error) {
|
|
|
|
func readNodeBytes(ctx * Context, id NodeID) (uint64, []byte, error) {
|
|
|
|
var bytes []byte
|
|
|
|
var bytes []byte
|
|
|
|
err := ctx.DB.View(func(txn *badger.Txn) error {
|
|
|
|
err := ctx.DB.View(func(txn *badger.Txn) error {
|
|
|
@ -267,11 +257,15 @@ func readNodeBytes(ctx * Context, id NodeID) (uint64, []byte, error) {
|
|
|
|
return header.TypeHash, node_bytes, nil
|
|
|
|
return header.TypeHash, node_bytes, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Load a Node from the database by ID
|
|
|
|
func LoadNode(ctx * Context, id NodeID) (Node, error) {
|
|
|
|
func LoadNode(ctx * Context, id NodeID) (Node, error) {
|
|
|
|
nodes := NodeMap{}
|
|
|
|
nodes := NodeMap{}
|
|
|
|
return LoadNodeRecurse(ctx, id, nodes)
|
|
|
|
return LoadNodeRecurse(ctx, id, nodes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Recursively load a node from the database.
|
|
|
|
|
|
|
|
// It's expected that node_type.Load adds the newly loaded node to nodes before calling LoadNodeRecurse again.
|
|
|
|
func LoadNodeRecurse(ctx * Context, id NodeID, nodes NodeMap) (Node, error) {
|
|
|
|
func LoadNodeRecurse(ctx * Context, id NodeID, nodes NodeMap) (Node, error) {
|
|
|
|
node, exists := nodes[id]
|
|
|
|
node, exists := nodes[id]
|
|
|
|
if exists == false {
|
|
|
|
if exists == false {
|
|
|
@ -299,6 +293,7 @@ func LoadNodeRecurse(ctx * Context, id NodeID, nodes NodeMap) (Node, error) {
|
|
|
|
return node, nil
|
|
|
|
return node, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Internal function to check for a duplicate node in a slice by ID
|
|
|
|
func checkForDuplicate(nodes []Node) error {
|
|
|
|
func checkForDuplicate(nodes []Node) error {
|
|
|
|
found := map[NodeID]bool{}
|
|
|
|
found := map[NodeID]bool{}
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
for _, node := range(nodes) {
|
|
|
@ -315,6 +310,7 @@ func checkForDuplicate(nodes []Node) error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Convert any slice of types that implement Node to a []Node
|
|
|
|
func NodeList[K Node](list []K) []Node {
|
|
|
|
func NodeList[K Node](list []K) []Node {
|
|
|
|
nodes := make([]Node, len(list))
|
|
|
|
nodes := make([]Node, len(list))
|
|
|
|
for i, node := range(list) {
|
|
|
|
for i, node := range(list) {
|
|
|
@ -325,10 +321,14 @@ func NodeList[K Node](list []K) []Node {
|
|
|
|
|
|
|
|
|
|
|
|
type NodeMap map[NodeID]Node
|
|
|
|
type NodeMap map[NodeID]Node
|
|
|
|
type NodesFn func(nodes NodeMap) error
|
|
|
|
type NodesFn func(nodes NodeMap) error
|
|
|
|
|
|
|
|
// Initiate a read context for nodes and call nodes_fn with init_nodes locked for read
|
|
|
|
func UseStates(ctx * Context, init_nodes []Node, nodes_fn NodesFn) error {
|
|
|
|
func UseStates(ctx * Context, init_nodes []Node, nodes_fn NodesFn) error {
|
|
|
|
nodes := NodeMap{}
|
|
|
|
nodes := NodeMap{}
|
|
|
|
return UseMoreStates(ctx, init_nodes, nodes, nodes_fn)
|
|
|
|
return UseMoreStates(ctx, init_nodes, nodes, nodes_fn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add nodes to an existing read context and call nodes_fn with new_nodes locked for read
|
|
|
|
func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn NodesFn) error {
|
|
|
|
func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn NodesFn) error {
|
|
|
|
err := checkForDuplicate(new_nodes)
|
|
|
|
err := checkForDuplicate(new_nodes)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
@ -355,6 +355,7 @@ func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn Node
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Initiate a write context for nodes and call nodes_fn with nodes locked for read
|
|
|
|
func UpdateStates(ctx * Context, nodes []Node, nodes_fn NodesFn) error {
|
|
|
|
func UpdateStates(ctx * Context, nodes []Node, nodes_fn NodesFn) error {
|
|
|
|
locked_nodes := NodeMap{}
|
|
|
|
locked_nodes := NodeMap{}
|
|
|
|
err := UpdateMoreStates(ctx, nodes, locked_nodes, nodes_fn)
|
|
|
|
err := UpdateMoreStates(ctx, nodes, locked_nodes, nodes_fn)
|
|
|
@ -367,6 +368,8 @@ func UpdateStates(ctx * Context, nodes []Node, nodes_fn NodesFn) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add nodes to an existing write context and call nodes_fn with nodes locked for read
|
|
|
|
func UpdateMoreStates(ctx * Context, nodes []Node, locked_nodes NodeMap, nodes_fn NodesFn) error {
|
|
|
|
func UpdateMoreStates(ctx * Context, nodes []Node, locked_nodes NodeMap, nodes_fn NodesFn) error {
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
_, locked := locked_nodes[node.ID()]
|
|
|
|
_, locked := locked_nodes[node.ID()]
|
|
|
@ -379,6 +382,7 @@ func UpdateMoreStates(ctx * Context, nodes []Node, locked_nodes NodeMap, nodes_f
|
|
|
|
return nodes_fn(locked_nodes)
|
|
|
|
return nodes_fn(locked_nodes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a new channel with a buffer the size of buffer, and register it to node with the id
|
|
|
|
func UpdateChannel(node Node, buffer int, id NodeID) chan GraphSignal {
|
|
|
|
func UpdateChannel(node Node, buffer int, id NodeID) chan GraphSignal {
|
|
|
|
if node == nil {
|
|
|
|
if node == nil {
|
|
|
|
panic("Cannot get an update channel to nil")
|
|
|
|
panic("Cannot get an update channel to nil")
|
|
|
|