mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
revert stale job retention
This commit is contained in:
@@ -25,7 +25,6 @@ export class MiningJob {
|
|||||||
public jobTemplateId: string;
|
public jobTemplateId: string;
|
||||||
public networkDifficulty: number;
|
public networkDifficulty: number;
|
||||||
public creation: number;
|
public creation: number;
|
||||||
public retiredAt?: number;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private network: bitcoinjs.networks.Network,
|
private network: bitcoinjs.networks.Network,
|
||||||
|
|||||||
@@ -550,19 +550,6 @@ export class StratumV1Client {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const classification = this.stratumV1JobsService.classifyJobForShare(job);
|
|
||||||
if (classification === 'stale-rejected') {
|
|
||||||
const err = new StratumErrorMessage(
|
|
||||||
submission.id,
|
|
||||||
eStratumErrorCode.JobNotFound,
|
|
||||||
'stale').response();
|
|
||||||
const success = await this.write(err);
|
|
||||||
if (!success) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const submissionHash = [
|
const submissionHash = [
|
||||||
submission.jobId,
|
submission.jobId,
|
||||||
submission.extraNonce2,
|
submission.extraNonce2,
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ describe('StratumV1JobsService', () => {
|
|||||||
expect(service.getJobTemplateById('1')).toBe(jobTemplate);
|
expect(service.getJobTemplateById('1')).toBe(jobTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should retire jobs when the block height changes', async () => {
|
it('should clear jobs when the block height changes', async () => {
|
||||||
const firstTemplate = await firstValueFrom(service.newMiningJob$);
|
const firstTemplate = await firstValueFrom(service.newMiningJob$);
|
||||||
service.addJob({ jobId: 'old-job', creation: Date.now() } as any);
|
service.addJob({ jobId: 'old-job', creation: Date.now() } as any);
|
||||||
|
|
||||||
@@ -57,27 +57,18 @@ describe('StratumV1JobsService', () => {
|
|||||||
const jobTemplate = await nextTemplate;
|
const jobTemplate = await nextTemplate;
|
||||||
|
|
||||||
expect(jobTemplate.blockData.clearJobs).toBe(true);
|
expect(jobTemplate.blockData.clearJobs).toBe(true);
|
||||||
expect(service.getJobById('old-job')).toEqual(expect.objectContaining({
|
expect(service.getJobById('old-job')).toBeUndefined();
|
||||||
jobId: 'old-job',
|
expect(service.getJobTemplateById(firstTemplate.blockData.id)).toBeUndefined();
|
||||||
retiredAt: Date.now()
|
|
||||||
}));
|
|
||||||
expect(service.getJobTemplateById(firstTemplate.blockData.id).blockData.retiredAt).toBe(Date.now());
|
|
||||||
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should age retired jobs and templates after the retention window', async () => {
|
it('should age old jobs and templates after five minutes', async () => {
|
||||||
const firstTemplate = await firstValueFrom(service.newMiningJob$);
|
await firstValueFrom(service.newMiningJob$);
|
||||||
const oldCreation = Date.now() - (1000 * 60 * 11);
|
const oldCreation = Date.now() - (1000 * 60 * 11);
|
||||||
const retiredAt = Date.now() - (1000 * 60 * 11);
|
service.jobs['old-job'] = { jobId: 'old-job', creation: oldCreation } as any;
|
||||||
firstTemplate.blockData.retiredAt = retiredAt;
|
service.blocks['old-template'] = {
|
||||||
for (let i = 4; i >= 0; i--) {
|
blockData: { creation: oldCreation }
|
||||||
service.jobs[`old-job-${i}`] = { jobId: `old-job-${i}`, creation: oldCreation - i, retiredAt } as any;
|
} as any;
|
||||||
(service as any).trackJob(`old-job-${i}`);
|
|
||||||
service.blocks[`old-template-${i}`] = {
|
|
||||||
blockData: { creation: oldCreation - i, retiredAt }
|
|
||||||
} as any;
|
|
||||||
(service as any).trackBlock(`old-template-${i}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height;
|
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height;
|
||||||
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
||||||
@@ -85,22 +76,11 @@ describe('StratumV1JobsService', () => {
|
|||||||
const jobTemplate = await nextTemplate;
|
const jobTemplate = await nextTemplate;
|
||||||
|
|
||||||
expect(jobTemplate.blockData.clearJobs).toBe(false);
|
expect(jobTemplate.blockData.clearJobs).toBe(false);
|
||||||
expect(service.getJobById('old-job-4')).toBeUndefined();
|
expect(service.getJobById('old-job')).toBeUndefined();
|
||||||
expect(service.getJobTemplateById('old-template-4')).toBeUndefined();
|
expect(service.getJobTemplateById('old-template')).toBeUndefined();
|
||||||
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should classify retired jobs inside and outside the stale grace window', () => {
|
|
||||||
const job = { jobId: '1', creation: Date.now(), retiredAt: Date.now() - 1000 } as any;
|
|
||||||
|
|
||||||
expect(service.classifyJobForShare(job, Date.now())).toBe('stale-creditable');
|
|
||||||
|
|
||||||
job.retiredAt = Date.now() - 6000;
|
|
||||||
|
|
||||||
expect(service.classifyJobForShare(job, Date.now())).toBe('stale-rejected');
|
|
||||||
expect(service.classifyJobForShare({ jobId: '2', creation: Date.now() } as any, Date.now())).toBe('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should increment job ids when jobs are added', () => {
|
it('should increment job ids when jobs are added', () => {
|
||||||
expect(service.getNextId()).toBe('1');
|
expect(service.getNextId()).toBe('1');
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,9 @@ export interface IJobTemplate {
|
|||||||
networkDifficulty: number;
|
networkDifficulty: number;
|
||||||
height: number;
|
height: number;
|
||||||
clearJobs: boolean;
|
clearJobs: boolean;
|
||||||
retiredAt?: number;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const STALE_GRACE_MS = parseInt(process.env.STRATUM_STALE_GRACE_MS) || 5000;
|
|
||||||
const MIN_RETAINED = 3;
|
|
||||||
export { STALE_GRACE_MS };
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StratumV1JobsService {
|
export class StratumV1JobsService {
|
||||||
|
|
||||||
@@ -36,11 +31,6 @@ export class StratumV1JobsService {
|
|||||||
public blocks: { [id: number]: IJobTemplate } = {};
|
public blocks: { [id: number]: IJobTemplate } = {};
|
||||||
|
|
||||||
private lastBlockHeight = 0;
|
private lastBlockHeight = 0;
|
||||||
private jobRetentionMs = parseInt(process.env.JOB_RETENTION_MS) || 600000;
|
|
||||||
private jobOrder: string[] = [];
|
|
||||||
private blockOrder: string[] = [];
|
|
||||||
private jobOrderSet = new Set<string>();
|
|
||||||
private blockOrderSet = new Set<string>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly bitcoinRpcService: BitcoinRpcService
|
private readonly bitcoinRpcService: BitcoinRpcService
|
||||||
@@ -121,7 +111,6 @@ export class StratumV1JobsService {
|
|||||||
tap((data) => {
|
tap((data) => {
|
||||||
this.cleanup(data.blockData.clearJobs);
|
this.cleanup(data.blockData.clearJobs);
|
||||||
this.blocks[data.blockData.id] = data;
|
this.blocks[data.blockData.id] = data;
|
||||||
this.trackBlock(data.blockData.id);
|
|
||||||
}),
|
}),
|
||||||
shareReplay({ refCount: true, bufferSize: 1 })
|
shareReplay({ refCount: true, bufferSize: 1 })
|
||||||
)
|
)
|
||||||
@@ -153,78 +142,32 @@ export class StratumV1JobsService {
|
|||||||
|
|
||||||
public cleanup(clearJobs: boolean, now: number = Date.now()) {
|
public cleanup(clearJobs: boolean, now: number = Date.now()) {
|
||||||
if (clearJobs) {
|
if (clearJobs) {
|
||||||
for (const id of this.blockOrder) {
|
this.blocks = {};
|
||||||
const block = this.blocks[id];
|
this.jobs = {};
|
||||||
if (block != null && block.blockData.retiredAt === undefined) {
|
return;
|
||||||
block.blockData.retiredAt = now;
|
}
|
||||||
}
|
|
||||||
}
|
let templatesDeleted = 0;
|
||||||
for (const jobId of this.jobOrder) {
|
let jobsDeleted = 0;
|
||||||
const job = this.jobs[jobId];
|
|
||||||
if (job != null && job.retiredAt === undefined) {
|
for (const templateId in this.blocks) {
|
||||||
job.retiredAt = now;
|
if (now - this.blocks[templateId].blockData.creation > (1000 * 60 * 5)) {
|
||||||
}
|
delete this.blocks[templateId];
|
||||||
|
templatesDeleted++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ageEntries(
|
for (const jobId in this.jobs) {
|
||||||
this.blocks,
|
if (now - this.jobs[jobId].creation > (1000 * 60 * 5)) {
|
||||||
this.blockOrder,
|
delete this.jobs[jobId];
|
||||||
this.blockOrderSet,
|
jobsDeleted++;
|
||||||
now,
|
|
||||||
entry => entry.blockData.creation,
|
|
||||||
entry => entry.blockData.retiredAt,
|
|
||||||
);
|
|
||||||
this.ageEntries(
|
|
||||||
this.jobs,
|
|
||||||
this.jobOrder,
|
|
||||||
this.jobOrderSet,
|
|
||||||
now,
|
|
||||||
entry => entry.creation,
|
|
||||||
entry => entry.retiredAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ageEntries<T>(
|
|
||||||
map: Record<string, T>,
|
|
||||||
order: string[],
|
|
||||||
orderSet: Set<string>,
|
|
||||||
now: number,
|
|
||||||
getCreation: (entry: T) => number,
|
|
||||||
getRetiredAt: (entry: T) => number | undefined,
|
|
||||||
): void {
|
|
||||||
while (order.length > MIN_RETAINED) {
|
|
||||||
const id = order[0];
|
|
||||||
const entry = map[id];
|
|
||||||
if (entry == null) {
|
|
||||||
order.shift();
|
|
||||||
orderSet.delete(id);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const retiredAt = getRetiredAt(entry);
|
|
||||||
|
|
||||||
if (retiredAt !== undefined && now - retiredAt > this.jobRetentionMs) {
|
|
||||||
delete map[id];
|
|
||||||
order.shift();
|
|
||||||
orderSet.delete(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retiredAt === undefined && now - getCreation(entry) > this.jobRetentionMs * 2) {
|
|
||||||
delete map[id];
|
|
||||||
order.shift();
|
|
||||||
orderSet.delete(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
//console.log(`Deleted ${templatesDeleted} templates and ${jobsDeleted} jobs.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public addJob(job: MiningJob) {
|
public addJob(job: MiningJob) {
|
||||||
this.jobs[job.jobId] = job;
|
this.jobs[job.jobId] = job;
|
||||||
this.trackJob(job.jobId);
|
|
||||||
this.latestJobId++;
|
this.latestJobId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,13 +175,6 @@ export class StratumV1JobsService {
|
|||||||
return this.jobs[jobId];
|
return this.jobs[jobId];
|
||||||
}
|
}
|
||||||
|
|
||||||
public classifyJobForShare(job: MiningJob, now: number = Date.now()): 'active' | 'stale-creditable' | 'stale-rejected' {
|
|
||||||
if (job.retiredAt === undefined) {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
return (now - job.retiredAt) <= STALE_GRACE_MS ? 'stale-creditable' : 'stale-rejected';
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNextTemplateId() {
|
public getNextTemplateId() {
|
||||||
return this.latestJobTemplateId.toString(16);
|
return this.latestJobTemplateId.toString(16);
|
||||||
}
|
}
|
||||||
@@ -246,20 +182,4 @@ export class StratumV1JobsService {
|
|||||||
return this.latestJobId.toString(16);
|
return this.latestJobId.toString(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private trackJob(jobId: string): void {
|
|
||||||
if (this.jobOrderSet.has(jobId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.jobOrderSet.add(jobId);
|
|
||||||
this.jobOrder.push(jobId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private trackBlock(blockId: string): void {
|
|
||||||
if (this.blockOrderSet.has(blockId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.blockOrderSet.add(blockId);
|
|
||||||
this.blockOrder.push(blockId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user