Skip to content

feat(requester): auto-heal stuck nonce markers via reconciliation loop - #985

Open
vishalchangrani wants to merge 11 commits into
mainfrom
vishal/mempool-reconcile-loop-sf
Open

feat(requester): auto-heal stuck nonce markers via reconciliation loop#985
vishalchangrani wants to merge 11 commits into
mainfrom
vishal/mempool-reconcile-loop-sf

Conversation

@vishalchangrani

@vishalchangrani vishalchangrani commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a background reconciliation loop to TxMemPool that auto-heals stuck nonce markers when a wrapping Cadence tx reverts or silently disappears, instead of waiting the full idle-eviction window (60s).
  • Recovers a wedged EOA within ~one Flow sealing window (~6-8s) by polling GetTransactionResult(lastFlowTxID) for each active EOA and clearing lastConsecutivelySubmitted / submitting / lastFlowTxID when the wrapper is sealed-with-error or unsealed past TxReconcileStaleAfter.
  • Two new config flags: --tx-reconcile-interval (default 1s) and --tx-reconcile-stale-after (default 30s).
  • New Prometheus counter TxPoolReconcileReset{reason} and a WARN log line at every reset for observability.

Why now

Real production incident on 2026-07-29 at 03:09:11Z (foundation gateway, EOA 0xdEA93da411CFB1f7d676BF81A686ED36C1d358B3, EVM nonce 25882):

  • Two consecutive-nonce wrappers (25881, 25882) were both submitted and landed in the same Flow block (159604703).
  • The collector executed them out of order. The 25881 wrapper ran fine; the 25882 wrapper's assert() in run.cdc reverted with evm_error=nonce too high (Cadence tx b946d5c3ea1a80bc165d26824c7504b28dd84c930f182cd048fef52d5c2ca16d).
  • Because the wrapper reverted, no EVM.TransactionExecuted event fired for the 25882 EVM tx — the mempool never learned it had failed.
  • lastConsecutivelySubmitted stayed stuck at 25882. Every subsequent retry returned ErrInFlightNonce. The wedge cleared only after ~65 seconds via the 60s idle-eviction of the empty queue.

With the reconciliation loop, the same scenario clears in ~7s: the reverted wrapper seals with a non-nil Error, the next tick observes it, and the marker is reset so the client's very next resubmission is accepted and re-submitted with the correct on-chain nonce.

Full analysis is on the issue thread: #983 (comment).

What changed

  • New fields
    • eoaQueue.lastFlowTxID (flow.Identifier) — the Flow tx ID of the most recent Cadence submission, populated by the fast path and reconcileSubmission, cleared on reset.
    • TxMemPool.getTxResult — injectable func(ctx, id) (*flow.TransactionResult, error), defaults to client.GetTransactionResult, injected in tests to run without a live AN.
  • New method on nonceTracker: resetSubmissionState() clears both lastConsecutivelySubmitted and submitting.
  • New goroutine: reconcileLoop ticks on TxReconcileInterval, calls reconcileOnce. Snapshot taken under queueMux, network call issued outside the lock, reset re-acquires the lock and re-checks the flow_tx_id still matches and that q.nonces.inFlight() is false, so neither a superseding ack'd submission nor a freshly-in-flight one is ever clobbered.
  • New config: TxReconcileInterval (default 1s), TxReconcileStaleAfter (default 30s) in config/config.go with corresponding CLI flags and validators in cmd/run/cmd.go. NewTxMemPool also defensively backfills these to the CLI defaults when zero, so programmatic constructors (the e2e test config) don't trigger time.NewTicker(0).
  • New metric: TxPoolReconcileReset(reason) — Prometheus counter with reason label (wrapper-reverted | unsealed-past-threshold). Nop implementation added.
  • Docs: BEHAVIOR SPEC in services/requester/tx_mempool.go extended with a new "Recovery" section (case 13); README's Configuration Flags table gains the two new flags.

Commits

  1. feat(requester): add mempool reconciliation loop to auto-heal stuck nonce markers — implementation.
  2. test(requester): unit tests for mempool reconciliation loop — focused tests covering revert, staleness, healthy sealed, unsealed-fresh, no-marker skip, superseded-flow-tx-id skip, and end-to-end unwedging.
  3. docs(requester): reconciliation loop behavior spec and README.
  4. fix(requester): reviewer-flagged blockers in reconciliation loop — defensive defaults in NewTxMemPool for zero durations (fixes e2e time.NewTicker(0) panic), and an inFlight() guard in reconcileOnce to prevent clobbering a fresh in-flight submission (guarded by a regression test).
  5. chore(requester): align reconcile-loop with PR #984 flow_tx_id naming — small alignment after the parent PR's naming decision.

Base branch

Targets mpeter/poc-index-finalized-block-results. PR #984 has been squash-merged into this base, so this PR now sits directly on top with no stacking dependency.

Test plan

  • go build ./... — clean.
  • go vet ./services/requester/... — clean.
  • go test ./services/requester/ -count=1 -race — passes; 8 reconcile-loop unit tests included:
    • Test_TxMemPool_ReconcileClearsMarkerWhenWrapperReverted
    • Test_TxMemPool_ReconcileClearsMarkerWhenWrapperStale
    • Test_TxMemPool_ReconcileLeavesMarkerWhenWrapperSealedSuccessfully
    • Test_TxMemPool_ReconcileLeavesMarkerWhenWrapperUnsealedAndFresh
    • Test_TxMemPool_ReconcileSkipsQueueWithoutMarker
    • Test_TxMemPool_ReconcileSkipsSupersededFlowTxID
    • Test_TxMemPool_ReconcileSkipsWhenFreshBatchInFlight (regression guard on the fix commit)
    • Test_TxMemPool_ReconcileClearsAllowsSubsequentAddToBeAccepted
  • E2E Test_TxMemPool in tests/ — passes (~33s).
  • Testnet dry-run: confirm TxPoolReconcileReset counter stays at zero under normal load; simulate a reverted wrapper and confirm the reset log fires within one tick.

Config recommendation

Deploy with the defaults (--tx-reconcile-interval=1s --tx-reconcile-stale-after=30s). Both defaults are conservative:

  • 1s polling is cheap (one AN GetTransactionResult call per active EOA per second, only for EOAs with an outstanding marker).
  • 30s stale-after gives Flow's sealing latency (~6-8s) a very wide margin so the reconciler cannot false-positive on healthy-but-slow-to-seal submissions.

After deployment, monitor:

  • The TxPoolReconcileReset{reason} counter (should be near zero; a non-zero rate on wrapper-reverted indicates real intra-block reordering; a non-zero rate on unsealed-past-threshold indicates AN issues).
  • The corresponding WARN log lines (grep for reconciliation clearing stuck in-flight marker).

Once we have a week or two of production data characterising sealing latency and reordering rates, we can tune TxReconcileStaleAfter down (e.g. to 15s) to shrink the worst-case wedge duration on silent AN drops.

Summary by CodeRabbit

  • New Features
    • Added configuration options for transaction reconciliation frequency and stale transaction handling.
    • Added transaction-pool reconciliation to detect reverted, sealed, or stale submissions.
    • Added metrics tracking reconciliation resets by reason.
  • Bug Fixes
    • Improved handling of stale and superseded transaction submissions.
    • Enabled successful retries after stale transaction markers are cleared.

vishalchangrani and others added 5 commits July 29, 2026 12:18
…once markers

Adds a background reconciliation loop to TxMemPool that polls the most recent
wrapping Cadence tx for each active EOA and clears the in-flight nonce marker
when the wrapper cannot advance the on-chain nonce. Bounds wedge duration to
one sealing window (~6-8s) instead of the full idleQueueRetention (~1 minute).

Motivation: the DFNS silent-drop incident (2026-07-29T03:09:11Z, EOA
0xdEA9...58B3, nonce 25882) showed the wrapping Cadence tx sealing with
evm_error=nonce too high after intra-block Collection Node reordering. In
that case the mempool has already advanced `lastConsecutivelySubmitted` on
successful send, so subsequent retries got ErrInFlightNonce until the
idle-eviction retention expired — that is the wedge this loop closes.

Implementation:
  * eoaQueue gains lastFlowTxID, set on every successful submission (fast
    path in Add, and reconcileSubmission after a background submitWork).
  * reconcileSubmission now takes the flowTxID so it can persist it on the
    queue alongside markSubmitted (existing behavior otherwise unchanged).
  * nonceTracker.resetSubmissionState clears both submission markers so the
    next Add re-classifies against on-chain state.
  * reconcileLoop ticks at TxReconcileInterval, snapshots per-EOA
    (flowTxID, lastSubmittedAt) under the lock, calls GetTransactionResult
    outside the lock, and resets state only on concrete evidence:
    SEALED-with-error, or unsealed past TxReconcileStaleAfter. Transient AN
    errors do not touch state. A short critical section around the reset
    re-checks that the same flowTxID still owns the queue.
  * Injectable getTxResult field mirrors the existing submitBatch pattern
    so tests can drive reconciliation without a live Access Node.
  * New metric TxPoolReconcileReset(reason) with labels
    {wrapper-reverted, unsealed-past-threshold}.
  * New config knobs TxReconcileInterval (default 1s) and
    TxReconcileStaleAfter (default 30s), both validated > 0 when
    --tx-mempool-mode=true.

Test file change is minimal: only satisfies the new reconcileSubmission
signature (added flow.Identifier{} argument at four existing call sites)
and defaults getTxResult in newTestPool to a no-op fake. No new tests are
added here — a follow-up subagent covers behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds seven table-tests exercising TxMemPool.reconcileOnce, covering every
branch of the recovery path (behavior-spec case 13):

  - wrapper reverted (Sealed + non-nil Error) clears the marker
  - wrapper unsealed past TxReconcileStaleAfter clears the marker
  - wrapper Sealed with no error leaves the marker in place
  - wrapper unsealed and fresh leaves the marker in place
  - queue without an outstanding marker is skipped (no getTxResult call)
  - fresher submission superseding lastFlowTxID between snapshot and
    reset is respected (no wrongful reset)
  - end-to-end: post-reconcile, a same-nonce retry is accepted rather
    than rejected as in flight

All tests drive reconcileOnce synchronously through a fake clock plus
injected submitBatch/getTxResult, so no goroutines and no wall-clock
sleeps are involved. testPoolConfig() now seeds TxReconcileInterval and
TxReconcileStaleAfter to match the CLI defaults (1s / 30s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend the tx_mempool.go BEHAVIOR SPEC with a Recovery section
describing the reconciliation loop's ticks, reset triggers, concurrency
model, and observability signals. Fix a doc-vs-code inconsistency in
the reconcileLoop docstring: it listed three reset cases, but only two
are implemented. The "chain advanced past highestSent" case is
redundant, because a successfully-sealed wrapper is progressed by the
next legitimate submission, so no wedge needs clearing. Add the two
new CLI flags (tx-reconcile-interval, tx-reconcile-stale-after) to the
README Configuration Flags table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address two blockers surfaced during the code-review pass on the initial
implementation (f46029c3):

1. Panic in NewTxMemPool when a programmatic caller (e.g. the e2e test
   config in tests/tx_mempool_test.go) leaves TxReconcileInterval or
   TxReconcileStaleAfter at their zero values: time.NewTicker(0) panics.
   Fix: backfill both fields to their CLI-default values (1s, 30s)
   inside NewTxMemPool before starting the reconcile loop.

2. Behavioral race in reconcileOnce that could clobber a fresh in-flight
   submission. After the snapshot-outside-the-lock pattern, if a
   concurrent background flush called markSubmitting(newHigh) between
   snapshot and reset, the reconciler saw an unchanged lastFlowTxID and
   would reset — clearing submitting for the newer batch. That would let
   a client retry duplicate the in-flight nonce, reintroducing the exact
   duplicate-wrapper failure this loop is meant to eliminate. Fix: after
   the lastFlowTxID re-check under the lock, also require
   q.nonces.inFlight() to be false; a matching lastFlowTxID guarantees
   the previous batch's markSubmitted has returned, so any submitting we
   observe now must belong to a strictly newer batch.

Also enriches the WARN log with elapsed-since-submit duration and, when
known, the wrapper's Cadence error message.

Adds Test_TxMemPool_ReconcileSkipsWhenFreshBatchInFlight; verified to
FAIL without the fix and PASS with it.

Verified: go build/vet clean; unit tests pass under -race; e2e
Test_TxMemPool suite now passes cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #984's review consolidated the log field name from flow-tx-id to
flow_tx_id (underscores are safer for Grafana filter syntax). The
reconciliation loop's WARN log and its associated behavior-spec comment
still used the old dashed name after rebase; align them so a downstream
grep on 'flow_tx_id' catches both the submission and the reconciler
reset log lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@m-Peter, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 54 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cbdbe1a7-3aa7-4311-9b57-e1eb414b50d6

📥 Commits

Reviewing files that changed from the base of the PR and between 5f21e92 and 2e15b70.

📒 Files selected for processing (1)
  • services/requester/tx_mempool.go
📝 Walkthrough

Walkthrough

The transaction mempool now polls submitted Flow transactions, clears markers for reverted or stale wrappers, preserves newer submissions, and records reset metrics. Polling and stale thresholds are configurable through the CLI and configuration.

Changes

Transaction reconciliation

Layer / File(s) Summary
Reconciliation configuration and metrics
config/config.go, cmd/run/cmd.go, README.md, metrics/*
Adds polling and stale-wrapper durations, validates positive values, documents defaults, and exposes reset counters by reason.
Submission state tracking
services/requester/tx_mempool.go, services/requester/tx_mempool_test.go
Tracks successful Flow transaction IDs and submission timestamps across fast-path and background submissions.
Reconciliation loop and validation
services/requester/tx_mempool.go, services/requester/tx_mempool_test.go
Polls transaction results, clears reverted or stale markers, protects newer state, and tests sealed, unsealed, superseded, and retry scenarios.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: Improvement, EVM

Suggested reviewers: peterargue, m-peter

Sequence Diagram(s)

sequenceDiagram
  participant TxMemPool
  participant FlowAccessAPI
  participant EOAQueue
  participant MetricsCollector
  TxMemPool->>FlowAccessAPI: Poll transaction result for Flow transaction ID
  FlowAccessAPI-->>TxMemPool: Return sealed or unsealed transaction result
  TxMemPool->>EOAQueue: Revalidate and clear submission markers
  TxMemPool->>MetricsCollector: Increment reset counter with reason
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a reconciliation loop that automatically clears stuck nonce markers.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vishal/mempool-reconcile-loop-sf

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.

Comment thread services/requester/tx_mempool.go Outdated
// staleness threshold is exceeded.
sealed := err == nil && result != nil && result.Status >= flow.TransactionStatusSealed
reverted := sealed && result.Error != nil
stale := now.Sub(s.lastSubmittedAt) > t.config.TxReconcileStaleAfter

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.

The stale check ignores the poll result. A wrapper that sealed successfully with no follow-up submission within 30s is reset with reason unsealed-past-threshold (verified with a scratch test on this branch). Every idle-after-submit EOA emits a spurious WARN and counter increment, and the reset drops the ErrInFlightNonce guard while the index may still lag the seal. Gate staleness on not-landed, e.g. stale := !(sealed && result.Error == nil) && now.Sub(s.lastSubmittedAt) > t.config.TxReconcileStaleAfter, and you could add a regression test for sealed-success past the threshold.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good catch 💯 Fixed in 4269ef3 .

Comment thread services/requester/tx_mempool.go Outdated
// path below. But avoid touching state on transient AN errors — only
// reset if we have concrete evidence (SEALED-with-error) OR the
// staleness threshold is exceeded.
sealed := err == nil && result != nil && result.Status >= flow.TransactionStatusSealed

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.

Status >= Sealed also matches Expired (5 > 4). An expired wrapper will never land and should reset immediately, but here it counts as sealed with a nil Error, so it only heals via the stale path and a naive !sealed fix of the stale bug would make it never reset.

@m-Peter m-Peter Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

With the reconciliation loop, the same scenario clears in ~7s:

Within 6-8 seconds the transaction will likely have a status of TransactionStatusFinalized and not TransactionStatusSealed, so it's worth mentioning that the reconciliation loop won't be able to recover a wedged transaction within 6-8 seconds. I'm not sure if we want to check against a result.Status == flow.TransactionStatusFinalized to expedite the recovery. But then again, I wonder if GetTransactionResultsByBlockID or GetTransactionResult can fetch finalized blocks/transactions, or if they will be missing from AN, until they become sealed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good catch 👌 Updated in 0b45280, to check specifically for result.Status == flow.TransactionStatusSealed

Comment thread services/requester/tx_mempool.go Outdated
Str("flow_tx_id", s.flowTxID.Hex()).
Str("reason", reason).
Dur("elapsed-since-submit", elapsed)
if reverted && result.Error != nil {

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.

nit: reverted already implies result.Error != nil (line 1261). if reverted { suffices.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good point 👍 Updated in 15091f3 .

Comment thread services/requester/tx_mempool.go Outdated

now := t.now()
for _, s := range snaps {
result, err := t.getTxResult(ctx, s.flowTxID)

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.

nit: this call has no deadline, so one hung AN call stalls the whole reconcile loop indefinitely and silently disables healing. Wrap each poll in context.WithTimeout (e.g. the tick interval), mirroring the fastPathSubmitTimeout rationale.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree with that. Moreover, to minimize the AN calls, I would instead fetch the transaction results of an entire block, e.g.:

txResults, err = k.client.GetTransactionResultsByBlockID(ctx, blockID)

We would likely have to track which block to fetch next, for inspecting the transaction results. And ideally, we need to give sufficient time, so that the block is sealed, not just finalized.

snaps could be turned to a map, and then we just iterate over the txResults:

type snapshot struct {
	from            gethCommon.Address
	lastSubmittedAt time.Time
}
var snaps [flow.Identifier]snapshot

for _, txResult := range txResults {
	s, ok := snaps[txResult.TransactionID]
	...
}

This way we fetch all the transaction results of a single block, which is 1 AN cal, instead of the potentially many GetTransactionResult calls that we would have to do by querying each EOA individually.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added a context.WithTimeout as a first step: 5f21e92 .

@zhangchiqing zhangchiqing 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.

looks good, other than what Janez's comments.

Base automatically changed from mpeter/poc-index-finalized-block-results to main July 30, 2026 16:02
@m-Peter m-Peter self-assigned this Jul 31, 2026

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/requester/tx_mempool.go (1)

930-954: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stamp lastSubmittedAt on successful background submissions.

collectPrefix stamps lastSubmittedAt before the submit work is detached, while reconcileSubmission only updates lastFlowTxID on success. The reconciliation snapshot uses that queue value, so an in-flight wrapper can appear stale if the network call takes longer than TxReconcileStaleAfter; stamp the queue again in the successful branch or pass the collection timestamp through as the submission timestamp.

🤖 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 `@services/requester/tx_mempool.go` around lines 930 - 954, Update
reconcileSubmission so the successful submission branch stamps q.lastSubmittedAt
in addition to q.lastFlowTxID, using the current submission time or the
collection timestamp propagated into the method. Keep the timestamp unchanged on
failed submissions and preserve the existing nonce rollback/marking behavior.
🤖 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.

Outside diff comments:
In `@services/requester/tx_mempool.go`:
- Around line 930-954: Update reconcileSubmission so the successful submission
branch stamps q.lastSubmittedAt in addition to q.lastFlowTxID, using the current
submission time or the collection timestamp propagated into the method. Keep the
timestamp unchanged on failed submissions and preserve the existing nonce
rollback/marking behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e38b236d-6e2b-4a49-ae16-dc2226b4ec1f

📥 Commits

Reviewing files that changed from the base of the PR and between 1276961 and 7b4496b.

📒 Files selected for processing (7)
  • README.md
  • cmd/run/cmd.go
  • config/config.go
  • metrics/collector.go
  • metrics/nop.go
  • services/requester/tx_mempool.go
  • services/requester/tx_mempool_test.go

@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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@services/requester/tx_mempool.go`:
- Around line 1262-1264: Update the reconciliation logic around
reconcileOnce/getTxResult so flow.TransactionStatusExpired is treated as a
terminal result alongside sealed transactions. Add an explicit expired branch
that immediately clears the submission markers and records the appropriate
expired reset reason, without waiting for TxReconcileStaleAfter; preserve
existing sealed and stale handling for other statuses.
- Line 1264: Update the stale condition in the transaction reconciliation logic
to use the equivalent simplified check !sealed || result.Error != nil,
preserving the existing time comparison and relying on result being non-nil
whenever sealed is true.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 993cf01f-f955-4135-a70d-bbe201a8b01f

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4496b and 5f21e92.

📒 Files selected for processing (2)
  • services/requester/tx_mempool.go
  • services/requester/tx_mempool_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/requester/tx_mempool_test.go

Comment thread services/requester/tx_mempool.go Outdated
Comment on lines +1262 to +1264
sealed := err == nil && result != nil && result.Status == flow.TransactionStatusSealed
reverted := sealed && result.Error != nil
stale := !(sealed && result.Error == nil) && now.Sub(s.lastSubmittedAt) > t.config.TxReconcileStaleAfter

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.

⚠️ Potential issue | 🟠 Major

Handle flow.TransactionStatusExpired as a terminal result.

Line 1262 recognizes only flow.TransactionStatusSealed. An expired wrapper therefore resets only after TxReconcileStaleAfter, although it cannot land later. Add an explicit expired branch, clear the markers immediately, and record the correct reset reason.

This repeats the previous review finding; the equality check fixes misclassification but not delayed recovery.

#!/bin/bash
set -euo pipefail

rg -n -C 6 \
  'TransactionStatusExpired|TransactionStatusSealed|reconcileOnce|getTxResult' \
  --glob '*.go' .
🧰 Tools
🪛 GitHub Check: Lint

[failure] 1264-1264:
QF1001: could apply De Morgan's law (staticcheck)

🤖 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 `@services/requester/tx_mempool.go` around lines 1262 - 1264, Update the
reconciliation logic around reconcileOnce/getTxResult so
flow.TransactionStatusExpired is treated as a terminal result alongside sealed
transactions. Add an explicit expired branch that immediately clears the
submission markers and records the appropriate expired reset reason, without
waiting for TxReconcileStaleAfter; preserve existing sealed and stale handling
for other statuses.

Comment thread services/requester/tx_mempool.go Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 👀 In Review

Development

Successfully merging this pull request may close these issues.

4 participants