Skip to content

fix: drain pending tx queue in batches with durable ack#3351

Merged
chatton merged 8 commits into
mainfrom
fix/pending-tx-drain-bottlenecks
Jun 11, 2026
Merged

fix: drain pending tx queue in batches with durable ack#3351
chatton merged 8 commits into
mainfrom
fix/pending-tx-drain-bottlenecks

Conversation

@chatton

@chatton chatton commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Overview

Fixes the pending tx drain bottlenecks we hit on testnet.

The queue used to hand out one entry per block, so a backlog of small entries took forever to drain. Drain now merges entries into a single batch up to the block's byte limit.

WAL entries were also deleted as soon as they were dequeued, so txs could be lost if the block never committed. Entries are now kept until the block is committed and explicitly acked. Failed acks are retried before the next block, and on restart anything already included in the last block is dropped from the reloaded queue.

Dedup moved from the reaper's in-memory cache into the queue itself, since the cache didn't survive restarts and let pending txs get re-submitted and included twice. The seen-set is rebuilt from the WAL on load and hashes are only released once their txs are committed.

Postponed txs are also handled by the queue now: persisted before the source entries are deleted, so ack retries can't lose or duplicate them.

Tested with a burst of 500K transactions, the important part of the fix being the sub 1s queue wait, this was ~20mins+ before this change.

out

Summary by CodeRabbit

  • Bug Fixes

    • Retries failed batch acknowledgements before producing the next block.
    • Startup crash-recovery drops transactions already committed to the last block.
  • New Features

    • WAL-backed batch queue with Drain/Ack semantics, in-flight tracking and tx-level deduplication.
    • Sequencer exposes an explicit batch-ack API; reconciles queued entries on restart.
    • Tracing preserves batch-ack capability when present.
  • Chores

    • Removed legacy transaction caching subsystem; reaper simplified to notification-focused draining.
  • Tests

    • Updated and added tests for drain/ack, dedup, ack retries, reaper notifications, and crash-recovery.

Resolves slow pending-tx drain caused by one-queue-entry-per-block
consumption and seen-on-enqueue dedup poisoning:

- single sequencer drains multiple queue entries per block up to
  MaxBytes; WAL entries are deleted only on ack after block commit,
  with retry before the next batch and startup reconciliation against
  the last committed block (DropIncluded)
- tx dedup moves from the 30-min cache-manager tx cache into the batch
  queue itself, keyed by sha256, released on ack; dead cache tx-seen
  subsystem removed
- postponed txs are requeued durably during ack and stay deduped
- Load cleans duplicate/stale WAL entries so restarts cannot
  resurrect committed txs
- reaper submits one scrape per interval and notifies the executor
  only when new entries were actually queued (explicit pending count
  wiring, immune to tracing wrappers)
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e78ab1d8-d21c-4b48-ae57-50bad5f372a6

📥 Commits

Reviewing files that changed from the base of the PR and between e221198 and 1ed69b3.

📒 Files selected for processing (4)
  • block/components.go
  • block/internal/executing/executor.go
  • block/internal/executing/executor_logic_test.go
  • pkg/telemetry/sequencer_tracing.go
💤 Files with no reviewable changes (1)
  • block/components.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • block/internal/executing/executor_logic_test.go

📝 Walkthrough

Walkthrough

Migrates tx dedup into a WAL-backed BatchQueue with Drain/Ack and in-flight bookkeeping, removes the tx-cache subsystem, simplifies reaper submission (no cache dedup), adds sequencer ack APIs and executor retry-before-next-block for post-commit batch acknowledgement, and updates tests/benches and component wiring.

Changes

Batch queue drain/ack with in-memory dedup and cache removal

