diff --git a/src/server/tasks.js b/src/server/tasks.js index 801bcaa..fb54855 100644 --- a/src/server/tasks.js +++ b/src/server/tasks.js @@ -294,7 +294,7 @@ export class Tasks { return this.store.tx(() => { for (const id of ids) this.requireLease(user, id); // Keep the verification queue moving: a few batches waiting per account at a time. - const max = this.config.maxQueuedBatches ?? 4; + const max = this.config.maxQueuedBatches ?? 8; const waiting = this.store.get(`SELECT COUNT(*) AS n FROM batches WHERE user_id = ? AND state IN ('queued', 'verifying')`, user.id).n; if (waiting >= max) { throw new HttpError(429, `you have ${waiting} batches waiting for verification (the limit is ${max}); keep your claims, do peer reviews meanwhile (td_list_peer_reviews), and submit again when one is done (td_get_batch shows it)`, { retryAfterSeconds: 120 }); diff --git a/src/server/verify.js b/src/server/verify.js index ac3ea8e..f4ad594 100644 --- a/src/server/verify.js +++ b/src/server/verify.js @@ -79,7 +79,12 @@ export class Verifier { // Verification compiles only the submitted units, so every batch is small here; the units // that include a changed header are compiled in the merge's big lane. const b = this.store.tx(() => { - const row = this.store.get(`SELECT * FROM batches WHERE state = 'queued' ORDER BY id LIMIT 1`); + // Fair between accounts: the oldest batch of whoever has the fewest batches verifying, so a + // fast contributor with several batches waiting does not hold every slot. + const row = this.store.get( + `SELECT b.* FROM batches b WHERE b.state = 'queued' + ORDER BY (SELECT COUNT(*) FROM batches v WHERE v.user_id = b.user_id AND v.state = 'verifying'), b.id LIMIT 1`, + ); if (row) { this.store.run(`UPDATE batches SET state = 'verifying' WHERE id = ?`, row.id); this.store.run(`UPDATE attempts SET state = 'verifying' WHERE batch_id = ? AND state = 'queued'`, row.id);