mirror of https://github.com/grafana/grafana
Dashboard: keep live timeseries moving left (v2) (#37769)
parent
32e11434da
commit
8c4c05493b
@ -0,0 +1,113 @@ |
||||
import { dateMath, dateTime, TimeRange } from '@grafana/data'; |
||||
import { PanelChrome } from './PanelChrome'; |
||||
|
||||
// target is 20hz (50ms), but we poll at 100ms to smooth out jitter
|
||||
const interval = 100; |
||||
|
||||
interface LiveListener { |
||||
last: number; |
||||
intervalMs: number; |
||||
panel: PanelChrome; |
||||
} |
||||
|
||||
class LiveTimer { |
||||
listeners: LiveListener[] = []; |
||||
|
||||
budget = 1; |
||||
threshold = 1.5; // trial and error appears about right
|
||||
ok = true; |
||||
lastUpdate = Date.now(); |
||||
|
||||
isLive = false; // the dashboard time range ends in "now"
|
||||
timeRange?: TimeRange; |
||||
liveTimeOffset = 0; |
||||
|
||||
/** Called when the dashboard time range changes */ |
||||
setLiveTimeRange(v?: TimeRange) { |
||||
this.timeRange = v; |
||||
this.isLive = v?.raw?.to === 'now'; |
||||
|
||||
if (this.isLive) { |
||||
const from = dateMath.parse(v!.raw.from, false)?.valueOf()!; |
||||
const to = dateMath.parse(v!.raw.to, true)?.valueOf()!; |
||||
this.liveTimeOffset = to - from; |
||||
|
||||
for (const listener of this.listeners) { |
||||
listener.intervalMs = getLiveTimerInterval(this.liveTimeOffset, listener.panel.props.width); |
||||
} |
||||
} |
||||
} |
||||
|
||||
listen(panel: PanelChrome) { |
||||
this.listeners.push({ |
||||
last: this.lastUpdate, |
||||
panel: panel, |
||||
intervalMs: getLiveTimerInterval( |
||||
60000, // 1min
|
||||
panel.props.width |
||||
), |
||||
}); |
||||
} |
||||
|
||||
remove(panel: PanelChrome) { |
||||
this.listeners = this.listeners.filter((v) => v.panel !== panel); |
||||
} |
||||
|
||||
updateInterval(panel: PanelChrome) { |
||||
if (!this.timeRange || !this.isLive) { |
||||
return; |
||||
} |
||||
for (const listener of this.listeners) { |
||||
if (listener.panel === panel) { |
||||
listener.intervalMs = getLiveTimerInterval(this.liveTimeOffset, listener.panel.props.width); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Called at the consistent dashboard interval
|
||||
measure = () => { |
||||
const now = Date.now(); |
||||
this.budget = (now - this.lastUpdate) / interval; |
||||
this.ok = this.budget <= this.threshold; |
||||
this.lastUpdate = now; |
||||
|
||||
// For live dashboards, listen to changes
|
||||
if (this.ok && this.isLive && this.timeRange) { |
||||
// when the time-range is relative fire events
|
||||
let tr: TimeRange | undefined = undefined; |
||||
for (const listener of this.listeners) { |
||||
if (!listener.panel.props.isInView) { |
||||
continue; |
||||
} |
||||
|
||||
const elapsed = now - listener.last; |
||||
if (elapsed >= listener.intervalMs) { |
||||
if (!tr) { |
||||
const { raw } = this.timeRange; |
||||
tr = { |
||||
raw, |
||||
from: dateTime(now - this.liveTimeOffset), |
||||
to: dateTime(now), |
||||
}; |
||||
} |
||||
listener.panel.liveTimeChanged(tr); |
||||
listener.last = now; |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
const FIVE_MINS = 5 * 60 * 1000; |
||||
|
||||
export function getLiveTimerInterval(delta: number, width: number): number { |
||||
const millisPerPixel = Math.ceil(delta / width / 100) * 100; |
||||
if (millisPerPixel > FIVE_MINS) { |
||||
return FIVE_MINS; |
||||
} |
||||
return millisPerPixel; |
||||
} |
||||
|
||||
export const liveTimer = new LiveTimer(); |
||||
setInterval(liveTimer.measure, interval); |
@ -1,28 +0,0 @@ |
||||
let lastUpdate = Date.now(); |
||||
|
||||
/** |
||||
* This object indicats how overloaded the main thread is |
||||
*/ |
||||
export const perf = { |
||||
budget: 1, |
||||
threshold: 1.5, // trial and error appears about right
|
||||
ok: true, |
||||
last: lastUpdate, |
||||
}; |
||||
|
||||
// Expose this as a global object so it can be changed locally
|
||||
// NOTE: when we are confident this is the right budget, this should be removed
|
||||
(window as any).grafanaStreamingPerf = perf; |
||||
|
||||
// target is 20hz (50ms), but we poll at 100ms to smooth out jitter
|
||||
const interval = 100; |
||||
|
||||
function measure() { |
||||
const now = Date.now(); |
||||
perf.last = now; |
||||
perf.budget = (now - lastUpdate) / interval; |
||||
perf.ok = perf.budget <= perf.threshold; |
||||
lastUpdate = now; |
||||
} |
||||
|
||||
setInterval(measure, interval); |
Loading…
Reference in new issue