Layer / File(s) Summary
Remove transaction-hash caching from cache system
block/internal/cache/manager.go, block/internal/cache/generic_cache.go, block/internal/cache/manager_test.go, block/internal/cache/generic_cache_test.go
Removes DefaultTxCacheRetention, all tx-caching methods from CacheManager (IsTxSeen, AreTxsSeen, SetTxSeen, SetTxsSeen, CleanupOldTxs), batch "seen" helpers from Cache, and tx-cache init/cleanup and related tests.
Refactor BatchQueue core: WAL, in-flight, dedup, Drain/Ack
pkg/sequencers/single/queue.go
Adds in-flight/postponed state, txSeen dedup, logger injection, Drain, Ack, SetPostponed, DropIncluded, load-time dedup/migration, and WAL-backed persistence/deletion logic.
BatchQueue tests and migration updates
pkg/sequencers/single/queue_test.go, pkg/sequencers/single/queue_migration_test.go
Rewrites tests to Drain/Ack semantics, injects zerolog.Nop() logger where needed, adds drainOne helper, and adds extensive WAL/dedup/restart/ack-retry/drop-included coverage.
Sequencer integration and startup reconciliation
pkg/sequencers/single/sequencer.go, pkg/sequencers/single/sequencer_test.go
Constructs queue with logger, increases load timeout, reconciles reloaded queue with last block via DropIncluded, switches GetNextBatch to queue.Drain/SetPostponed, and exposes AckBatch API; tests updated and new crash-recovery test added.
Reaper and executor ack logic
block/internal/reaping/reaper.go, block/internal/reaping/reaper_test.go, block/internal/reaping/bench_test.go, block/internal/executing/executor.go, block/internal/executing/executor_logic_test.go
Reaper drops cache-based dedup and cleanup, submits fetched txs directly; executor adds SetOnBatchCommitted, pendingBatchAck retry-before-next-block, and post-commit ack invocation with deferred retry on failure; tests and benchmarks updated.
Component wiring for enqueue counter and ack
block/components.go, pkg/telemetry/sequencer_tracing.go
Captures the raw sequencer before tracing, preserves AckBatch through tracing wrappers, updates NewReaper call shape, and registers executor ack callback when supported (warns otherwise).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • julienrbrt
  • tac0turtle
  • alpe

🐰 I dug through WAL and code with care,
Batches now drain and wait for their fair share.
Dedup moved home from cache to queue,
Acks retry kindly until they succeed true.
🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing pending transaction queue drain bottlenecks by implementing batch draining with durable acknowledgment.
Description check ✅ Passed The description is comprehensive and well-structured, covering context, goals, technical rationale, and concrete results with performance metrics.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pending-tx-drain-bottlenecks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Claude finished @chatton's task in 4m 25s —— View job


Code Review: fix/pending-tx-drain-bottlenecks

  • Load repository context and AGENTS.md
  • Read core changed files (queue.go, sequencer.go, executor.go, reaper.go, components.go)
  • Review cache removal (cache/manager.go, cache/generic_cache.go)
  • Review tests (queue_test.go, sequencer_test.go, reaper_test.go, executor_logic_test.go)
  • Review telemetry changes
  • Synthesize findings and post full review

Overall Assessment

This is a well-engineered fix for a real production problem. The WAL-backed Drain/Ack queue design is sound, the dedup migration from reaper cache to queue is correct, and crash recovery is properly handled. The core invariants are maintained and the tests cover the important paths. A few issues worth discussing before merge.


Correctness — Key Invariants ✅

Rollback ordering (queue.go:320-338): The reverse-iteration prepend in rollbackInFlightLocked correctly restores in-flight items in original order. For inFlight = [A, B, C] with head == 3: i=2→queue[2]=C, i=1→queue[1]=B, i=0→queue[0]=A. ✅

postponedItem guard (queue.go:207): The postponedItem != nil check in SetPostponed correctly makes the call a no-op after a failed Ack, preventing the already-persisted postponed entry from being replaced. ✅

DropIncluded in-place compaction (queue.go:406): The kept := bq.queue[:bq.head] alias pattern is correct — writes always trail reads since at most one item is appended per item iterated. ✅

Prepend key ordering: nextPrependSeq counts down from initialSeqNum - 1, producing keys like 0x7FFFFFFFFFFFFFFF which are lexicographically smaller than append keys (0x8000000000000000 and up). WAL reload sorts and processes prepended entries first. ✅

Tracing wrapper forwards AckBatch (telemetry/sequencer_tracing.go:37-41): WithTracingSequencer returns a tracedAckSequencer when the inner sequencer implements AckBatch, so the interface probe in NewExecutor still succeeds through the tracing layer. ✅


Issues

1. Block production liveness under persistent WAL failure — Medium

