Golang Dag

package dag

import (
    "context"
    "ina-fei/api/internal/config"
    "ina-fei/api/internal/types"
    "log"
    "sync"
)

type Graph struct {
    // nodes to run
    Nodes []*Node
    // where to fetch all node result
    ResultGather chan *NodeResult
    // adjoint table for this graph
    cache map[string][]*Node
}

func NewGraph() *Graph {
    return &Graph{ResultGather: make(chan *NodeResult), cache: make(map[string][]*Node)}
}

func (g *Graph) Add(node *Node) {
    // add to adjoint table
    // check if this node already be exists
    // panic if exists
    if _, ok := g.cache[node.Meta.Name]; ok == true {
        panic("node already exists in this graph")
    }
    // add to adjoint table
    g.cache[node.Meta.Name] = make([]*Node, 0)
    // connect node to graph
    node.ResultGather = g.ResultGather
    //add to list prepare for run
    g.Nodes = append(g.Nodes, node)
}

func (g *Graph) Connect(from, to *Node) {
    // check both node exists in this graph
    if _, ok := g.cache[from.Meta.Name]; ok != true {
        panic("node not exists in this graph; call Add before connect")
    }
    if _, ok := g.cache[to.Meta.Name]; ok != true {
        panic("node not exists in this graph; call Add before connect")
    }
    // check if already connected
    for _, n := range g.cache[from.Meta.Name] {
        if n.Meta.Name == to.Meta.Name {
            log.Println("already connect, will do nothing")
            return
        }
    }
    // do connect
    // modify adjoint table
    g.cache[from.Meta.Name] = append(g.cache[from.Meta.Name], to)
    // modify channel of both side node
    c := make(chan *NodeResult)
    from.OutputChannels = append(from.OutputChannels, c)
    to.InputChannels = append(to.InputChannels, c)
    log.Printf("connect from node %s to node %s", from.Meta.Name, to.Meta.Name)
}

func (g *Graph) findCycle() {
    // inDegrees stores in degree count for every node
    inDegrees := make(map[string]int)
    // set up
    for _, n := range g.Nodes {
        // set current node
        if _, ok := inDegrees[n.Meta.Name]; ok != true {
            inDegrees[n.Meta.Name] = 0
        }
        // set successor of current node
        successors := g.cache[n.Meta.Name]
        for _, successor := range successors {
            inDegrees[successor.Meta.Name] += 1
        }
    }
    //
    for len(inDegrees) != 0 {
        readyNodes := make([]string, 0)
        for k, v := range inDegrees {
            if v == 0 {
                readyNodes = append(readyNodes, k)
            }
        }
        if len(readyNodes) == 0 {
            panic("find cycle")
        }
        for _, readyNode := range readyNodes {
            successorOfReadyNode := g.cache[readyNode]
            for _, s := range successorOfReadyNode {
                inDegrees[s.Meta.Name] -= 1
            }
            // delete ready node
            delete(inDegrees, readyNode)
        }
    }
}

// total async
// check g.ResultGather
func (g *Graph) Run(ctx context.Context, config config.Config, req types.Request) {
    // find cycle first
    g.findCycle()
    var wg sync.WaitGroup
    for _, n := range g.Nodes {
        wg.Add(1)
        go func(n *Node) {
            defer wg.Done()
            n.Run(ctx, config, req)
        }(n)
    }
    go func() {
        wg.Wait()
        close(g.ResultGather)
    }()
}
dag
package dag

import (
    "context"
    "fmt"
    "api/internal/config"
    "api/internal/types"
    "log"
    "sync"

     "github.com/google/uuid"
)

type NodeType int

const (
    DataSourceType NodeType = iota
    VarLogicType
)

type (
    NodeMetaData struct {
        Name string
        Type NodeType
    }

    NodeResult struct {
        Meta NodeMetaData
        // error occurs or not
        Err error
        // result
        // map if NodeType is VarLogicType
        // pointer to AyoTransitResult otherwise
        Result interface{}
    }

    // Node logic func generate node result
    // ctx -> not currently be used
    // config -> configure from yaml file and etc.
    // req -> request info contains basic user info and etc.
    // gather  -> channel where proceed result is stored
    NodeLogicFunc func(ctx context.Context, config config.Config, req types.Request, cache []*NodeResult) *NodeResult

    Node struct {
        Meta           NodeMetaData
        InputChannels  []chan *NodeResult
        OutputChannels []chan *NodeResult
        ResultGather   chan *NodeResult
        LogicFunc      NodeLogicFunc
    }
)

func NewNode(nodeType NodeType, name string) *Node {
    return &Node{Meta: NodeMetaData{Name: name, Type: nodeType}}
}

func NewNodeWithName(nodeType NodeType) *Node {
    return &Node{Meta: NodeMetaData{Name: uuid.New().String(), Type: nodeType}}
}

func (node *Node) SetFunc(logicFunc NodeLogicFunc) {
    node.LogicFunc = logicFunc
}

func (node *Node) MakeNodeResult(result interface{}, err error) *NodeResult {
    return &NodeResult{
        Meta:   node.Meta,
        Err:    err,
        Result: result,
    }
}

func (node *Node) Run(ctx context.Context, config config.Config, req types.Request) {
    // check func is set
    if node.LogicFunc == nil {
        panic("call setFunc before Run")
    }
    var wg sync.WaitGroup
    // wait for input
    // gather all input
    gather := make(chan *NodeResult)
    log.Printf("node %s begin to wait", node.Meta.Name)
    for _, i := range node.InputChannels {
        wg.Add(1)
        go func(i chan *NodeResult) {
            defer wg.Done()
            nodeResult := <-i
            gather <- nodeResult
        }(i)
    }
    go func() {
        wg.Wait()
        close(gather)
    }()
    // run logic
    cache := make([]*NodeResult, 0)
    for i := range gather {
        cache = append(cache, i)
    }
    log.Printf("node %s finish wait", node.Meta.Name)
    log.Printf("node %s prepare to run", node.Meta.Name)
    currentNodeResult := node.LogicFunc(ctx, config, req, cache)
    // send result to output channels
    for _, c := range node.OutputChannels {
        wg.Add(1)
        go func(c chan *NodeResult) {
            defer wg.Done()
            defer close(c)
            c <- currentNodeResult
        }(c)
    }
    // for send result
    wg.Add(1)
    go func() {
        defer wg.Done()
        if node.Meta.Type == VarLogicType {
            fmt.Printf("node %s send result to ResultGather\n", node.Meta.Name)
            node.ResultGather <- currentNodeResult
        }
    }()
    wg.Wait()
    log.Printf("node %s finish run", node.Meta.Name)
}
node

 

posted @ 2021-04-12 18:47  gostreamer  阅读(223)  评论(0)    收藏  举报