From 2bdab7513e90c350f346ccd0d192c8bedaf67adb Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 4 Nov 2017 22:21:50 +0000 Subject: [PATCH 1/5] ditch all memoisation we will re-introduce it more selectively later --- render/filters.go | 8 ++++---- render/render.go | 17 ++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/render/filters.go b/render/filters.go index bfc235a16..6d9f46272 100644 --- a/render/filters.go +++ b/render/filters.go @@ -114,20 +114,20 @@ type Filter struct { // MakeFilter makes a new Filter (that ignores pseudo nodes). func MakeFilter(f FilterFunc, r Renderer) Renderer { - return Memoise(&Filter{ + return &Filter{ Renderer: r, FilterFunc: func(n report.Node) bool { return n.Topology == Pseudo || f(n) }, - }) + } } // MakeFilterPseudo makes a new Filter that will not ignore pseudo nodes. func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer { - return Memoise(&Filter{ + return &Filter{ Renderer: r, FilterFunc: f, - }) + } } // MakeFilterDecorator makes a decorator that filters out non-pseudo nodes diff --git a/render/render.go b/render/render.go index e770e6ff7..2e4357588 100644 --- a/render/render.go +++ b/render/render.go @@ -33,19 +33,18 @@ type Reduce []Renderer // MakeReduce is the only sane way to produce a Reduce Renderer. func MakeReduce(renderers ...Renderer) Renderer { - r := Reduce(renderers) - return Memoise(&r) + return Reduce(renderers) } // Render produces a set of Nodes given a Report. -func (r *Reduce) Render(rpt report.Report, dct Decorator) report.Nodes { - l := len(*r) +func (r Reduce) Render(rpt report.Report, dct Decorator) report.Nodes { + l := len(r) switch l { case 0: return report.Nodes{} } c := make(chan report.Nodes, l) - for _, renderer := range *r { + for _, renderer := range r { renderer := renderer // Pike!! go func() { c <- renderer.Render(rpt, dct) @@ -61,9 +60,9 @@ func (r *Reduce) Render(rpt report.Report, dct Decorator) report.Nodes { } // Stats implements Renderer -func (r *Reduce) Stats(rpt report.Report, dct Decorator) Stats { +func (r Reduce) Stats(rpt report.Report, dct Decorator) Stats { var result Stats - for _, renderer := range *r { + for _, renderer := range r { result = result.merge(renderer.Stats(rpt, dct)) } return result @@ -78,7 +77,7 @@ type Map struct { // MakeMap makes a new Map func MakeMap(f MapFunc, r Renderer) Renderer { - return Memoise(&Map{f, r}) + return &Map{f, r} } // Render transforms a set of Nodes produces by another Renderer. @@ -180,7 +179,7 @@ type conditionalRenderer struct { // ConditionalRenderer renders nothing if the condition is false, otherwise it defers // to the wrapped Renderer. func ConditionalRenderer(c Condition, r Renderer) Renderer { - return Memoise(conditionalRenderer{c, r}) + return conditionalRenderer{c, r} } func (cr conditionalRenderer) Render(rpt report.Report, dct Decorator) report.Nodes { From 0b9bab255c920ec3e67a1e36e92dd14b5a774dc7 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 4 Nov 2017 22:24:46 +0000 Subject: [PATCH 2/5] memoise top-level rendering This deals with browsers requesting the same rendering for the same timespan when no new reports have been received for that timespan. --- app/api_topologies.go | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api_topologies.go b/app/api_topologies.go index 92e4e3d11..848a1c1c8 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -435,6 +435,7 @@ func (r *Registry) Add(ts ...APITopologyDesc) { defer r.Unlock() for _, t := range ts { t.URL = apiTopologyURL + t.id + t.renderer = render.Memoise(t.renderer) if t.parent != "" { parent := r.items[t.parent] From b793a9efa8221352b61af6d549efd65ac0959d5f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sat, 4 Nov 2017 23:05:52 +0000 Subject: [PATCH 3/5] memoise shared top-level renderers and add a comment indicating non-memoisation of other, not shared top-level renderers. This memoisation is effective when the browser requests multiple topologies for the same report. --- render/container.go | 8 +++++--- render/ecs.go | 6 ++++-- render/host.go | 2 ++ render/pod.go | 8 ++++++-- render/process.go | 4 ++++ render/swarm.go | 2 ++ render/weave.go | 2 ++ 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/render/container.go b/render/container.go index 83eec193c..e72e55c40 100644 --- a/render/container.go +++ b/render/container.go @@ -182,11 +182,11 @@ func (r containerWithImageNameRenderer) Render(rpt report.Report, dct Decorator) // ContainerWithImageNameRenderer is a Renderer which produces a container // graph where the ranks are the image names, not their IDs -var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerRenderer} +var ContainerWithImageNameRenderer = Memoise(containerWithImageNameRenderer{ContainerRenderer}) // ContainerImageRenderer is a Renderer which produces a renderable container // image graph by merging the container graph and the container image topology. -var ContainerImageRenderer = FilterEmpty(report.Container, +var ContainerImageRenderer = Memoise(FilterEmpty(report.Container, MakeMap( MapContainerImage2Name, MakeReduce( @@ -197,10 +197,12 @@ var ContainerImageRenderer = FilterEmpty(report.Container, SelectContainerImage, ), ), -) +)) // ContainerHostnameRenderer is a Renderer which produces a renderable container // by hostname graph.. +// +// not memoised var ContainerHostnameRenderer = FilterEmpty(report.Container, MakeReduce( MakeMap( diff --git a/render/ecs.go b/render/ecs.go index e36f3dc52..365b8fb28 100644 --- a/render/ecs.go +++ b/render/ecs.go @@ -5,7 +5,7 @@ import ( ) // ECSTaskRenderer is a Renderer for Amazon ECS tasks. -var ECSTaskRenderer = ConditionalRenderer(renderECSTopologies, +var ECSTaskRenderer = Memoise(ConditionalRenderer(renderECSTopologies, renderParents( report.Container, []string{report.ECSTask}, UnmanagedID, MakeFilter( @@ -13,9 +13,11 @@ var ECSTaskRenderer = ConditionalRenderer(renderECSTopologies, ContainerWithImageNameRenderer, ), ), -) +)) // ECSServiceRenderer is a Renderer for Amazon ECS services. +// +// not memoised var ECSServiceRenderer = ConditionalRenderer(renderECSTopologies, renderParents( report.ECSTask, []string{report.ECSService}, "", diff --git a/render/host.go b/render/host.go index 1bd0ec1d4..8983eb05a 100644 --- a/render/host.go +++ b/render/host.go @@ -6,6 +6,8 @@ import ( // HostRenderer is a Renderer which produces a renderable host // graph from the host topology. +// +// not memoised var HostRenderer = MakeReduce( MakeMap( MapEndpoint2Host, diff --git a/render/pod.go b/render/pod.go index 6277dba18..30c012c4a 100644 --- a/render/pod.go +++ b/render/pod.go @@ -42,7 +42,7 @@ func isPauseContainer(n report.Node) bool { // PodRenderer is a Renderer which produces a renderable kubernetes // graph by merging the container graph and the pods topology. -var PodRenderer = ConditionalRenderer(renderKubernetesTopologies, +var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies, MakeFilter( func(n report.Node) bool { state, ok := n.Latest.Lookup(kubernetes.State) @@ -65,10 +65,12 @@ var PodRenderer = ConditionalRenderer(renderKubernetesTopologies, ConnectionJoin(MapPod2IP, selectPodsWithDeployments{}), ), ), -) +)) // PodServiceRenderer is a Renderer which produces a renderable kubernetes services // graph by merging the pods graph and the services topology. +// +// not memoised var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, renderParents( report.Pod, []string{report.Service}, "", @@ -80,6 +82,8 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, // Pods with no controller are mapped to 'Unmanaged' // We can't simply combine the rendered graphs of the high level objects as they would never // have connections to each other. +// +// not memoised var KubeControllerRenderer = ConditionalRenderer(renderKubernetesTopologies, renderParents( report.Pod, []string{report.Deployment, report.DaemonSet, report.StatefulSet, report.CronJob}, UnmanagedID, diff --git a/render/process.go b/render/process.go index d38cc610e..8900f2235 100644 --- a/render/process.go +++ b/render/process.go @@ -74,10 +74,14 @@ func (r processWithContainerNameRenderer) Render(rpt report.Report, dct Decorato // ProcessWithContainerNameRenderer is a Renderer which produces a process // graph enriched with container names where appropriate +// +// not memoised var ProcessWithContainerNameRenderer = processWithContainerNameRenderer{ProcessRenderer} // ProcessNameRenderer is a Renderer which produces a renderable process // name graph by munging the progess graph. +// +// not memoised var ProcessNameRenderer = ConditionalRenderer(renderProcesses, MakeMap( MapProcess2Name, diff --git a/render/swarm.go b/render/swarm.go index da6306914..28db6d96f 100644 --- a/render/swarm.go +++ b/render/swarm.go @@ -5,6 +5,8 @@ import ( ) // SwarmServiceRenderer is a Renderer for Docker Swarm services +// +// not memoised var SwarmServiceRenderer = ConditionalRenderer(renderSwarmTopologies, renderParents( report.Container, []string{report.SwarmService}, UnmanagedID, diff --git a/render/weave.go b/render/weave.go index 216a0cf1c..404acaae3 100644 --- a/render/weave.go +++ b/render/weave.go @@ -6,6 +6,8 @@ import ( ) // WeaveRenderer is a Renderer which produces a renderable weave topology. +// +// not memoised var WeaveRenderer = MakeMap( MapWeaveIdentity, SelectOverlay, From 5f4f9da4df092fe912ff410970f756d45315158a Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 5 Nov 2017 00:21:45 +0000 Subject: [PATCH 4/5] memoise a few more shared renderers --- render/container.go | 6 +++--- render/process.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/render/container.go b/render/container.go index e72e55c40..b832c35ce 100644 --- a/render/container.go +++ b/render/container.go @@ -24,7 +24,7 @@ var UncontainedIDPrefix = MakePseudoNodeID(UncontainedID) // NB We only want processes in container _or_ processes with network connections // but we need to be careful to ensure we only include each edge once, by only // including the ProcessRenderer once. -var ContainerRenderer = MakeFilter( +var ContainerRenderer = Memoise(MakeFilter( func(n report.Node) bool { // Drop deleted containers state, ok := n.Latest.Lookup(docker.ContainerState) @@ -37,9 +37,9 @@ var ContainerRenderer = MakeFilter( ), ConnectionJoin(MapContainer2IP, SelectContainer), ), -) +)) -var mapEndpoint2IP = MakeMap(endpoint2IP, SelectEndpoint) +var mapEndpoint2IP = Memoise(MakeMap(endpoint2IP, SelectEndpoint)) const originalNodeID = "original_node_id" diff --git a/render/process.go b/render/process.go index 8900f2235..5093ed3ec 100644 --- a/render/process.go +++ b/render/process.go @@ -26,7 +26,7 @@ var EndpointRenderer = SelectEndpoint // ProcessRenderer is a Renderer which produces a renderable process // graph by merging the endpoint graph and the process topology. -var ProcessRenderer = ConditionalRenderer(renderProcesses, +var ProcessRenderer = Memoise(ConditionalRenderer(renderProcesses, MakeReduce( MakeMap( MapEndpoint2Process, @@ -34,13 +34,13 @@ var ProcessRenderer = ConditionalRenderer(renderProcesses, ), SelectProcess, ), -) +)) // ColorConnectedProcessRenderer colors connected nodes from // ProcessRenderer. Since the process topology views only show // connected processes, we need this info to determine whether // processes appearing in a details panel are linkable. -var ColorConnectedProcessRenderer = ColorConnected(ProcessRenderer) +var ColorConnectedProcessRenderer = Memoise(ColorConnected(ProcessRenderer)) // processWithContainerNameRenderer is a Renderer which produces a process // graph enriched with container names where appropriate From 7b0a08ae9d6f9e0de65ba960ff24fae18b52c89e Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Sun, 5 Nov 2017 11:05:28 +0000 Subject: [PATCH 5/5] memoise renderer passed to ConnectionJoin We do this here rather than in ConnectionJoin since that way it is obvious that the renderer isn't memoised already. --- render/pod.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/render/pod.go b/render/pod.go index 30c012c4a..28e3a9f67 100644 --- a/render/pod.go +++ b/render/pod.go @@ -62,7 +62,9 @@ var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies, ), ), ), - ConnectionJoin(MapPod2IP, selectPodsWithDeployments{}), + // ConnectionJoin invokes the renderer twice, hence it + // helps to memoise it. + ConnectionJoin(MapPod2IP, Memoise(selectPodsWithDeployments{})), ), ), ))