Raise SV2 retained job limit

This commit is contained in:
Ben
2026-07-14 22:55:36 -04:00
parent 0b030fecca
commit fd226f206f
4 changed files with 38 additions and 3 deletions
+1 -1
View File
@@ -102,7 +102,7 @@ STRATUM_SUBMISSION_DEDUP_MAX_ENTRIES=10000
# Per-client lifecycle bounds. Obsolete queued jobs are coalesced; clients are
# disconnected instead of dropping still-recoverable candidates or buffering forever.
SV2_JOB_RETENTION_MS=300000
SV2_MAX_RETAINED_JOBS_PER_CHANNEL=16
SV2_MAX_RETAINED_JOBS_PER_CHANNEL=64
SV2_MAX_QUEUED_JOB_OPERATIONS=4
SV2_MAX_SOCKET_BUFFER_BYTES=262144
SV2_SOCKET_WRITE_TIMEOUT_MS=2000
+1 -1
View File
@@ -125,7 +125,7 @@ Standard and extended candidates retain exact header/body reconstruction, and
late network-target candidates remain recoverable without crediting stale shares.
Pending SV2 canonical jobs are coalesced per client, while a new-tip activation
is moved ahead of any not-yet-started canonical work for that tip. The finite
defaults are 16 retained jobs per channel, four queued operations, 256 KiB of
defaults are 64 retained jobs per channel, four queued operations, 256 KiB of
outstanding socket writes, and a two-second write-callback deadline. A client is
disconnected if `SV2_MAX_RETAINED_JOBS_PER_CHANNEL`,
`SV2_MAX_QUEUED_JOB_OPERATIONS`, `SV2_MAX_SOCKET_BUFFER_BYTES`, or
+35
View File
@@ -927,6 +927,41 @@ describe('StratumV2Client extended channels', () => {
)).toBe(true);
});
it('keeps enough retained SV2 jobs for repeated fast activation and full-template followup cycles', async () => {
const { client, jobTemplate } = await createClient();
await (client as any).handleOpenExtendedMiningChannel(serializeOpenExtendedMiningChannel({
requestId: 1,
userIdentity: 'tb1qumezefzdeqqwn5zfvgdrhxjzc5ylr39uhuxcz4.worker',
nominalHashRate: 0,
maxTarget: Buffer.alloc(32, 0xff),
minExtranonceSize: 8,
}));
let currentTemplate = jobTemplate;
for (let index = 0; index < 20; index++) {
const activation = {
...createActivationTemplate(currentTemplate),
previousblockhash: (index + 1).toString(16).padStart(64, '0'),
mintime: parseInt(MockRecording1.TIME, 16) + index + 1,
curtime: parseInt(MockRecording1.TIME, 16) + index + 1,
};
await client.enqueueWorkActivation(activation as any);
currentTemplate = {
...createCanonicalFollowup(currentTemplate, activation),
blockData: {
...createCanonicalFollowup(currentTemplate, activation).blockData,
id: `canonical-followup-${index}`,
},
};
await client.enqueueCanonicalJob(currentTemplate);
}
expect((client as any).socket.destroyed).toBe(false);
const channel = (client as any).channels.get(1);
expect(channel.extendedJobs.size).toBeLessThanOrEqual((client as any).maxRetainedJobsPerChannel);
expect(channel.stagedFutureJobId).toBeDefined();
});
it('activates a staged standard future and rejects incompatible activation versions', async () => {
const { client, sentFrames, jobTemplate } = await createClient();
await (client as any).handleOpenStandardMiningChannel(serializeOpenStandardMiningChannel({
+1 -1
View File
@@ -78,7 +78,7 @@ const DEFAULT_DIFFICULTY_CHECK_INTERVAL_MS = 60 * 1000;
const DEFAULT_CLIENT_HASHRATE_PERSIST_INTERVAL_MS = 60 * 1000;
const FIXED_STANDARD_EXTRANONCE2 = '0000000000000000';
const DEFAULT_SV2_JOB_RETENTION_MS = 5 * 60 * 1000;
const DEFAULT_SV2_MAX_RETAINED_JOBS_PER_CHANNEL = 16;
const DEFAULT_SV2_MAX_RETAINED_JOBS_PER_CHANNEL = 64;
const DEFAULT_SV2_MAX_QUEUED_JOB_OPERATIONS = 4;
const DEFAULT_SV2_MAX_SOCKET_BUFFER_BYTES = 256 * 1024;
const DEFAULT_SV2_SOCKET_WRITE_TIMEOUT_MS = 2 * 1000;