From d3b1586572eff52cf5b3902390bd9b64e657ec11 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 May 2024 14:13:33 -0400 Subject: [PATCH 1/2] make Ancestors and Descendents variadic A walk can start from multiple roots, so making the Ancestors and Descendents methods variadic is the easiest way to add access to that via the API with no changes in existing callers. We can use this from terraform to avoid repeatedly walking overlapping sets of nodes. --- internal/dag/dag.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/dag/dag.go b/internal/dag/dag.go index 6fdf1dcd91..7323b4ffba 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -30,14 +30,21 @@ func (g *AcyclicGraph) DirectedGraph() Grapher { // Returns a Set that includes every Vertex yielded by walking down from the // provided starting Vertex v. -func (g *AcyclicGraph) Ancestors(v Vertex) (Set, error) { +func (g *AcyclicGraph) Ancestors(vs ...Vertex) (Set, error) { s := make(Set) memoFunc := func(v Vertex, d int) error { s.Add(v) return nil } - if err := g.DepthFirstWalk(g.downEdgesNoCopy(v), memoFunc); err != nil { + start := make(Set) + for _, v := range vs { + for _, dep := range g.downEdgesNoCopy(v) { + start.Add(dep) + } + } + + if err := g.DepthFirstWalk(start, memoFunc); err != nil { return nil, err } @@ -46,14 +53,21 @@ func (g *AcyclicGraph) Ancestors(v Vertex) (Set, error) { // Returns a Set that includes every Vertex yielded by walking up from the // provided starting Vertex v. -func (g *AcyclicGraph) Descendents(v Vertex) (Set, error) { +func (g *AcyclicGraph) Descendents(vs ...Vertex) (Set, error) { s := make(Set) memoFunc := func(v Vertex, d int) error { s.Add(v) return nil } - if err := g.ReverseDepthFirstWalk(g.upEdgesNoCopy(v), memoFunc); err != nil { + start := make(Set) + for _, v := range vs { + for _, dep := range g.upEdgesNoCopy(v) { + start.Add(dep) + } + } + + if err := g.ReverseDepthFirstWalk(start, memoFunc); err != nil { return nil, err } From f41a1fb3a402b53395924c2e47e218700b8adba4 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 May 2024 14:34:19 -0400 Subject: [PATCH 2/2] walk multiple ancestors at once in ReferenceMap The existing implementation of parentModuleDependsOn would walk each reference separately, but if there were many dependencies which were all interconnected, that would end up walking over the same nodes multiple times, and storing many duplicate results. Since a graph walk can start at multiple nodes, we use the extended version of Ancestors to start at all dependencies simultaneously, reducing the time spend traversing the graph as well as automatically removing duplicates. --- internal/terraform/transform_reference.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/terraform/transform_reference.go b/internal/terraform/transform_reference.go index a22715627d..5e5f9e0651 100644 --- a/internal/terraform/transform_reference.go +++ b/internal/terraform/transform_reference.go @@ -437,17 +437,18 @@ func (m ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsO deps, fromParentModule := m.dependsOn(g, mod) for _, dep := range deps { - // add the dependency - res = append(res, dep) - - // and check any transitive resource dependencies for more resources - ans, _ := g.Ancestors(dep) - for _, v := range ans { - if isDependableResource(v) { - res = append(res, v) - } + if isDependableResource(dep) { + res = append(res, dep) } } + + ans, _ := g.Ancestors(deps...) + for _, v := range ans { + if isDependableResource(v) { + res = append(res, v) + } + } + fromModule = fromModule || fromParentModule }