import { inject , injectable } from "inversify" ; import TYPES from "../../common/types" ; import { Log } from "../../common/log" ; import { SystemInfoAttribute } from "../system-info/system-info.model" ; import { SystemInfoService } from "../system-info/system-info.service" ; import { SystemStats } from "./system-stats.model" ; import { SystemStatsRepository } from "./system-stats.repo" ; import { SystemStatsDto , SystemStatsInput , RpcStatsInput , ServerStatsInput , SingleServerStatsInput } from "./system-stats.dto" ; interface PropertyLookupMap { [ property ] : Map < number , number > ; } interface SystemInfo { rpm : number ; fpm : number ; hosts : Map < string , ServerInfo > ; } interface ServerInfo { rpm : number ; fpm : number ; } interface SystemStatsService { getSystemStats ( ) : Promise < SystemStatsDto > ; saveSystemStats ( time , stats ) : Promise < void > ; } @ injectable ( ) class SystemStatsServiceImpl implements SystemStatsService { private static readonly LOGGER = Log . getLogger ( "SystemStatsService" ) ; private static readonly MAX_STATS : number = 20 ; public constructor ( @ inject ( TYPES . SystemStatsRepository ) private systemStatsRepository , @ inject ( TYPES . SystemInfoService ) private systemInfoService ) { } private getLinkKey ( source , target ) { return `${ Buffer . from ( source ) . toString ( "base64" ) }-${ Buffer . from ( target ) . toString ( "base64" ) }` ; } private fromLinkKey ( key ) { const parts = key . split ( "-" ) ; return [ Buffer . from ( parts [ 0 ] , "base64" ) . toString ( "utf8" ) , Buffer . from ( parts [ 1 ] , "base64" ) . toString ( "utf8" ) ] ; } private calcDataSum ( data , begin , end ) { let sum = 0 ; for ( let time = begin ; time <= end ; time ++ ) { if ( data . has ( time ) ) { sum += data . get ( time ) ; } } return sum ; } public getSystemStats ( ) { let holder = new Map < string , any > ( ) ; let result = { time : 0 , nodes : [ ] , links : [ ] } ; return this . systemStatsRepository . getLatestStatsTime ( ) . then ( ( time ) => { if ( time === null ) return [ ] ; holder . set ( "time" , time ) ; return this . systemStatsRepository . getSystemStats ( time - 14 * 60 , time ) ; } ) . then ( ( stats ) => { if ( stats . length < 1 ) return result ; result . time = holder . get ( "time" ) ; let nodeMap = new Map < string , PropertyLookupMap > ( ) ; let linkMap = new Map < string , PropertyLookupMap > ( ) ; stats . forEach ( ( stat ) => { let time = stat . time ; for ( let node of stat . nodes ) { let key = node . name ; if ( ! nodeMap . has ( key ) ) { nodeMap . set ( key , { "rpm" : new Map < number , number > ( ) , "fpm" : new Map < number , number > ( ) } ) ; } nodeMap . get ( key ) [ "rpm" ] . set ( time , node . rpm ) ; nodeMap . get ( key ) [ "fpm" ] . set ( time , node . fpm ) ; } for ( let link of stat . links ) { let key = this . getLinkKey ( link . source , link . target ) ; if ( ! linkMap . has ( key ) ) { linkMap . set ( key , { "rpm" : new Map < number , number > ( ) , "fpm" : new Map < number , number > ( ) } ) ; } linkMap . get ( key ) [ "rpm" ] . set ( time , link . rpm ) ; linkMap . get ( key ) [ "fpm" ] . set ( time , link . fpm ) ; } } ) ; let calcNodeAvg = ( node , property , minutes ) : number => { let end = holder . get ( "time" ) ; let begin = end - minutes * 60 ; let data = nodeMap . get ( node ) [ property ] ; let sum = this . calcDataSum ( data , begin , end ) ; return parseFloat ( ( sum / minutes ) . toFixed ( 3 ) ) ; } ; let calcLinkAvg = ( link , property , minutes ) : number => { let end = holder . get ( "time" ) ; let begin = end - minutes * 60 ; let data = linkMap . get ( link ) [ property ] ; let sum = this . calcDataSum ( data , begin , end ) ; return parseFloat ( ( sum / minutes ) . toFixed ( 3 ) ) ; } ; for ( let node of nodeMap . keys ( ) ) { let rpm = [ calcNodeAvg ( node , "rpm" , 1 ) , calcNodeAvg ( node , "rpm" , 5 ) , calcNodeAvg ( node , "rpm" , 15 ) ] ; let fpm = [ calcNodeAvg ( node , "fpm" , 1 ) , calcNodeAvg ( node , "fpm" , 5 ) , calcNodeAvg ( node , "fpm" , 15 ) ] ; result . nodes . push ( [ node , rpm , fpm ] ) ; } for ( let link of linkMap . keys ( ) ) { let rpm = [ calcLinkAvg ( link , "rpm" , 60 ) , calcLinkAvg ( link , "rpm" , 5 * 60 ) , calcLinkAvg ( link , "rpm" , 15 * 60 ) ] ; let fpm = [ calcLinkAvg ( link , "fpm" , 60 ) , calcLinkAvg ( link , "fpm" , 5 * 60 ) , calcLinkAvg ( link , "fpm" , 15 * 60 ) ] ; const parts = this . fromLinkKey ( link ) ; let source = parts [ 0 ] ; let target = parts [ 1 ] ; result . links . push ( [ source , target , rpm , fpm ] ) ; } return result ; } ) ; } public saveSystemStats ( time , stats ) { let systemStats = { time : time , nodes : [ ] , links : [ ] } ; let systems = new Map < string , SystemInfo > ( ) ; let systemInfos = new Array < SystemInfoAttribute > ( ) ; for ( let system in stats ) { if ( system === undefined ) continue ; if ( ! systems . has ( system ) ) { systems . set ( system , { rpm : 0 , fpm : 0 , hosts : new Map < string , ServerInfo > ( ) } ) ; } for ( let target in stats [ system ] ) { if ( target === undefined ) continue ; if ( ! systems . has ( target ) ) { systems . set ( target , { rpm : 0 , fpm : 0 , hosts : new Map < string , ServerInfo > ( ) } ) ; } let targetSystem = systems . get ( target ) ; let rpm = 0 ; let fpm = 0 ; for ( let host in stats [ system ] [ target ] ) { if ( host === undefined ) continue ; if ( ! targetSystem . hosts . has ( host ) ) { targetSystem . hosts . set ( host , { rpm : 0 , fpm : 0 } ) ; } let info = stats [ system ] [ target ] [ host ] ; let targetServer = targetSystem . hosts . get ( host ) ; targetServer . rpm += info . rpm || 0 ; targetServer . fpm += info . fpm || 0 ; rpm += info . rpm || 0 ; fpm += info . fpm || 0 ; systemInfos . push ( { time : time , name : target , node : host , source : system , rpm : info . rpm , fpm : info . fpm } ) ; } targetSystem . rpm += rpm ; targetSystem . fpm += fpm ; systemStats . links . push ( { source : system , target : target , fpm : fpm , rpm : rpm } ) ; } } systems . forEach ( ( info , system ) => { let fpm = info . fpm ; let rpm = info . rpm ; systemStats . nodes . push ( { name : system , fpm : fpm , rpm : rpm } ) ; } ) ; return this . systemStatsRepository . saveSystemStats ( systemStats ) . then ( ( ) => { return this . systemInfoService . saveSystemInfo ( systemInfos ) ; } ) ; } } export { SystemStats , SystemStatsService , SystemStatsServiceImpl } ;