fix(xo-web): enhance RRD stats (#6903)

- fix infinite requests
- avoid duplicate requests
This commit is contained in:
Mathieu
2023-06-28 15:17:00 +02:00
committed by GitHub
parent 161c20b534
commit 38439cbc43
5 changed files with 85 additions and 59 deletions
+1
View File
@@ -24,6 +24,7 @@
- [Remote] Fix `remote is disabled` error when editing a disabled remote
- [Settings/Servers] Fix connectiong using an explicit IPv6 address
- [Backups/Health check] Use the right SR for health check during replication job (PR [#6902](https://github.com/vatesfr/xen-orchestra/pull/6902))
- [RRD stats] Improve RRD stats performance (PR [#6903](https://github.com/vatesfr/xen-orchestra/pull/6903))
### Packages to release
+7
View File
@@ -12,6 +12,13 @@ export const DEFAULT_GRANULARITY = {
value: 'lastTenMinutes',
}
export const INTERVAL_BY_GRANULARITY = {
seconds: 5,
minutes: 60,
hours: 3600,
days: 86400,
}
const OPTIONS = [
DEFAULT_GRANULARITY,
{
+13 -3
View File
@@ -18,19 +18,29 @@ export default class MiniStats extends Component {
fetchStats: PropTypes.func.isRequired,
}
state = {
statsIsPending: false,
}
_fetch = () => {
if (this.state.statsIsPending) {
return
}
this.setState({
statsIsPending: true,
})
this.props.fetch().then(stats => {
this.setState({ stats })
this._timeout = setTimeout(this._fetch, 5e3)
this.setState({ stats, statsIsPending: false })
})
}
componentWillMount() {
this._fetch()
this._interval = setInterval(this._fetch, 5e3)
}
componentWillUnmount() {
clearTimeout(this._timeout)
clearInterval(this._interval)
}
render() {
+32 -28
View File
@@ -4,7 +4,7 @@ import Icon from 'icon'
import React from 'react'
import Tooltip from 'tooltip'
import { Container, Row, Col } from 'grid'
import { DEFAULT_GRANULARITY, fetchStats, SelectGranularity } from 'stats'
import { DEFAULT_GRANULARITY, fetchStats, SelectGranularity, INTERVAL_BY_GRANULARITY } from 'stats'
import { Toggle } from 'form'
import { CpuLineChart, MemoryLineChart, PifLineChart, LoadLineChart } from 'xo-line-chart'
@@ -12,48 +12,52 @@ export default class HostStats extends Component {
state = {
granularity: DEFAULT_GRANULARITY,
useCombinedValues: false,
statsIsPending: false,
}
loop(host = this.props.host) {
if (this.cancel) {
this.cancel()
fetchHostStats = () => {
if (this.state.statsIsPending) {
return
}
const host = this.props.host
if (host.power_state !== 'Running') {
return
}
let cancelled = false
this.cancel = () => {
cancelled = true
if (this.props.statsOverview?.interval === INTERVAL_BY_GRANULARITY[this.state.granularity.granularity]) {
this.setState({
stats: this.props.statsOverview,
selectStatsLoading: false,
})
return
}
this.setState({
statsIsPending: true,
})
fetchStats(host, 'host', this.state.granularity).then(stats => {
if (cancelled) {
return
}
this.cancel = null
clearTimeout(this.timeout)
this.setState(
{
stats,
selectStatsLoading: false,
},
() => {
this.timeout = setTimeout(this.loop, stats.interval * 1000)
}
)
this.setState({
stats,
selectStatsLoading: false,
statsIsPending: false,
})
})
}
loop = ::this.loop
initFetchHostStats() {
this.fetchHostStats()
this.interval = setInterval(this.fetchHostStats, INTERVAL_BY_GRANULARITY[this.state.granularity.granularity] * 1000)
}
initFetchHostStats = ::this.initFetchHostStats
componentWillMount() {
this.loop()
this.initFetchHostStats()
}
componentWillUnmount() {
clearTimeout(this.timeout)
clearInterval(this.interval)
}
componentWillReceiveProps(props) {
@@ -61,7 +65,7 @@ export default class HostStats extends Component {
const hostNext = props.host
if (hostCur.power_state !== 'Running' && hostNext.power_state === 'Running') {
this.loop(hostNext)
this.initFetchHostStats(hostNext)
} else if (hostCur.power_state === 'Running' && hostNext.power_state !== 'Running') {
this.setState({
stats: undefined,
@@ -70,14 +74,14 @@ export default class HostStats extends Component {
}
handleSelectStats(granularity) {
clearTimeout(this.timeout)
clearInterval(this.interval)
this.setState(
{
granularity,
selectStatsLoading: true,
},
this.loop
this.initFetchHostStats
)
}
handleSelectStats = ::this.handleSelectStats
+32 -28
View File
@@ -4,7 +4,7 @@ import Icon from 'icon'
import React from 'react'
import Tooltip from 'tooltip'
import { Container, Row, Col } from 'grid'
import { DEFAULT_GRANULARITY, fetchStats, SelectGranularity } from 'stats'
import { DEFAULT_GRANULARITY, fetchStats, INTERVAL_BY_GRANULARITY, SelectGranularity } from 'stats'
import { Toggle } from 'form'
import { CpuLineChart, MemoryLineChart, VifLineChart, XvdLineChart } from 'xo-line-chart'
@@ -12,48 +12,52 @@ export default class VmStats extends Component {
state = {
granularity: DEFAULT_GRANULARITY,
useCombinedValues: false,
statsIsPending: false,
}
loop(vm = this.props.vm) {
if (this.cancel) {
this.cancel()
fetchVmStats = () => {
if (this.state.statsIsPending) {
return
}
const vm = this.props.vm
if (vm.power_state !== 'Running') {
return
}
let cancelled = false
this.cancel = () => {
cancelled = true
if (this.props.statsOverview?.interval === INTERVAL_BY_GRANULARITY[this.state.granularity.granularity]) {
this.setState({
stats: this.props.statsOverview,
selectStatsLoading: false,
})
return
}
this.setState({
statsIsPending: true,
})
fetchStats(vm, 'vm', this.state.granularity).then(stats => {
if (cancelled) {
return
}
this.cancel = null
clearTimeout(this.timeout)
this.setState(
{
stats,
selectStatsLoading: false,
},
() => {
this.timeout = setTimeout(this.loop, stats.interval * 1000)
}
)
this.setState({
stats,
selectStatsLoading: false,
statsIsPending: false,
})
})
}
loop = ::this.loop
initFetchVmStats() {
this.fetchVmStats()
this.interval = setInterval(this.fetchVmStats, INTERVAL_BY_GRANULARITY[this.state.granularity.granularity] * 1000)
}
initFetchVmStats = ::this.initFetchVmStats
componentWillMount() {
this.loop()
this.initFetchVmStats()
}
componentWillUnmount() {
clearTimeout(this.timeout)
clearInterval(this.interval)
}
componentWillReceiveProps(props) {
@@ -61,7 +65,7 @@ export default class VmStats extends Component {
const vmNext = props.vm
if (vmCur.power_state !== 'Running' && vmNext.power_state === 'Running') {
this.loop(vmNext)
this.initFetchVmStats(vmNext)
} else if (vmCur.power_state === 'Running' && vmNext.power_state !== 'Running') {
this.setState({
stats: undefined,
@@ -70,14 +74,14 @@ export default class VmStats extends Component {
}
handleSelectStats(granularity) {
clearTimeout(this.timeout)
clearInterval(this.interval)
this.setState(
{
granularity,
selectStatsLoading: true,
},
this.loop
this.initFetchVmStats
)
}
handleSelectStats = ::this.handleSelectStats