From 6bc42defefea5d4e0397b0930ee5dfea0d0289ef Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Tue, 16 Jan 2024 19:37:29 -0500 Subject: [PATCH] separate home graph into it's own table --- .../client-statistics.service.ts | 28 +++++++++++ src/ORM/home-graph/home-graph.entity.ts | 14 ++++++ src/ORM/home-graph/home-graph.module.ts | 15 ++++++ src/ORM/home-graph/home-graph.service.ts | 45 ++++++++++++++++++ src/ORM/rpc-block/rpc-block.service.ts | 15 ++++++ src/app.controller.ts | 6 ++- src/app.module.ts | 8 +++- src/services/app.service.ts | 47 ++++++++++++++++++- 8 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 src/ORM/home-graph/home-graph.entity.ts create mode 100644 src/ORM/home-graph/home-graph.module.ts create mode 100644 src/ORM/home-graph/home-graph.service.ts diff --git a/src/ORM/client-statistics/client-statistics.service.ts b/src/ORM/client-statistics/client-statistics.service.ts index b623040..ca270a8 100644 --- a/src/ORM/client-statistics/client-statistics.service.ts +++ b/src/ORM/client-statistics/client-statistics.service.ts @@ -85,6 +85,34 @@ export class ClientStatisticsService { } + public async getChartDataForSiteSince(date: Date) { + + const query = ` + SELECT + time AS label, + ROUND(((SUM(shares) * 4294967296) / 600)) AS data + FROM + client_statistics_entity AS entry + WHERE + entry.time > ${date.getTime()} + GROUP BY + time + ORDER BY + time + LIMIT 144; + `; + + const result: any[] = await this.clientStatisticsRepository.query(query); + + + return result.map(res => { + res.label = new Date(parseInt(res.label)).toISOString(); + return res; + }).slice(0, result.length - 1) + + } + + // public async getHashRateForAddress(address: string) { diff --git a/src/ORM/home-graph/home-graph.entity.ts b/src/ORM/home-graph/home-graph.entity.ts new file mode 100644 index 0000000..b62a6d7 --- /dev/null +++ b/src/ORM/home-graph/home-graph.entity.ts @@ -0,0 +1,14 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class HomeGraphEntity { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ type: 'bigint' }) + label: number; + + @Column({ type: 'bigint' }) + data: number; +} \ No newline at end of file diff --git a/src/ORM/home-graph/home-graph.module.ts b/src/ORM/home-graph/home-graph.module.ts new file mode 100644 index 0000000..6c9a4a6 --- /dev/null +++ b/src/ORM/home-graph/home-graph.module.ts @@ -0,0 +1,15 @@ +import { Global, Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { HomeGraphEntity } from './home-graph.entity'; +import { HomeGraphService } from './home-graph.service'; + + + +@Global() +@Module({ + imports: [TypeOrmModule.forFeature([HomeGraphEntity])], + providers: [HomeGraphService], + exports: [TypeOrmModule, HomeGraphService], +}) +export class HomeGraphModule { } \ No newline at end of file diff --git a/src/ORM/home-graph/home-graph.service.ts b/src/ORM/home-graph/home-graph.service.ts new file mode 100644 index 0000000..6175c96 --- /dev/null +++ b/src/ORM/home-graph/home-graph.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { HomeGraphEntity } from './home-graph.entity'; + +@Injectable() +export class HomeGraphService { + constructor( + @InjectRepository(HomeGraphEntity) private homeGraphRepository: Repository + ) { + + } + + public async getLatestTime(): Promise { + const result = await this.homeGraphRepository.createQueryBuilder('entity') + .select('MAX(entity.label)', 'maxNumber') + .getRawOne(); + + if (result.maxNumber == null) { + return new Date(new Date().getTime() - (24 * 60 * 60 * 1000)); + } + + return new Date(parseInt(result.maxNumber)); + } + + public async save(graph: { label: number, data: number }[]) { + await this.homeGraphRepository.insert(graph); + } + + public async getChartDataForSite(limit: number = 144 * 7) { + const records = await this.homeGraphRepository + .createQueryBuilder('homeGraph') + .orderBy('homeGraph.id', 'DESC') + .limit(limit) + .getRawMany(); + + return records.map(res => { + return { + label: new Date(parseInt(res.homeGraph_label)).toISOString(), + data: res.homeGraph_data + } + }); + } +} \ No newline at end of file diff --git a/src/ORM/rpc-block/rpc-block.service.ts b/src/ORM/rpc-block/rpc-block.service.ts index 71b5b6c..f6de1db 100644 --- a/src/ORM/rpc-block/rpc-block.service.ts +++ b/src/ORM/rpc-block/rpc-block.service.ts @@ -25,4 +25,19 @@ export class RpcBlockService { public saveBlock(blockHeight: number, data: string) { return this.rpcBlockRepository.update(blockHeight, { data }) } + + public async deleteOldBlocks() { + const result = await this.rpcBlockRepository.createQueryBuilder('entity') + .select('MAX(entity.blockHeight)', 'maxNumber') + .getRawOne(); + + const newestBlock = result ? result.maxNumber : null; + + await this.rpcBlockRepository.createQueryBuilder() + .delete() + .where('"blockHeight" < :newestBlock', { newestBlock }) + .execute(); + + return; + } } \ No newline at end of file diff --git a/src/app.controller.ts b/src/app.controller.ts index 6af196b..cef29da 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -6,6 +6,7 @@ import { firstValueFrom } from 'rxjs'; import { BlocksService } from './ORM/blocks/blocks.service'; import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service'; import { ClientService } from './ORM/client/client.service'; +import { HomeGraphService } from './ORM/home-graph/home-graph.service'; import { BitcoinRpcService } from './services/bitcoin-rpc.service'; @Controller() @@ -18,7 +19,8 @@ export class AppController { private readonly clientService: ClientService, private readonly clientStatisticsService: ClientStatisticsService, private readonly blocksService: BlocksService, - private readonly bitcoinRpcService: BitcoinRpcService + private readonly bitcoinRpcService: BitcoinRpcService, + private readonly homeGraphService: HomeGraphService ) { } @Get('info') @@ -66,7 +68,7 @@ export class AppController { return cachedResult; } - const chartData = await this.clientStatisticsService.getChartDataForSite(); + const chartData = await this.homeGraphService.getChartDataForSite(); //10 min await this.cacheManager.set(CACHE_KEY, chartData, 10 * 60 * 1000); diff --git a/src/app.module.ts b/src/app.module.ts index 5e0083e..443ac68 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -17,6 +17,8 @@ import { ClientStatisticsEntity } from './ORM/client-statistics/client-statistic import { ClientStatisticsModule } from './ORM/client-statistics/client-statistics.module'; import { ClientEntity } from './ORM/client/client.entity'; import { ClientModule } from './ORM/client/client.module'; +import { HomeGraphEntity } from './ORM/home-graph/home-graph.entity'; +import { HomeGraphModule } from './ORM/home-graph/home-graph.module'; import { RpcBlockEntity } from './ORM/rpc-block/rpc-block.entity'; import { RpcBlocksModule } from './ORM/rpc-block/rpc-block.module'; import { TelegramSubscriptionsEntity } from './ORM/telegram-subscriptions/telegram-subscriptions.entity'; @@ -39,7 +41,8 @@ const ORMModules = [ AddressSettingsModule, TelegramSubscriptionsModule, BlocksModule, - RpcBlocksModule + RpcBlocksModule, + HomeGraphModule ] @Module({ @@ -60,7 +63,8 @@ const ORMModules = [ BlocksEntity, ClientStatisticsEntity, RpcBlockEntity, - TelegramSubscriptionsEntity + TelegramSubscriptionsEntity, + HomeGraphEntity ], synchronize: configService.get('PRODUCTION') != 'true', logging: false, diff --git a/src/services/app.service.ts b/src/services/app.service.ts index c02cedf..43a2365 100644 --- a/src/services/app.service.ts +++ b/src/services/app.service.ts @@ -4,6 +4,8 @@ import { DataSource } from 'typeorm'; import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service'; import { ClientService } from '../ORM/client/client.service'; +import { HomeGraphService } from '../ORM/home-graph/home-graph.service'; +import { RpcBlockService } from '../ORM/rpc-block/rpc-block.service'; @Injectable() export class AppService implements OnModuleInit { @@ -11,7 +13,9 @@ export class AppService implements OnModuleInit { constructor( private readonly clientStatisticsService: ClientStatisticsService, private readonly clientService: ClientService, - private readonly dataSource: DataSource + private readonly rpcBlockService: RpcBlockService, + private readonly homeGraphService: HomeGraphService, + private readonly dataSource: DataSource, ) { } @@ -39,4 +43,45 @@ export class AppService implements OnModuleInit { } } + @Interval(1000 * 60 * 60 * 24) + private async deleteOldBlocks() { + console.log('Deleting Old Blocks'); + if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) { + await this.rpcBlockService.deleteOldBlocks(); + } + } + + @Interval(1000 * 60 * 10) + private async updateChart() { + console.log('Updating Chart'); + + const latestGraphUpdate = await this.homeGraphService.getLatestTime(); + + + const data = await this.dataSource.query(` + SELECT + time AS label, + ROUND(((SUM(shares) * 4294967296) / 600)) AS data + FROM + client_statistics_entity AS entry + WHERE + entry.time > ${latestGraphUpdate.getTime()} + GROUP BY + time + ORDER BY + time + LIMIT 144; + `) + + console.log(`Fetched ${data.length} rows`); + + if (data.length < 2) { + return; + } + + const result = data.slice(0, data.length - 1); + + this.homeGraphService.save(result); + + } } \ No newline at end of file