ProduceBlock (executor.go:516-519) returns an error on every call when pendingBatchAck is set and the ack keeps failing. The execution loop logs and retries indefinitely. If the WAL backend is permanently inaccessible, the node halts block production with no escalation path. The comment in reconcileQueueWithLastBlock says "at most one committed block can be un-acked at any time" — this invariant is correct but relies entirely on the executor's retry behaviour. Consider documenting what operators should do in this state (manual WAL repair?) or adding a metric/alert for pending ack age.

2. Single-block reconciliation scope — Low/Informational

reconcileQueueWithLastBlock only inspects the last block (height). The assumption is correct — the executor retries a failed ack before producing the next block, so only one committed-but-un-acked block can exist. But this invariant is only stated in a comment. If the retry is ever bypassed (e.g., a future refactor of ProduceBlock), stale txs from earlier blocks will be re-included silently. Worth adding an assertion or at minimum a louder comment at the call site in NewSequencer.

3. Unconditional SetPostponed(nil) — Nit

sequencer.go:362 calls c.queue.SetPostponed(postponedMempoolTxs) unconditionally. When postponedMempoolTxs is nil, SetPostponed acquires the lock and sets bq.inFlightPostponed = nil — harmless since the len(...) > 0 guard in Ack skips the WAL persist. But it's a lock acquisition on every GetNextBatch regardless. Fix this →

// before
c.queue.SetPostponed(postponedMempoolTxs)

// after  
if len(postponedMempoolTxs) > 0 {
    c.queue.SetPostponed(postponedMempoolTxs)
}

4. WAL read failure hard-fails startup without retry — Low

queue.go:469-470 returns immediately on any datastore read error during Load(), crashing startup. The rationale is sound (silently dropping WAL entries is worse), but a transient datastore hiccup during a restart permanently prevents the node from booting until the operator intervenes. CodeRabbit's suggestion of bounded retry/backoff around the query is reasonable for production hardening. At minimum, the error message could surface actionable guidance.

5. maxBytes=1 in drainOne test helper — Nit

queue_test.go:75 calls bq.Drain(ctx, 1) with maxBytes=1. This works because the drain loop always includes at least the first entry (len(allTxs) > 0 check), so it's functionally "drain one item regardless of size." Consider renaming to maxBytes=0 (drain all) and breaking on size, or using a dedicated "drain exactly one entry" helper, to avoid confusion when reading tests.


Missing Test Coverage

  • DropIncluded with head > 0: No test exercises DropIncluded on a queue where some entries have already been drained (head-advanced). The in-place compaction with kept := bq.queue[:bq.head] deserves a targeted test.
  • Concurrent stress test: The PR lacks a go test -race targeted test for concurrent AddBatch/Drain/Ack. The implementation uses a single mutex correctly, but a brief race test (even a simple parallel goroutine driving adds/drains) would catch any future mutex regression.
  • WAL retry after partial DropIncluded: No test for the case where DropIncluded successfully deletes fully-included entries but crashes mid-way through rewriting partially-included entries, then restarts and runs again.

Architecture

The batchAcknowledger interface is duplicated in both executing (executor.go:105) and telemetry (sequencer_tracing.go:24). This is necessary in Go (unexported interfaces from separate packages), but it means both definitions need to stay in sync if AckBatch's signature ever changes. A shared exported interface in core/sequencer would remove this fragility — though that's a separate PR concern.

The julienrbrt comment about moving AckBatch wiring into NewExecutor is already done in this PR — the constructor probes the interface directly (executor.go:174), which is cleaner than the previous public-setter approach.


Good Stuff

  • The context.WithoutCancel use in rollbackInFlightLocked (queue.go:311) for best-effort cleanup during shutdown is exactly right.
  • The clear(bq.inFlight); bq.inFlight = bq.inFlight[:0] pattern preserves the backing array for reuse while allowing GC of old elements.
  • TestProduceBlock_RetriesFailedAckBeforeNextBlock is a clean, deterministic test with a failure counter — no sleeps or goroutines needed.
  • TestSequencer_ReconcileQueueWithLastBlock_CrashRecovery directly exercises the invariant that matters most.
  • Removing the reaper's in-memory dedup cache significantly simplifies that component.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedJun 11, 2026, 1:59 PM

chatton added 2 commits June 11, 2026 08:59
- defer postponed entry requeue until ack fully succeeds so a drain
  rollback after a failed ack neither loses nor duplicates postponed txs
