user agent report

This commit is contained in:
Ben Wilson
2024-02-16 23:49:47 -05:00
parent 39e537a00c
commit 87508bfb69
7 changed files with 140 additions and 44 deletions
@@ -0,0 +1,15 @@
import { Global, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserAgentReportService } from './user-agent-report.service';
import { UserAgentReportView } from './user-agent-report.view';
@Global()
@Module({
imports: [TypeOrmModule.forFeature([UserAgentReportView])],
providers: [UserAgentReportService],
exports: [TypeOrmModule, UserAgentReportService],
})
export class UserAgentReportModule { }
@@ -0,0 +1,35 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserAgentReportView } from './user-agent-report.view';
@Injectable()
export class UserAgentReportService {
constructor(
@InjectRepository(UserAgentReportView)
private userAgentReport: Repository<UserAgentReportView>,
) {
}
public async getReport() {
return await this.userAgentReport.find();
}
public async refreshReport() {
try {
return await this.userAgentReport.query(`
COMMIT;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
REFRESH MATERIALIZED VIEW user_agent_report_view;
COMMIT;
`);
} catch (e) {
console.log(e)
}
}
}
@@ -0,0 +1,27 @@
import { DataSource, ViewColumn, ViewEntity } from 'typeorm';
import { ClientEntity } from '../../client/client.entity';
@ViewEntity({
materialized: true,
expression: (dataSource: DataSource) =>
dataSource
.createQueryBuilder()
.select('client.userAgent as "userAgent"')
.addSelect('COUNT(client.userAgent)', 'count')
.addSelect('MAX(client.bestDifficulty)', 'bestDifficulty')
.addSelect('SUM(client.hashRate)', 'totalHashRate')
.from(ClientEntity, 'client')
.groupBy('client.userAgent')
.orderBy('"totalHashRate"', 'DESC')
})
export class UserAgentReportView {
@ViewColumn()
userAgent: string;
@ViewColumn()
count: number;
@ViewColumn()
bestDifficulty: number;
@ViewColumn()
totalHashRate: number;
}
+11 -11
View File
@@ -104,16 +104,16 @@ export class ClientService {
return await this.clientRepository.softDelete({}) return await this.clientRepository.softDelete({})
} }
public async getUserAgents() { // public async getUserAgents() {
const result = await this.clientRepository.createQueryBuilder('client') // const result = await this.clientRepository.createQueryBuilder('client')
.select('client.userAgent as "userAgent"') // .select('client.userAgent as "userAgent"')
.addSelect('COUNT(client.userAgent)', 'count') // .addSelect('COUNT(client.userAgent)', 'count')
.addSelect('MAX(client.bestDifficulty)', 'bestDifficulty') // .addSelect('MAX(client.bestDifficulty)', 'bestDifficulty')
.addSelect('SUM(client.hashRate)', 'totalHashRate') // .addSelect('SUM(client.hashRate)', 'totalHashRate')
.groupBy('client.userAgent') // .groupBy('client.userAgent')
.orderBy('"totalHashRate"', 'DESC') // .orderBy('"totalHashRate"', 'DESC')
.getRawMany(); // .getRawMany();
return result; // return result;
} // }
} }
+9 -7
View File
@@ -3,6 +3,7 @@ import { Controller, Get, Inject } from '@nestjs/common';
import { Cache } from 'cache-manager'; import { Cache } from 'cache-manager';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { UserAgentReportService } from './ORM/_views/user-agent-report/user-agent-report.service';
import { AddressSettingsService } from './ORM/address-settings/address-settings.service'; import { AddressSettingsService } from './ORM/address-settings/address-settings.service';
import { BlocksService } from './ORM/blocks/blocks.service'; import { BlocksService } from './ORM/blocks/blocks.service';
import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service'; import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service';
@@ -22,7 +23,8 @@ export class AppController {
private readonly blocksService: BlocksService, private readonly blocksService: BlocksService,
private readonly bitcoinRpcService: BitcoinRpcService, private readonly bitcoinRpcService: BitcoinRpcService,
private readonly homeGraphService: HomeGraphService, private readonly homeGraphService: HomeGraphService,
private readonly addressSettingsService: AddressSettingsService private readonly addressSettingsService: AddressSettingsService,
private readonly userAgentReportService: UserAgentReportService
) { } ) { }
@Get('info') @Get('info')
@@ -38,7 +40,7 @@ export class AppController {
const blockData = await this.blocksService.getFoundBlocks(); const blockData = await this.blocksService.getFoundBlocks();
const userAgents = await this.clientService.getUserAgents(); const userAgents = await this.userAgentReportService.getReport();
const highScores = await this.addressSettingsService.getHighScores(); const highScores = await this.addressSettingsService.getHighScores();
const data = { const data = {
@@ -48,8 +50,8 @@ export class AppController {
uptime: this.uptime uptime: this.uptime
}; };
//1 min //5 min
await this.cacheManager.set(CACHE_KEY, data, 1 * 60 * 1000); await this.cacheManager.set(CACHE_KEY, data, 5 * 60 * 1000);
return data; return data;
@@ -66,9 +68,9 @@ export class AppController {
} }
const userAgents = await this.clientService.getUserAgents(); const userAgents = await this.userAgentReportService.getReport();
const totalHashRate = userAgents.reduce((acc, userAgent) => acc + parseFloat(userAgent.totalHashRate), 0); const totalHashRate = userAgents.reduce((acc, userAgent) => acc + userAgent.totalHashRate, 0);
const totalMiners = userAgents.reduce((acc, userAgent) => acc + parseInt(userAgent.count), 0); const totalMiners = userAgents.reduce((acc, userAgent) => acc + userAgent.count, 0);
const blockHeight = (await firstValueFrom(this.bitcoinRpcService.newBlock$)).blocks; const blockHeight = (await firstValueFrom(this.bitcoinRpcService.newBlock$)).blocks;
const blocksFound = await this.blocksService.getFoundBlocks(); const blocksFound = await this.blocksService.getFoundBlocks();
+6 -2
View File
@@ -10,6 +10,8 @@ import { AddressController } from './controllers/address/address.controller';
import { ClientController } from './controllers/client/client.controller'; import { ClientController } from './controllers/client/client.controller';
import { BitcoinAddressValidator } from './models/validators/bitcoin-address.validator'; import { BitcoinAddressValidator } from './models/validators/bitcoin-address.validator';
import { UniqueNonceIndex } from './ORM/_migrations/UniqueNonceIndex'; import { UniqueNonceIndex } from './ORM/_migrations/UniqueNonceIndex';
import { UserAgentReportModule } from './ORM/_views/user-agent-report/user-agent-report.module';
import { UserAgentReportView } from './ORM/_views/user-agent-report/user-agent-report.view';
import { AddressSettingsEntity } from './ORM/address-settings/address-settings.entity'; import { AddressSettingsEntity } from './ORM/address-settings/address-settings.entity';
import { AddressSettingsModule } from './ORM/address-settings/address-settings.module'; import { AddressSettingsModule } from './ORM/address-settings/address-settings.module';
import { BlocksEntity } from './ORM/blocks/blocks.entity'; import { BlocksEntity } from './ORM/blocks/blocks.entity';
@@ -43,7 +45,8 @@ const ORMModules = [
TelegramSubscriptionsModule, TelegramSubscriptionsModule,
BlocksModule, BlocksModule,
RpcBlocksModule, RpcBlocksModule,
HomeGraphModule HomeGraphModule,
UserAgentReportModule
] ]
@Module({ @Module({
@@ -65,7 +68,8 @@ const ORMModules = [
ClientStatisticsEntity, ClientStatisticsEntity,
RpcBlockEntity, RpcBlockEntity,
TelegramSubscriptionsEntity, TelegramSubscriptionsEntity,
HomeGraphEntity HomeGraphEntity,
UserAgentReportView
], ],
synchronize: configService.get('PRODUCTION') != 'true', synchronize: configService.get('PRODUCTION') != 'true',
logging: false, logging: false,
+37 -24
View File
@@ -1,7 +1,7 @@
import { Injectable, OnModuleInit } from '@nestjs/common'; import { Injectable, OnModuleInit } from '@nestjs/common';
import { Interval } from '@nestjs/schedule';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { UserAgentReportService } from '../ORM/_views/user-agent-report/user-agent-report.service';
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service'; import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
import { ClientService } from '../ORM/client/client.service'; import { ClientService } from '../ORM/client/client.service';
import { HomeGraphService } from '../ORM/home-graph/home-graph.service'; import { HomeGraphService } from '../ORM/home-graph/home-graph.service';
@@ -16,41 +16,54 @@ export class AppService implements OnModuleInit {
private readonly rpcBlockService: RpcBlockService, private readonly rpcBlockService: RpcBlockService,
private readonly homeGraphService: HomeGraphService, private readonly homeGraphService: HomeGraphService,
private readonly dataSource: DataSource, private readonly dataSource: DataSource,
private readonly userAgentReportService: UserAgentReportService
) { ) {
} }
async onModuleInit() { async onModuleInit() {
if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) {
setInterval(async () => {
await this.deleteOldStatistics();
}, 1000 * 60 * 60);
setInterval(async () => {
console.log('Killing dead clients');
await this.clientService.killDeadClients();
}, 1000 * 60 * 5);
setInterval(async () => {
console.log('Deleting Old Blocks');
await this.rpcBlockService.deleteOldBlocks();
}, 1000 * 60 * 60 * 24);
setInterval(async () => {
await this.updateChart();
}, 1000 * 60 * 10);
setInterval(async () => {
console.log('Refreshing user agent report view')
await this.userAgentReportService.refreshReport();
console.log('Finished Refreshing user agent report view')
}, 1000 * 60 * 5);
}
} }
@Interval(1000 * 60 * 60)
private async deleteOldStatistics() { private async deleteOldStatistics() {
console.log('Deleting statistics'); console.log('Deleting statistics');
if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) {
const deletedStatistics = await this.clientStatisticsService.deleteOldStatistics(); const deletedStatistics = await this.clientStatisticsService.deleteOldStatistics();
console.log(`Deleted ${deletedStatistics.affected} old statistics`); console.log(`Deleted ${deletedStatistics.affected} old statistics`);
const deletedClients = await this.clientService.deleteOldClients(); const deletedClients = await this.clientService.deleteOldClients();
console.log(`Deleted ${deletedClients.affected} old clients`); console.log(`Deleted ${deletedClients.affected} old clients`);
}
} }
@Interval(1000 * 60 * 5)
private async killDeadClients() {
console.log('Killing dead clients');
if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) {
await this.clientService.killDeadClients();
}
}
@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() { private async updateChart() {
console.log('Updating Chart'); console.log('Updating Chart');