Widen the job retention window from ~60s to ~300s - #72
Merged
rsantacroce merged 1 commit intoSep 1, 2026
Conversation
A job stops being solvable about 60 seconds after it is issued, and the retention ring never gets a say. RECENT_JOBS is 8, which at the default 30s bitcoind_poll_interval_ms is 240s of capacity -- but RECENT_JOB_TTL_MS expires everything at 60s first, so the ring never binds and its slots are dead capacity. The comment above JOB_DIFF_RING describes retention the code does not actually have. That makes the effective window ~60-90s (60s plus the wait for the next job, since retire_job's sweep is lazy). ckpool's equivalent cap is 600s. So any miner whose work arrives slightly late -- a proxy, rented hashrate, anything with a hop in front of it -- gets its shares rejected as stale here and accepted everywhere else. Measured on a production pool before proposing this: over 16 hours of instrumented rejects, every stale rejection was for a job under 300s old, and the two worst-affected miners were running 11% and 39% rejects against a pool-wide 0.44%. After deploying a 300s window: pool-wide 0.05%, zero evictions in the following hour where ~350 were expected, and the 11% miner went to 0% across 654 shares. Three changes, and the second is why this is not a one-line patch: RECENT_JOB_TTL_MS 60000 -> 300000 RECENT_JOBS 8 -> 16 (so the ring stays slack, not binding) JOB_DIFF_RING 16 -> derived from RECENT_JOBS JOB_DIFF_RING was a literal 16 sized against a ring of 8. Raising the ring alone would leave the oldest still-solvable job with no difficulty entry, so a correct submit would be judged at the wrong difficulty -- a silent wrong answer rather than a build error. It is now derived. A _Static_assert ties the ring depth to the TTL so raising one without the other fails the build. Verified by shrinking the ring and watching the build break, not by assuming. test_job_survives_retirement_while_held pushed a hardcoded 24 jobs to force ring turnover, sized against a ring of 8. With the ring at 16 that margin falls to 1.5x, so it is raised to 64 and carries a comment saying it must stay ahead of RECENT_JOBS -- which is not visible from the test, so it cannot be derived. NOT included: marking shares stale when they predate a tip change, as ckpool does (workbase_id < blockchange_id) while keeping every same-height job valid regardless of age. That is arguably the better shape, but it would newly REFUSE shares that are credited today, at block boundaries, to exactly the miners this change is meant to help. It deserves its own change and its own evidence.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Jobs stop being solvable about 60 seconds after they are issued. Any miner with a hop in front of it — a proxy, rented hashrate — has its shares rejected as stale here while other pools accept them. ckpool's equivalent limit is 600s.
Measured on a production pool before proposing this. Over 16 hours of instrumented rejects, every stale rejection was for a job under 300s old, and the two worst-hit miners were running 11% and 39% rejects against a pool-wide 0.44%. After moving to a 300s window: pool-wide 0.05%, zero evictions in the next hour where ~350 were expected, and the 11% miner went to 0%.
Three changes, and the middle one is why this isn't a one-liner:
JOB_DIFF_RINGwas a literal 16 sized against a ring of 8. Raising the ring alone would leave the oldest still-solvable job with no difficulty entry, so a correct submit would be judged at the wrong difficulty — a silent wrong answer rather than a build error. It is now derived, and a_Static_assertties ring depth to the TTL so raising one without the other fails the build. Verified by shrinking the ring and watching the build break.One test needed updating:
test_job_survives_retirement_while_heldpushed a hardcoded 24 jobs to force ring turnover, sized against a ring of 8. At 16 that margin drops to 1.5x, so it is raised to 64 with a comment saying why.