- replace fmt.Printf with structured logging in BatchQueue
- use monotonic enqueue counter in reaper to detect new submissions
  race-free against concurrent drain/ack
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 51 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.00%. Comparing base (3fed700) to head (1ed69b3).

Files with missing lines Patch % Lines
pkg/sequencers/single/queue.go 84.57% 17 Missing and 10 partials ⚠️
pkg/sequencers/single/sequencer.go 60.52% 9 Missing and 6 partials ⚠️
pkg/telemetry/sequencer_tracing.go 33.33% 3 Missing and 1 partial ⚠️
block/internal/executing/executor.go 82.35% 1 Missing and 2 partials ⚠️
block/internal/reaping/reaper.go 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3351      +/-   ##
==========================================
+ Coverage   60.72%   61.00%   +0.28%     
==========================================
  Files         127      127              
  Lines       13781    13879      +98     
==========================================
+ Hits         8368     8467      +99     
+ Misses       4500     4488      -12     
- Partials      913      924      +11     
Flag Coverage Δ
combined 61.00% <80.00%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chatton chatton changed the title fix: [WIP] drain pending tx queue in batches with durable ack fix: drain pending tx queue in batches with durable ack Jun 11, 2026
@chatton chatton marked this pull request as ready for review June 11, 2026 11:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/sequencers/single/queue.go (1)

135-138: ⚡ Quick win

Consider wrapping the persistence error for better context.

The error from persistBatch is returned directly. Wrapping it would provide better context for debugging failures.

