Consumer context: gas-killer/roadmap#18 (P4). This becomes the binding constraint once the sequencer drives multiple heights (#193). Service re-pins its rev in gas-killer/service#375.
Problem
Submitter::run (router/src/submitter.rs:138-143) is strictly serial:
pub async fn run(mut self) {
while let Some(certified) = self.certified.recv().await {
self.handle_certified(certified).await;
}
}
Everything is inside handle_certified: the block-number fetch, pubkeyHashToOperator resolution, getNonSignerStakesAndSignature, the application handler, and the retry loop with tokio::time::sleep(backoff) at :233 (MAX_RETRIES = 2, 2s → 4s).
So W heights certify concurrently and then execute one at a time. On the Gas Killer consumer that work is measured at 8.0 / 15.6 s p50/p95 today.
Worse, it is self-amplifying: a single slow or retrying submission delays every other height's Resolution, which delays the sequencer's window from advancing, which can push other in-flight heights past ROUND_TIMEOUT and turn them into skips — which is a much more expensive failure than a slow submission.
Scope
- Bounded-concurrency dispatch inside
run() — a JoinSet or FuturesUnordered capped by a new config value, default 1 so behaviour is unchanged until deliberately raised
- Requires
H: Clone + Send on the handler. Worth confirming with the consumer first, though the Gas Killer handler holds cloneable providers and Arcs, so this looks cheap
operator_cache (:84) is a plain HashMap on &mut self; move it behind a DashMap or an Arc<RwLock<..>> so concurrent submissions share cache hits instead of each paying the pubkeyHashToOperator round-trip
- Preserve the
Resolution contract: each height must still emit exactly one resolution, and the sequencer must not care about ordering between heights. Verify against the windowed sequencer's on_resolution, which handles arbitrary arrival order — but state the guarantee explicitly rather than relying on it
- Consider whether the retry backoff should be per-submission rather than serializing the whole channel
A caution for consumers that broadcast transactions
The Gas Killer router currently renders a payload rather than sending a transaction, so it has no nonce to race. A consumer whose handler broadcasts from a single wallet will need a submit lock or a nonce manager before raising the concurrency — the naive per-tx eth_getTransactionCount pattern collides. Worth a note in the config doc so nobody raises the knob and discovers this on mainnet.
Acceptance
Consumer context: gas-killer/roadmap#18 (P4). This becomes the binding constraint once the sequencer drives multiple heights (#193). Service re-pins its
revin gas-killer/service#375.Problem
Submitter::run(router/src/submitter.rs:138-143) is strictly serial:Everything is inside
handle_certified: the block-number fetch,pubkeyHashToOperatorresolution,getNonSignerStakesAndSignature, the application handler, and the retry loop withtokio::time::sleep(backoff)at:233(MAX_RETRIES = 2, 2s → 4s).So W heights certify concurrently and then execute one at a time. On the Gas Killer consumer that work is measured at 8.0 / 15.6 s p50/p95 today.
Worse, it is self-amplifying: a single slow or retrying submission delays every other height's
Resolution, which delays the sequencer's window from advancing, which can push other in-flight heights pastROUND_TIMEOUTand turn them into skips — which is a much more expensive failure than a slow submission.Scope
run()— aJoinSetorFuturesUnorderedcapped by a new config value, default 1 so behaviour is unchanged until deliberately raisedH: Clone + Sendon the handler. Worth confirming with the consumer first, though the Gas Killer handler holds cloneable providers andArcs, so this looks cheapoperator_cache(:84) is a plainHashMapon&mut self; move it behind aDashMapor anArc<RwLock<..>>so concurrent submissions share cache hits instead of each paying thepubkeyHashToOperatorround-tripResolutioncontract: each height must still emit exactly one resolution, and the sequencer must not care about ordering between heights. Verify against the windowed sequencer'son_resolution, which handles arbitrary arrival order — but state the guarantee explicitly rather than relying on itA caution for consumers that broadcast transactions
The Gas Killer router currently renders a payload rather than sending a transaction, so it has no nonce to race. A consumer whose handler broadcasts from a single wallet will need a submit lock or a nonce manager before raising the concurrency — the naive per-tx
eth_getTransactionCountpattern collides. Worth a note in the config doc so nobody raises the knob and discovers this on mainnet.Acceptance
Resolutionoperator_cachehit rate does not degrade under concurrency (no duplicate lookups for the same operator)