2022-07-06 11:58:06 +02:00
import logger from "../../logger" ;
import DB from "../../database" ;
import lightningApi from "../../api/lightning/lightning-api-factory" ;
2022-04-19 17:37:06 +04:00
class LightningStatsUpdater {
constructor ( ) { }
2022-07-04 12:00:16 +02:00
public async $startService() {
2022-04-24 01:33:38 +04:00
logger . info ( 'Starting Stats service' ) ;
2022-04-19 17:37:06 +04:00
const now = new Date ( ) ;
const nextHourInterval = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) , Math . floor ( now . getHours ( ) / 1 ) + 1 , 0 , 0 , 0 ) ;
const difference = nextHourInterval . getTime ( ) - now . getTime ( ) ;
2022-07-04 12:00:16 +02:00
// setTimeout(() => {
setInterval ( async ( ) = > {
await this . $runTasks ( ) ;
2022-04-19 17:37:06 +04:00
} , 1000 * 60 * 60 ) ;
2022-07-04 12:00:16 +02:00
//}, difference);
2022-05-01 03:01:27 +04:00
2022-07-04 12:00:16 +02:00
await this . $runTasks ( ) ;
}
private async $runTasks() {
await this . $populateHistoricalData ( ) ;
await this . $logLightningStatsDaily ( ) ;
await this . $logNodeStatsDaily ( ) ;
2022-04-19 17:37:06 +04:00
}
2022-04-27 02:52:23 +04:00
private async $logNodeStatsDaily() {
2022-07-06 11:58:06 +02:00
logger . info ( ` Running daily node stats update... ` ) ;
2022-04-29 03:57:27 +04:00
const currentDate = new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ;
try {
const [ state ] : any = await DB . query ( ` SELECT string FROM state WHERE name = 'last_node_stats' ` ) ;
// Only store once per day
2022-05-01 03:01:27 +04:00
if ( state [ 0 ] . string === currentDate ) {
2022-04-29 03:57:27 +04:00
return ;
}
2022-04-19 17:37:06 +04:00
2022-05-16 00:01:53 +04:00
const query = ` SELECT nodes.public_key, c1.channels_count_left, c2.channels_count_right, c1.channels_capacity_left, c2.channels_capacity_right FROM nodes LEFT JOIN (SELECT node1_public_key, COUNT(id) AS channels_count_left, SUM(capacity) AS channels_capacity_left FROM channels WHERE channels.status < 2 GROUP BY node1_public_key) c1 ON c1.node1_public_key = nodes.public_key LEFT JOIN (SELECT node2_public_key, COUNT(id) AS channels_count_right, SUM(capacity) AS channels_capacity_right FROM channels WHERE channels.status < 2 GROUP BY node2_public_key) c2 ON c2.node2_public_key = nodes.public_key ` ;
2022-04-29 03:57:27 +04:00
const [ nodes ] : any = await DB . query ( query ) ;
2022-05-08 16:16:54 +04:00
// First run we won't have any nodes yet
if ( nodes . length < 10 ) {
return ;
}
2022-04-29 03:57:27 +04:00
for ( const node of nodes ) {
await DB . query (
2022-05-08 16:16:54 +04:00
` INSERT INTO node_stats(public_key, added, capacity, channels) VALUES (?, NOW(), ?, ?) ` ,
2022-05-05 23:19:24 +04:00
[ node . public_key , ( parseInt ( node . channels_capacity_left || 0 , 10 ) ) + ( parseInt ( node . channels_capacity_right || 0 , 10 ) ) ,
node . channels_count_left + node . channels_count_right ] ) ;
2022-04-29 03:57:27 +04:00
}
await DB . query ( ` UPDATE state SET string = ? WHERE name = 'last_node_stats' ` , [ currentDate ] ) ;
2022-07-06 11:58:06 +02:00
logger . info ( 'Daily node stats has updated.' ) ;
2022-04-29 03:57:27 +04:00
} catch ( e ) {
logger . err ( '$logNodeStatsDaily() error: ' + ( e instanceof Error ? e.message : e ) ) ;
2022-04-27 02:52:23 +04:00
}
}
2022-07-04 12:00:16 +02:00
// We only run this on first launch
private async $populateHistoricalData() {
2022-07-06 11:58:06 +02:00
logger . info ( ` Running historical stats population... ` ) ;
2022-07-04 12:00:16 +02:00
const startTime = '2018-01-13' ;
2022-04-19 17:37:06 +04:00
try {
2022-07-06 11:58:06 +02:00
const [ rows ] : any = await DB . query ( ` SELECT COUNT(*) FROM lightning_stats ` ) ;
2022-07-04 12:00:16 +02:00
// Only store once per day
if ( rows [ 0 ] [ 'COUNT(*)' ] > 0 ) {
return ;
}
const [ channels ] : any = await DB . query ( ` SELECT capacity, created, closing_date FROM channels ORDER BY created ASC ` ) ;
let date : Date = new Date ( startTime ) ;
const currentDate = new Date ( ) ;
while ( date < currentDate ) {
let totalCapacity = 0 ;
let channelsCount = 0 ;
for ( const channel of channels ) {
if ( new Date ( channel . created ) > date ) {
break ;
}
if ( channel . closing_date !== null && new Date ( channel . closing_date ) < date ) {
continue ;
}
totalCapacity += channel . capacity ;
channelsCount ++ ;
}
2022-07-06 11:58:06 +02:00
const query = ` INSERT INTO lightning_stats(
2022-07-04 12:00:16 +02:00
added ,
channel_count ,
node_count ,
total_capacity
)
VALUES ( FROM_UNIXTIME ( ? ) , ? , ? , ? ) ` ;
await DB . query ( query , [
date . getTime ( ) / 1000 ,
channelsCount ,
0 ,
totalCapacity ,
] ) ;
// Add one day and continue
date . setDate ( date . getDate ( ) + 1 ) ;
}
const [ nodes ] : any = await DB . query ( ` SELECT first_seen FROM nodes ORDER BY first_seen ASC ` ) ;
date = new Date ( startTime ) ;
while ( date < currentDate ) {
let nodeCount = 0 ;
for ( const node of nodes ) {
if ( new Date ( node . first_seen ) > date ) {
break ;
}
nodeCount ++ ;
}
2022-07-06 11:58:06 +02:00
const query = ` UPDATE lightning_stats SET node_count = ? WHERE added = FROM_UNIXTIME(?) ` ;
2022-07-04 12:00:16 +02:00
await DB . query ( query , [
nodeCount ,
date . getTime ( ) / 1000 ,
] ) ;
// Add one day and continue
date . setDate ( date . getDate ( ) + 1 ) ;
}
2022-07-06 11:58:06 +02:00
logger . info ( 'Historical stats populated.' ) ;
2022-07-04 12:00:16 +02:00
} catch ( e ) {
logger . err ( '$populateHistoricalData() error: ' + ( e instanceof Error ? e.message : e ) ) ;
}
}
private async $logLightningStatsDaily() {
2022-07-06 11:58:06 +02:00
logger . info ( ` Running lightning daily stats log... ` ) ;
2022-07-04 12:00:16 +02:00
const currentDate = new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ;
try {
const [ state ] : any = await DB . query ( ` SELECT string FROM state WHERE name = 'last_node_stats' ` ) ;
// Only store once per day
if ( state [ 0 ] . string === currentDate ) {
return ;
}
2022-05-01 03:01:27 +04:00
const networkGraph = await lightningApi . $getNetworkGraph ( ) ;
let total_capacity = 0 ;
for ( const channel of networkGraph . channels ) {
if ( channel . capacity ) {
total_capacity += channel . capacity ;
}
}
2022-04-27 02:52:23 +04:00
2022-07-06 11:58:06 +02:00
const query = ` INSERT INTO lightning_stats(
2022-04-19 17:37:06 +04:00
added ,
channel_count ,
node_count ,
2022-05-01 03:01:27 +04:00
total_capacity
2022-04-19 17:37:06 +04:00
)
2022-05-01 03:01:27 +04:00
VALUES ( NOW ( ) , ? , ? , ? ) ` ;
2022-04-19 17:37:06 +04:00
await DB . query ( query , [
2022-05-01 03:01:27 +04:00
networkGraph . channels . length ,
networkGraph . nodes . length ,
total_capacity ,
2022-04-19 17:37:06 +04:00
] ) ;
2022-07-06 11:58:06 +02:00
logger . info ( ` Lightning daily stats done. ` ) ;
2022-04-19 17:37:06 +04:00
} catch ( e ) {
2022-07-06 11:58:06 +02:00
logger . err ( '$logLightningStatsDaily() error: ' + ( e instanceof Error ? e.message : e ) ) ;
2022-04-19 17:37:06 +04:00
}
}
}
export default new LightningStatsUpdater ( ) ;