db refactor

This commit is contained in:
Ben Wilson
2024-02-16 17:12:36 -05:00
parent 8e977c21a1
commit 3e2de62259
13 changed files with 190 additions and 96 deletions
+15
View File
@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UniqueNonceIndex implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE UNIQUE INDEX "IDX_unique_nonce" ON "client_entity" ("sessionId") WHERE "deletedAt" IS NOT NULL`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_unique_nonce"`);
}
}
@@ -14,6 +14,9 @@ export class AddressSettingsEntity extends TrackedEntity {
@Column({ type: 'decimal', default: 0 })
bestDifficulty: number;
@Column({ nullable: true })
bestDifficultyUserAgent: string;
@Column({ nullable: true })
miscCoinbaseScriptData: string;
@@ -17,13 +17,18 @@ export class AddressSettingsService {
public async getSettings(address: string, createIfNotFound: boolean) {
const settings = await this.addressSettingsRepository.findOne({ where: { address } });
if (createIfNotFound == true && settings == null) {
return await this.createNew(address);
// It's possible to have a race condition here so if we get a PK violation, fetch it
try {
return await this.createNew(address);
} catch (e) {
return await this.addressSettingsRepository.findOne({ where: { address } });
}
}
return settings;
}
public async updateBestDifficulty(address: string, bestDifficulty: number) {
return await this.addressSettingsRepository.update({ address }, { bestDifficulty });
public async updateBestDifficulty(address: string, bestDifficulty: number, bestDifficultyUserAgent: string) {
return await this.addressSettingsRepository.update({ address }, { bestDifficulty, bestDifficultyUserAgent });
}
public async createNew(address: string) {
@@ -46,4 +51,12 @@ export class AddressSettingsService {
bestDifficulty: 0
});
}
public async getHighScores() {
return await this.addressSettingsRepository.createQueryBuilder()
.select('"updatedAt", "bestDifficulty", "bestDifficultyUserAgent"')
.orderBy('"bestDifficulty"', 'DESC')
.limit(10)
.execute();
}
}
@@ -1,12 +1,11 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
import { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { ClientEntity } from '../client/client.entity';
import { TrackedEntity } from '../utils/TrackedEntity.entity';
@Entity()
//Index for getHashRateForSession
@Index(["address", "clientName", "sessionId"])
//Index for statistics save
@Index(["address", "clientName", "sessionId", "time"])
@Index(["clientId", "time"])
export class ClientStatisticsEntity extends TrackedEntity {
@PrimaryGeneratedColumn()
@@ -32,4 +31,17 @@ export class ClientStatisticsEntity extends TrackedEntity {
acceptedCount: number;
@ManyToOne(
() => ClientEntity,
clientEntity => clientEntity.statistics,
{ nullable: false, }
)
client: ClientEntity;
@Index()
@Column({ name: 'clientId' })
public clientId: string;
}
@@ -19,25 +19,15 @@ export class ClientStatisticsService {
}
public async save(clientStatistic: Partial<ClientStatisticsEntity>) {
// Attempt to update the existing record
const updateResult = await this.clientStatisticsRepository
.createQueryBuilder()
.update(ClientStatisticsEntity)
.set({
shares: () => `"shares" + :sharesIncrement`,
acceptedCount: () => `"acceptedCount" + 1`
// // Attempt to update the existing record
const result = await this.clientStatisticsRepository.update({ clientId: clientStatistic.clientId, time: clientStatistic.time },
{
shares: clientStatistic.shares,
acceptedCount: clientStatistic.acceptedCount
})
.where('address = :address AND clientName = :clientName AND sessionId = :sessionId AND time = :time', {
address: clientStatistic.address,
clientName: clientStatistic.clientName,
sessionId: clientStatistic.sessionId,
time: clientStatistic.time,
sharesIncrement: clientStatistic.shares
})
.execute();
// Check if the update affected any rows
if (updateResult.affected === 0) {
if (result.affected === 0) {
// If no rows were updated, insert a new record
await this.clientStatisticsRepository.insert(clientStatistic);
}
@@ -139,7 +129,7 @@ export class ClientStatisticsService {
}
public async getHashRateForSession(address: string, clientName: string, sessionId: string) {
public async getHashRateForSession(clientId: string) {
const query = `
SELECT
@@ -149,12 +139,12 @@ export class ClientStatisticsService {
FROM
client_statistics_entity AS entry
WHERE
entry.address = $1 AND entry."clientName" = $2 AND entry."sessionId" = $3
entry."clientId" = $1
ORDER BY time DESC
LIMIT 2;
`;
const result = await this.clientStatisticsRepository.query(query, [address, clientName, sessionId]);
const result = await this.clientStatisticsRepository.query(query, [clientId]);
if (result.length < 1) {
return 0;
@@ -179,7 +169,7 @@ export class ClientStatisticsService {
}
public async getChartDataForSession(address: string, clientName: string, sessionId: string) {
public async getChartDataForSession(clientId: string) {
var yesterday = new Date(new Date().getTime() - (24 * 60 * 60 * 1000));
const query = `
@@ -189,7 +179,7 @@ export class ClientStatisticsService {
FROM
client_statistics_entity AS entry
WHERE
entry.address = $1 AND entry."clientName" = $2 AND entry."sessionId" = $3 AND entry.time > ${yesterday.getTime()}
entry."clientId" = $1 AND entry.time > ${yesterday.getTime()}
GROUP BY
time
ORDER BY
@@ -197,7 +187,7 @@ export class ClientStatisticsService {
LIMIT 144;
`;
const result = await this.clientStatisticsRepository.query(query, [address, clientName, sessionId]);
const result = await this.clientStatisticsRepository.query(query, [clientId]);
return result.map(res => {
res.label = new Date(parseInt(res.label)).toISOString();
+18 -10
View File
@@ -1,21 +1,24 @@
import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
import { Column, Entity, Index, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { ClientStatisticsEntity } from '../client-statistics/client-statistics.entity';
import { TrackedEntity } from '../utils/TrackedEntity.entity';
@Entity()
@Index(['address', 'clientName', 'sessionId'], { unique: true })
@Index("IDX_unique_nonce", { synchronize: false })
export class ClientEntity extends TrackedEntity {
@PrimaryColumn({ length: 62, type: 'varchar' })
address: string;
@PrimaryColumn({ length: 64, type: 'varchar' })
clientName: string;
@PrimaryGeneratedColumn('uuid')
id: string;
@Index()
@PrimaryColumn({ length: 8, type: 'varchar' })
@Column({ length: 62, type: 'varchar' })
address: string;
@Column({ length: 64, type: 'varchar' })
clientName: string;
@Column({ length: 8, type: 'varchar', })
sessionId: string;
@@ -23,7 +26,6 @@ export class ClientEntity extends TrackedEntity {
userAgent: string;
@Column({ type: 'timestamp' })
startTime: Date;
@@ -33,5 +35,11 @@ export class ClientEntity extends TrackedEntity {
@Column({ default: 0, type: 'decimal' })
hashRate: number;
@OneToMany(
() => ClientStatisticsEntity,
clientStatisticsEntity => clientStatisticsEntity.client
)
statistics: ClientStatisticsEntity[]
}
+7 -7
View File
@@ -28,8 +28,8 @@ export class ClientService {
.execute();
}
public async heartbeat(address: string, clientName: string, sessionId: string, hashRate: number, updatedAt: Date) {
return await this.clientRepository.update({ address, clientName, sessionId }, { hashRate, deletedAt: null, updatedAt });
public async heartbeat(id, hashRate: number, updatedAt: Date) {
return await this.clientRepository.update({ id }, { hashRate, deletedAt: null, updatedAt });
}
// public async save(client: Partial<ClientEntity>) {
@@ -48,8 +48,8 @@ export class ClientService {
return client as ClientEntity;
}
public async delete(sessionId: string) {
return await this.clientRepository.softDelete({ sessionId });
public async delete(id: string) {
return await this.clientRepository.softDelete({ id });
}
public async deleteOldClients() {
@@ -65,8 +65,8 @@ export class ClientService {
}
public async updateBestDifficulty(sessionId: string, bestDifficulty: number) {
return await this.clientRepository.update({ sessionId }, { bestDifficulty });
public async updateBestDifficulty(id: string, bestDifficulty: number) {
return await this.clientRepository.update({ id }, { bestDifficulty });
}
public async connectedClientCount(): Promise<number> {
return await this.clientRepository.count();
@@ -111,7 +111,7 @@ export class ClientService {
.addSelect('MAX(client.bestDifficulty)', 'bestDifficulty')
.addSelect('SUM(client.hashRate)', 'totalHashRate')
.groupBy('client.userAgent')
.orderBy('count', 'DESC')
.orderBy('"totalHashRate"', 'DESC')
.getRawMany();
return result;
}