Skip to content

submitter: bounded concurrency instead of one certified height at a time #195

Description

@bagelface

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

  • Default 1 is behaviour-identical to today
  • At concurrency 4, four certified heights submit concurrently and each emits exactly one Resolution
  • Deterministic-runtime test: one submission retrying does not delay another height's resolution
  • operator_cache hit rate does not degrade under concurrency (no duplicate lookups for the same operator)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlatencyFeatures related to latency improvementspriority:mediumImportant but not urgentthroughputTask throughput / parallel pipeline (roadmap#18, #19)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions