mirror of
https://github.com/weaveworks/scope.git
synced 2026-02-14 18:09:59 +00:00
* Pass Go context down to Renderers This is useful for cancellation or tracing. * Add tracing spans to app Also log things like number of nodes in Map, total number of reports.
82 lines
1.6 KiB
Go
82 lines
1.6 KiB
Go
package render
|
|
|
|
import (
|
|
"context"
|
|
"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(ctx context.Context, 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(ctx, 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
|
|
}
|