feat: machine performance indicators
This commit is contained in:
parent
9896b44178
commit
1a166bc279
9 changed files with 189 additions and 23 deletions
|
|
@ -1,5 +1,7 @@
|
|||
const _ = require('lodash/fp')
|
||||
const pgp = require('pg-promise')()
|
||||
const axios = require('axios')
|
||||
const uuid = require('uuid')
|
||||
|
||||
const db = require('./db')
|
||||
const pairing = require('./pairing')
|
||||
|
|
@ -39,11 +41,14 @@ function getMachineNames (config) {
|
|||
const unresponsiveStatus = { label: 'Unresponsive', type: 'error' }
|
||||
const stuckStatus = { label: 'Stuck', type: 'error' }
|
||||
|
||||
return Promise.all([getMachines(), getConfig(config)])
|
||||
.then(([machines, config]) => Promise.all(
|
||||
[machines, checkPings(machines), dbm.machineEvents(), config]
|
||||
return Promise.all([getMachines(), getConfig(config), getNetworkHeartbeat(), getNetworkPerformance()])
|
||||
.then(([rawMachines, config, heartbeat, performance]) => Promise.all(
|
||||
[rawMachines, checkPings(rawMachines), dbm.machineEvents(), config, heartbeat, performance]
|
||||
))
|
||||
.then(([machines, pings, events, config]) => {
|
||||
.then(([rawMachines, pings, events, config, heartbeat, performance]) => {
|
||||
const mergeByDeviceId = (x, y) => _.values(_.merge(_.keyBy('deviceId', x), _.keyBy('deviceId', y)))
|
||||
const machines = mergeByDeviceId(mergeByDeviceId(rawMachines, heartbeat), performance)
|
||||
|
||||
const getStatus = (ping, stuck) => {
|
||||
if (ping && ping.age) return unresponsiveStatus
|
||||
|
||||
|
|
@ -66,7 +71,6 @@ function getMachineNames (config) {
|
|||
|
||||
return _.assign(r, { cashOut, statuses })
|
||||
}
|
||||
|
||||
return _.map(addName, machines)
|
||||
})
|
||||
}
|
||||
|
|
@ -143,4 +147,56 @@ function setMachine (rec) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { getMachineName, getMachines, getMachine, getMachineNames, setMachine }
|
||||
function updateNetworkPerformance (deviceId, data) {
|
||||
const downloadSpeed = _.head(data)
|
||||
const dbData = {
|
||||
device_id: deviceId,
|
||||
download_speed: downloadSpeed.speed,
|
||||
created: new Date()
|
||||
}
|
||||
const cs = new pgp.helpers.ColumnSet(['device_id', 'download_speed', 'created'],
|
||||
{ table: 'machine_network_performance' })
|
||||
const onConflict = ' ON CONFLICT (device_id) DO UPDATE SET ' +
|
||||
cs.assignColumns({ from: 'EXCLUDED', skip: ['device_id'] })
|
||||
const upsert = pgp.helpers.insert(dbData, cs) + onConflict
|
||||
return db.none(upsert)
|
||||
}
|
||||
|
||||
function updateNetworkHeartbeat (deviceId, data) {
|
||||
const avgResponseTime = _.meanBy(e => _.toNumber(e.averageResponseTime), data)
|
||||
const avgPacketLoss = _.meanBy(e => _.toNumber(e.packetLoss), data)
|
||||
const dbData = {
|
||||
id: uuid.v4(),
|
||||
device_id: deviceId,
|
||||
average_response_time: avgResponseTime,
|
||||
average_packet_loss: avgPacketLoss
|
||||
}
|
||||
const sql = pgp.helpers.insert(dbData, null, 'machine_network_heartbeat')
|
||||
return db.none(sql)
|
||||
}
|
||||
|
||||
function getNetworkPerformance () {
|
||||
const sql = `SELECT device_id, download_speed FROM machine_network_performance`
|
||||
return db.manyOrNone(sql)
|
||||
.then(res => _.map(_.mapKeys(_.camelCase))(res))
|
||||
}
|
||||
|
||||
function getNetworkHeartbeat () {
|
||||
const sql = `SELECT AVG(average_response_time) AS response_time, AVG(average_packet_loss) AS packet_loss, device_id
|
||||
FROM machine_network_heartbeat
|
||||
GROUP BY device_id`
|
||||
return db.manyOrNone(sql)
|
||||
.then(res => _.map(_.mapKeys(_.camelCase))(res))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMachineName,
|
||||
getMachines,
|
||||
getMachine,
|
||||
getMachineNames,
|
||||
setMachine,
|
||||
updateNetworkPerformance,
|
||||
updateNetworkHeartbeat,
|
||||
getNetworkPerformance,
|
||||
getNetworkHeartbeat
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ const typeDef = gql`
|
|||
cassette2: Int
|
||||
statuses: [MachineStatus]
|
||||
latestEvent: MachineEvent
|
||||
downloadSpeed: String
|
||||
responseTime: String
|
||||
packetLoss: String
|
||||
}
|
||||
|
||||
type MachineEvent {
|
||||
|
|
|
|||
|
|
@ -289,6 +289,23 @@ function plugins (settings, deviceId) {
|
|||
])
|
||||
}
|
||||
|
||||
function pruneMachinesHeartbeat () {
|
||||
const sql = `DELETE
|
||||
FROM
|
||||
machine_network_heartbeat h
|
||||
USING (SELECT
|
||||
device_id,
|
||||
max(created) as lastEntry
|
||||
FROM
|
||||
machine_network_heartbeat
|
||||
GROUP BY
|
||||
device_id) d
|
||||
WHERE
|
||||
d.device_id = h.device_id
|
||||
AND h.created < d.lastEntry`
|
||||
db.none(sql)
|
||||
}
|
||||
|
||||
function isHd (tx) {
|
||||
return wallet.isHd(settings, tx)
|
||||
}
|
||||
|
|
@ -795,7 +812,8 @@ function plugins (settings, deviceId) {
|
|||
sell,
|
||||
getNotificationConfig,
|
||||
notifyOperator,
|
||||
fetchCurrentConfigVersion
|
||||
fetchCurrentConfigVersion,
|
||||
pruneMachinesHeartbeat
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const LOGS_CLEAR_INTERVAL = 1 * T.day
|
|||
const SANCTIONS_INITIAL_DOWNLOAD_INTERVAL = 5 * T.minutes
|
||||
const SANCTIONS_UPDATE_INTERVAL = 1 * T.week
|
||||
const RADAR_UPDATE_INTERVAL = 5 * T.minutes
|
||||
const PRUNE_MACHINES_HEARBEAT = 1 * T.day
|
||||
|
||||
const CHECK_NOTIFICATION_INTERVAL = 20 * T.seconds
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ function start (__settings) {
|
|||
setInterval(initialSanctionsDownload, SANCTIONS_INITIAL_DOWNLOAD_INTERVAL)
|
||||
setInterval(updateAndLoadSanctions, SANCTIONS_UPDATE_INTERVAL)
|
||||
setInterval(updateCoinAtmRadar, RADAR_UPDATE_INTERVAL)
|
||||
setInterval(() => pi().pruneMachinesHeartbeat(), PRUNE_MACHINES_HEARBEAT)
|
||||
}
|
||||
|
||||
module.exports = { start, reload }
|
||||
|
|
|
|||
|
|
@ -1,25 +1,17 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const { getMachine } = require('../machine-loader')
|
||||
const { updateNetworkHeartbeat, updateNetworkPerformance } = require('../machine-loader')
|
||||
|
||||
function networkHeartbeat (req, res, next) {
|
||||
return getMachine(req.deviceId)
|
||||
.then(machine => {
|
||||
console.log(`${machine.name} network heartbeat:`)
|
||||
console.log(req.body)
|
||||
return res.status(200).send({ status: 'OK' })
|
||||
})
|
||||
return updateNetworkHeartbeat(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
function networkPerformance (req, res, next) {
|
||||
return getMachine(req.deviceId)
|
||||
.then(machine => {
|
||||
console.log(`${machine.name} network performance:`)
|
||||
console.log(req.body)
|
||||
return res.status(200).send({ status: 'OK' })
|
||||
})
|
||||
return updateNetworkPerformance(req.deviceId, req.body)
|
||||
.then(() => res.status(200).send({ status: 'OK' }))
|
||||
.catch(next)
|
||||
}
|
||||
|
||||
|
|
|
|||
26
migrations/1626275844773-add-machine-network-performance.js
Normal file
26
migrations/1626275844773-add-machine-network-performance.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const db = require('./db')
|
||||
|
||||
exports.up = function (next) {
|
||||
var sql = [
|
||||
'drop table if exists machine_network_heartbeat',
|
||||
'drop table if exists machine_network_performance',
|
||||
`create table machine_network_performance (
|
||||
device_id text PRIMARY KEY,
|
||||
download_speed numeric NOT NULL,
|
||||
created timestamptz NOT NULL default now()
|
||||
)`,
|
||||
`create table machine_network_heartbeat (
|
||||
id uuid PRIMARY KEY,
|
||||
device_id text not null,
|
||||
average_response_time numeric NOT NULL,
|
||||
average_packet_loss numeric NOT NULL,
|
||||
created timestamptz NOT NULL default now()
|
||||
)`
|
||||
]
|
||||
|
||||
db.multi(sql, next)
|
||||
}
|
||||
|
||||
exports.down = function (next) {
|
||||
next()
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import React, { useState } from 'react'
|
||||
|
|
@ -88,6 +89,37 @@ const Overview = ({ data, onActionSuccess }) => {
|
|||
<P>{makeLastPing(data.lastPing)}</P>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.rowItem}>
|
||||
<Label3 className={classes.label3}>Network speed</Label3>
|
||||
<P>
|
||||
{data.downloadSpeed
|
||||
? new BigNumber(data.downloadSpeed).toFixed(4).toString() +
|
||||
' MB/s'
|
||||
: 'unavailable'}
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.rowItem}>
|
||||
<Label3 className={classes.label3}>Latency</Label3>
|
||||
<P>
|
||||
{data.responseTime
|
||||
? new BigNumber(data.responseTime).toFixed(3).toString() + ' ms'
|
||||
: 'unavailable'}
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.rowItem}>
|
||||
<Label3 className={classes.label3}>Loss</Label3>
|
||||
<P>
|
||||
{data.packetLoss
|
||||
? new BigNumber(data.packetLoss).toFixed(3).toString() + ' %'
|
||||
: 'unavailable'}
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.row}>
|
||||
<div className={classes.rowItem}>
|
||||
{' '}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ const GET_INFO = gql`
|
|||
label
|
||||
type
|
||||
}
|
||||
downloadSpeed
|
||||
responseTime
|
||||
packetLoss
|
||||
}
|
||||
config
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import gql from 'graphql-tag'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
|
|
@ -34,6 +35,9 @@ const GET_MACHINES = gql`
|
|||
label
|
||||
type
|
||||
}
|
||||
downloadSpeed
|
||||
responseTime
|
||||
packetLoss
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
@ -58,7 +62,7 @@ const MachineStatus = () => {
|
|||
const elements = [
|
||||
{
|
||||
header: 'Machine Name',
|
||||
width: 250,
|
||||
width: 150,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => (
|
||||
|
|
@ -76,18 +80,48 @@ const MachineStatus = () => {
|
|||
},
|
||||
{
|
||||
header: 'Status',
|
||||
width: 350,
|
||||
width: 150,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => <MainStatus statuses={m.statuses} />
|
||||
},
|
||||
{
|
||||
header: 'Last ping',
|
||||
width: 200,
|
||||
width: 175,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m => (m.lastPing ? moment(m.lastPing).fromNow() : 'unknown')
|
||||
},
|
||||
{
|
||||
header: 'Network speed',
|
||||
width: 150,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m =>
|
||||
m.downloadSpeed
|
||||
? new BigNumber(m.downloadSpeed).toFixed(4).toString() + ' MB/s'
|
||||
: 'unavailable'
|
||||
},
|
||||
{
|
||||
header: 'Latency',
|
||||
width: 150,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m =>
|
||||
m.responseTime
|
||||
? new BigNumber(m.responseTime).toFixed(3).toString() + ' ms'
|
||||
: 'unavailable'
|
||||
},
|
||||
{
|
||||
header: 'Loss',
|
||||
width: 125,
|
||||
size: 'sm',
|
||||
textAlign: 'left',
|
||||
view: m =>
|
||||
m.packetLoss
|
||||
? new BigNumber(m.packetLoss).toFixed(3).toString() + ' %'
|
||||
: 'unavailable'
|
||||
},
|
||||
{
|
||||
header: 'Software Version',
|
||||
width: 200,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue