Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/flow/internal/dag/ops.go
4095 views
1
package dag
2
3
import (
4
"fmt"
5
"strings"
6
7
"github.com/hashicorp/go-multierror"
8
)
9
10
// Reduce performs a transitive reduction on g. A transitive reduction removes
11
// as many edges as possible while maintaining the same "reachability" as the
12
// original graph: any node N reachable from node S will still be reachable
13
// after a reduction.
14
func Reduce(g *Graph) {
15
// A direct edge between two vertices can be removed if that same target
16
// vertex is indirectly reachable through another edge.
17
//
18
// To detect this, we iterate through all vertices in the graph, performing a
19
// depth-first search at its dependencies. If the target vertex is reachable
20
// from the source vertex, the edge is removed.
21
for source := range g.nodes {
22
_ = Walk(g, g.Dependencies(source), func(direct Node) error {
23
// Iterate over (direct, indirect) edges and remove (source, indirect)
24
// edges if they exist. This is a safe operation because other is still
25
// reachable by source via its (source, direct) edge.
26
for indirect := range g.outEdges[direct] {
27
g.RemoveEdge(Edge{From: source, To: indirect})
28
}
29
return nil
30
})
31
}
32
}
33
34
// Validate checks that the graph doesn't contain cycles
35
func Validate(g *Graph) error {
36
var err error
37
38
// Check cycles using strongly connected components algorithm
39
for _, cycle := range StronglyConnectedComponents(g) {
40
if len(cycle) > 1 {
41
cycleStr := make([]string, len(cycle))
42
for i, node := range cycle {
43
cycleStr[i] = node.NodeID()
44
}
45
err = multierror.Append(err, fmt.Errorf("cycle: %s", strings.Join(cycleStr, ", ")))
46
}
47
}
48
49
// Check self references
50
for _, e := range g.Edges() {
51
if e.From == e.To {
52
err = multierror.Append(err, fmt.Errorf("self reference: %s", e.From.NodeID()))
53
}
54
}
55
56
return err
57
}
58
59