Files
weave-scope/render/memoise.go
Matthias Radestock 5bdf46956d optimisation: make Memoise(Memoise(...)) only memoise once
All outputs of Memoise() are fixpoints to the function, i.e. feeding
them as inputs to the function just returns them.

We don't hit this optimisation in current code but have had instances
in the past.
2017-11-23 17:03:33 +00:00

81 lines
1.6 KiB
Go

package render
import (
"fmt"
"math/rand"
"sync"
"github.com/bluele/gcache"
"github.com/weaveworks/scope/report"
)
// renderCache is keyed on the combination of Memoiser and report
// id. It contains promises of report.Nodes, which result from
// rendering the report with the Memoiser's renderer.
//
// The use of promises ensures that in the absence of cache evictions
// a memoiser will only ever render a report once, even when Render()
// is invoked concurrently.
var renderCache = gcache.New(100).LRU().Build()
type memoise struct {
sync.Mutex
Renderer
id string
}
// Memoise wraps the renderer in a loving embrace of caching.
func Memoise(r Renderer) Renderer {
if _, ok := r.(*memoise); ok {
return r // fixpoint
}
return &memoise{
Renderer: r,
id: fmt.Sprintf("%x", rand.Int63()),
}
}
// Render produces a set of Nodes given a Report. Ideally, it just
// retrieves a promise from the cache and returns its value, otherwise
// it stores a new promise and fulfils it by calling through to
// m.Renderer.
func (m *memoise) Render(rpt report.Report) Nodes {
key := fmt.Sprintf("%s-%s", rpt.ID, m.id)
m.Lock()
v, err := renderCache.Get(key)
if err == nil {
m.Unlock()
return v.(*promise).Get()
}
promise := newPromise()
renderCache.Set(key, promise)
m.Unlock()
output := m.Renderer.Render(rpt)
promise.Set(output)
return output
}
type promise struct {
val Nodes
done chan struct{}
}
func newPromise() *promise {
return &promise{done: make(chan struct{})}
}
func (p *promise) Set(val Nodes) {
p.val = val
close(p.done)
}
func (p *promise) Get() Nodes {
<-p.done
return p.val
}