📝 Suggested improvement
 	key := seqToKey(bq.nextAddSeq)
 	if err := bq.persistBatch(ctx, batch, key); err != nil {
 		bq.rollbackSeenLocked(hashes)
-		return err
+		return fmt.Errorf("failed to persist batch to WAL: %w", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/sequencers/single/queue.go` around lines 135 - 138, The call to
bq.persistBatch returns an error that is propagated directly; wrap that error
with contextual information before returning so callers know what operation
failed. Update the if block that calls bq.persistBatch(ctx, batch, key) to wrap
the returned err (e.g., include the key and operation name) when returning, and
ensure the package imports fmt (or errors) to use error wrapping via %w (or
errors.Wrap) so the original error is preserved; keep the rollback call to
bq.rollbackSeenLocked(hashes) unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/sequencers/single/queue.go`:
- Around line 135-138: The call to bq.persistBatch returns an error that is
propagated directly; wrap that error with contextual information before
returning so callers know what operation failed. Update the if block that calls
bq.persistBatch(ctx, batch, key) to wrap the returned err (e.g., include the key
and operation name) when returning, and ensure the package imports fmt (or
errors) to use error wrapping via %w (or errors.Wrap) so the original error is
preserved; keep the rollback call to bq.rollbackSeenLocked(hashes) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 686eac89-6664-447a-bb99-cef8eb2de766

📥 Commits

Reviewing files that changed from the base of the PR and between 29e8542 and dda0c60.

📒 Files selected for processing (14)
  • block/components.go
  • block/internal/cache/generic_cache.go
  • block/internal/cache/generic_cache_test.go
  • block/internal/cache/manager.go
  • block/internal/cache/manager_test.go
  • block/internal/executing/executor.go
  • block/internal/reaping/bench_test.go
  • block/internal/reaping/reaper.go
  • block/internal/reaping/reaper_test.go
  • pkg/sequencers/single/queue.go
  • pkg/sequencers/single/queue_migration_test.go
  • pkg/sequencers/single/queue_test.go
  • pkg/sequencers/single/sequencer.go
  • pkg/sequencers/single/sequencer_test.go
💤 Files with no reviewable changes (3)
  • block/internal/cache/manager_test.go
  • block/internal/cache/generic_cache_test.go
  • block/internal/cache/generic_cache.go

chatton added 2 commits June 11, 2026 13:28
- fail Load on datastore read errors instead of dropping txs
- detach rollback WAL cleanup from drain context so it runs on shutdown
- raise queue load timeout for large WAL backlogs
- merge postponed-tx collection into the filter loop
- document DropIncluded aliasing, SetPostponed contract, txSeen bound
- add tests: executor ack retry, reconcile crash recovery, bulk-prepend rollback

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/sequencers/single/queue.go (1)

472-474: WAL datastore read errors hard-fail node startup (no internal retry/backoff)

WAL read failure returns error instead of continuing
			// a datastore read failure means the WAL cannot be trusted as
			// loaded — fail startup rather than silently dropping txs.
			return fmt.Errorf("failed to read WAL entry from datastore: %w", result.Error)
  • pkg/sequencers/single/queue.go returns an error on any datastore read failure while loading the WAL; only protobuf decode errors are logged and skipped (queue.go:483).
  • That error propagates through pkg/sequencers/single/sequencer.go (failed to load batch queue from DB) back to the app RunE handlers (apps/*/cmd/run.go), which then bubbles up to rootCmd.Execute() and terminates the process (apps/*/main.go calls os.Exit(1)).

Consider adding a bounded retry/backoff around the WAL db.Query/load path if startup availability during transient datastore issues matters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/sequencers/single/queue.go` around lines 472 - 474, The WAL loader in
pkg/sequencers/single/queue.go currently returns immediately on any datastore
read failure (the path that returns fmt.Errorf("failed to read WAL entry...":
%w, result.Error)), causing hard-fail startup; instead, wrap the datastore read
(the db.Query / WAL load block inside the queue/WAL load function) in a bounded
retry with exponential backoff and jitter (configurable max attempts and total
timeout) so transient datastore errors are retried before failing, while
preserving the existing behavior of logging and skipping protobuf decode errors;
ensure the retry only surrounds the datastore read/result.Error check and still
returns an error if retries are exhausted so callers like the sequencer load
caller (the loadBatchQueue/loadQueue code path used by
pkg/sequencers/single/sequencer.go) observe a final failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/sequencers/single/queue.go`:
- Around line 472-474: The WAL loader in pkg/sequencers/single/queue.go
currently returns immediately on any datastore read failure (the path that
returns fmt.Errorf("failed to read WAL entry...": %w, result.Error)), causing
hard-fail startup; instead, wrap the datastore read (the db.Query / WAL load
block inside the queue/WAL load function) in a bounded retry with exponential
backoff and jitter (configurable max attempts and total timeout) so transient
datastore errors are retried before failing, while preserving the existing
behavior of logging and skipping protobuf decode errors; ensure the retry only
surrounds the datastore read/result.Error check and still returns an error if
retries are exhausted so callers like the sequencer load caller (the
loadBatchQueue/loadQueue code path used by pkg/sequencers/single/sequencer.go)
observe a final failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 188839d7-9e11-4b38-b2b7-5ebe73172a22

📥 Commits

Reviewing files that changed from the base of the PR and between dda0c60 and d9ebe05.

📒 Files selected for processing (5)
  • block/internal/executing/executor_logic_test.go
  • pkg/sequencers/single/queue.go
  • pkg/sequencers/single/queue_test.go
  • pkg/sequencers/single/sequencer.go
  • pkg/sequencers/single/sequencer_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/sequencers/single/sequencer.go
  • pkg/sequencers/single/queue_test.go

@julienrbrt julienrbrt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I don't like about this is the coupling, even though loose between the single sequencer queue and the reaper.

Can we add a changelog?

Comment thread block/internal/reaping/reaper.go Outdated
r.cache.SetTxsSeen(newHashes)
totalSubmitted += len(newTxs)
var before uint64
if r.totalEnqueuedBatches != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look super needed tbf apart from the metrics, and this adds the loose coupling.
and brings to some hacks line L158 for based/solo which does not implement this.

Can we just simplify here with a simple len(txs) which would be ok for 99% of the cases on all sequencers?

The only thing we risks is onTxsSubmitted being called early which could at worse lead to an empty block.

@julienrbrt julienrbrt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly lgtm.

Comment thread block/components.go Outdated
return nil, fmt.Errorf("failed to create reaper: %w", err)
}

// wire batch ack callback so drained queue entries are committed after block commit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we move this in the executor constructor, we get the sequencer data there.
this would decrease the public api surface.

@chatton chatton added this pull request to the merge queue Jun 11, 2026
Merged via the queue into main with commit 85119a6 Jun 11, 2026
59 of 62 checks passed
@chatton chatton deleted the fix/pending-tx-drain-bottlenecks branch June 11, 2026 15:23
@claude claude Bot mentioned this pull request Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants