Migrate pool storage to TimescaleDB and Redis

This commit is contained in:
Ben
2026-06-07 20:33:13 -04:00
parent fb367ac5f0
commit ec69f5fb6c
64 changed files with 4751 additions and 1146 deletions
@@ -1,32 +1,59 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AddressSettingsModule } from '../../ORM/address-settings/address-settings.module';
import { ClientStatisticsModule } from '../../ORM/client-statistics/client-statistics.module';
import { ClientModule } from '../../ORM/client/client.module';
import { AddressSettingsService } from '../../ORM/address-settings/address-settings.service';
import { ClientStatisticsService } from '../../ORM/client-statistics/client-statistics.service';
import { ClientService } from '../../ORM/client/client.service';
import { ShareAccountingService } from '../../ORM/share-accounting/share-accounting.service';
import { RedisMessagingService } from '../../services/redis-messaging.service';
import { ClientController } from './client.controller';
describe('ClientController', () => {
let controller: ClientController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'better-sqlite3',
database: ':memory:',
synchronize: true,
autoLoadEntities: true,
cache: true,
logging: false
}),
AddressSettingsModule,
ClientModule,
ClientStatisticsModule
],
controllers: [ClientController],
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ClientController],
providers: [
{
provide: ClientService,
useValue: {
getByAddress: jest.fn(),
getByName: jest.fn(),
getBySessionId: jest.fn(),
},
},
{
provide: ClientStatisticsService,
useValue: {
getChartDataForAddress: jest.fn(),
getChartDataForGroup: jest.fn(),
getChartDataForSession: jest.fn(),
},
},
{
provide: AddressSettingsService,
useValue: {
getSettings: jest.fn(),
},
},
{
provide: ShareAccountingService,
useValue: {
getAddressSummary: jest.fn(),
getWorkerGroupSummary: jest.fn(),
getSessionSummary: jest.fn(),
getSessionSummaries: jest.fn().mockResolvedValue(new Map()),
},
},
{
provide: RedisMessagingService,
useValue: {
getClientPresenceByAddress: jest.fn().mockResolvedValue([]),
},
},
],
}).compile();
}).compile();
controller = module.get<ClientController>(ClientController);
});
+31 -7
View File
@@ -3,6 +3,8 @@ import { Controller, Get, NotFoundException, Param } from '@nestjs/common';
import { AddressSettingsService } from '../../ORM/address-settings/address-settings.service';
import { ClientStatisticsService } from '../../ORM/client-statistics/client-statistics.service';
import { ClientService } from '../../ORM/client/client.service';
import { ShareAccountingService } from '../../ORM/share-accounting/share-accounting.service';
import { RedisMessagingService } from '../../services/redis-messaging.service';
@Controller('client')
@@ -11,29 +13,38 @@ export class ClientController {
constructor(
private readonly clientService: ClientService,
private readonly clientStatisticsService: ClientStatisticsService,
private readonly addressSettingsService: AddressSettingsService
private readonly addressSettingsService: AddressSettingsService,
private readonly shareAccountingService: ShareAccountingService,
private readonly redisMessagingService: RedisMessagingService
) { }
@Get(':address')
async getClientInfo(@Param('address') address: string) {
const workers = await this.clientService.getByAddress(address);
const workers = await this.redisMessagingService.getClientPresenceByAddress(address);
const sessionSummaries = await this.shareAccountingService.getSessionSummaries(workers.map(worker => worker.clientId));
const addressSettings = await this.addressSettingsService.getSettings(address, false);
return {
bestDifficulty: addressSettings?.bestDifficulty,
workersCount: workers.length,
accounting: await this.shareAccountingService.getAddressSummary(address),
workers: await Promise.all(
workers.map(async (worker) => {
const sessionSummary = sessionSummaries.get(worker.clientId);
const bestDifficulty = Math.max(
Number(worker.bestDifficulty ?? 0),
Number(sessionSummary?.bestSubmissionDifficulty ?? 0),
);
return {
sessionId: worker.sessionId,
name: worker.clientName,
bestDifficulty: parseFloat(worker.bestDifficulty as any).toFixed(2),
hashRate: worker.hashRate,
bestDifficulty: bestDifficulty.toFixed(2),
hashRate: sessionSummary?.hashRateLast10Minutes ?? worker.hashRate,
startTime: worker.startTime,
lastSeen: worker.updatedAt
lastSeen: sessionSummary?.latestShareAt ?? worker.lastSeen
};
})
)
@@ -49,7 +60,8 @@ export class ClientController {
@Get(':address/:workerName')
async getWorkerGroupInfo(@Param('address') address: string, @Param('workerName') workerName: string) {
const workers = await this.clientService.getByName(address, workerName);
const workers = (await this.redisMessagingService.getClientPresenceByAddress(address))
.filter(worker => worker.clientName === workerName);
const bestDifficulty = workers.reduce((pre, cur, idx, arr) => {
if (cur.bestDifficulty > pre) {
@@ -63,6 +75,7 @@ export class ClientController {
name: workerName,
bestDifficulty: Math.floor(bestDifficulty),
accounting: await this.shareAccountingService.getWorkerGroupSummary(address, workerName),
chartData: chartData,
}
@@ -71,7 +84,17 @@ export class ClientController {
@Get(':address/:workerName/:sessionId')
async getWorkerInfo(@Param('address') address: string, @Param('workerName') workerName: string, @Param('sessionId') sessionId: string) {
const worker = await this.clientService.getBySessionId(address, workerName, sessionId);
const presenceWorker = (await this.redisMessagingService.getClientPresenceByAddress(address))
.find(worker => worker.clientName === workerName && worker.sessionId === sessionId);
const worker = presenceWorker == null
? await this.clientService.getBySessionId(address, workerName, sessionId)
: {
id: presenceWorker.clientId,
sessionId: presenceWorker.sessionId,
clientName: presenceWorker.clientName,
bestDifficulty: presenceWorker.bestDifficulty,
startTime: presenceWorker.startTime,
};
if (worker == null) {
return new NotFoundException();
}
@@ -81,6 +104,7 @@ export class ClientController {
sessionId: worker.sessionId,
name: worker.clientName,
bestDifficulty: Math.floor(worker.bestDifficulty),
accounting: await this.shareAccountingService.getSessionSummary(worker.id),
chartData: chartData,
startTime: worker.startTime
}