Skip to content

Commit

Permalink
Remove pointer math (comparison) from render caching, as it is unreli…
Browse files Browse the repository at this point in the history
…able
  • Loading branch information
paulbellamy committed Feb 17, 2016
1 parent a2710ec commit a0a60ca
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 28 deletions.
7 changes: 5 additions & 2 deletions app/benchmark_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/ugorji/go/codec"

"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test/fixture"
)
Expand Down Expand Up @@ -51,7 +50,11 @@ func BenchmarkTopologyList(b *testing.B) {
Form: url.Values{},
}
for i := 0; i < b.N; i++ {
render.ResetCache()
b.StopTimer()
topologyRegistry.walk(func(t APITopologyDesc) {
t.renderer.ResetCache()
})
b.StartTimer()
topologyRegistry.renderTopologies(report, request)
}
}
8 changes: 6 additions & 2 deletions render/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ func benchmarkRender(b *testing.B, r render.Renderer) {
b.ResetTimer()

for i := 0; i < b.N; i++ {
render.ResetCache()
b.StopTimer()
r.ResetCache()
b.StartTimer()
benchmarkRenderResult = r.Render(report)
if len(benchmarkRenderResult) == 0 {
b.Errorf("Rendered topology contained no nodes")
Expand All @@ -80,7 +82,9 @@ func benchmarkStats(b *testing.B, r render.Renderer) {

for i := 0; i < b.N; i++ {
// No way to tell if this was successful :(
render.ResetCache()
b.StopTimer()
r.ResetCache()
b.StartTimer()
benchmarkStatsResult = r.Stats(report)
}
}
Expand Down
25 changes: 7 additions & 18 deletions render/memoise.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,33 @@
package render

import (
"fmt"
"reflect"

"github.com/bluele/gcache"

"github.com/weaveworks/scope/report"
)

var renderCache = gcache.New(100).LRU().Build()

type memoise struct {
Renderer
cache gcache.Cache
}

// Memoise wraps the renderer in a loving embrace of caching
func Memoise(r Renderer) Renderer { return &memoise{r} }
func Memoise(r Renderer) Renderer { return &memoise{r, gcache.New(10).LRU().Build()} }

// Render produces a set of RenderableNodes given a Report.
// Ideally, it just retrieves it from the cache, otherwise it calls through to
// `r` and stores the result.
func (m *memoise) Render(rpt report.Report) RenderableNodes {
key := ""
v := reflect.ValueOf(m.Renderer)
switch v.Kind() {
case reflect.Ptr, reflect.Func:
key = fmt.Sprintf("%s-%x", rpt.ID, v.Pointer())
default:
return m.Renderer.Render(rpt)
}
if result, err := renderCache.Get(key); err == nil {
if result, err := m.cache.Get(rpt.ID); err == nil {
return result.(RenderableNodes)
}
output := m.Renderer.Render(rpt)
renderCache.Set(key, output)
m.cache.Set(rpt.ID, output)
return output
}

// ResetCache blows away the rendered node cache.
func ResetCache() {
renderCache.Purge()
func (m *memoise) ResetCache() {
m.cache.Purge()
m.Renderer.ResetCache()
}
10 changes: 10 additions & 0 deletions render/memoise_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type renderFunc func(r report.Report) render.RenderableNodes

func (f renderFunc) Render(r report.Report) render.RenderableNodes { return f(r) }
func (f renderFunc) Stats(r report.Report) render.Stats { return render.Stats{} }
func (f renderFunc) ResetCache() {}

func TestMemoise(t *testing.T) {
calls := 0
Expand Down Expand Up @@ -48,4 +49,13 @@ func TestMemoise(t *testing.T) {
if calls != 2 {
t.Errorf("Expected renderer to have been called again for a different report")
}

m.ResetCache()
result4 := m.Render(rpt1)
if !reflect.DeepEqual(result1, result4) {
t.Errorf("Expected original result to be returned: %s", test.Diff(result1, result4))
}
if calls != 3 {
t.Errorf("Expected renderer to have been called again after cache reset")
}
}
8 changes: 8 additions & 0 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
type Renderer interface {
Render(report.Report) RenderableNodes
Stats(report.Report) Stats
ResetCache()
}

// Stats is the type returned by Renderer.Stats
Expand Down Expand Up @@ -49,6 +50,13 @@ func (r *Reduce) Stats(rpt report.Report) Stats {
return result
}

// ResetCache blows away the rendered node cache.
func (r *Reduce) ResetCache() {
for _, renderer := range *r {
renderer.ResetCache()
}
}

// Map is a Renderer which produces a set of RenderableNodes from the set of
// RenderableNodes produced by another Renderer.
type Map struct {
Expand Down
9 changes: 3 additions & 6 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ type mockRenderer struct {
render.RenderableNodes
}

func (m mockRenderer) Render(rpt report.Report) render.RenderableNodes {
return m.RenderableNodes
}
func (m mockRenderer) Stats(rpt report.Report) render.Stats {
return render.Stats{}
}
func (m mockRenderer) Render(rpt report.Report) render.RenderableNodes { return m.RenderableNodes }
func (m mockRenderer) Stats(rpt report.Report) render.Stats { return render.Stats{} }
func (m mockRenderer) ResetCache() {}

func TestReduceRender(t *testing.T) {
renderer := render.Reduce([]render.Renderer{
Expand Down
3 changes: 3 additions & 0 deletions render/selectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func (t TopologySelector) Stats(r report.Report) Stats {
return Stats{}
}

// ResetCache implements Renderer
func (t TopologySelector) ResetCache() {}

// MakeRenderableNodes converts a topology to a set of RenderableNodes
func MakeRenderableNodes(t report.Topology) RenderableNodes {
result := RenderableNodes{}
Expand Down

0 comments on commit a0a60ca

Please sign in to comment.