Skip to content

fix: never resolve a fetch to state older than an observed found result - #1507

Merged
GabrielePicco merged 9 commits into
masterfrom
fix/consumed-subscription-update-discard
Jul 30, 2026
Merged

fix: never resolve a fetch to state older than an observed found result#1507
GabrielePicco merged 9 commits into
masterfrom
fix/consumed-subscription-update-discard

Conversation

@GabrielePicco

@GabrielePicco GabrielePicco commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Problem

When a subscription update arrives for an account with a fetch already in flight, the provider consumes the update to resolve that pending fetch instead of forwarding it to the fetch cloner. If the fetch then enters the slot-matching retry (e.g. its companion account resolved at an older slot), the RPC-only refetch could return a view older than the consumed update — a lagging RPC still showing the account as closed — and finalize NotFound.

The freshest state is silently dropped: nothing is forwarded, nothing is cloned, and since the bank now holds a non-delegated entry, every later ensure skips the remote fetch. An account that is delegated on chain can stay absent from the ER indefinitely, with no error past the initial ResolvedAccountCouldNoLongerBeFound.

This can be triggered by rapid re-delegation of the same PDA: the re-delegation notification races the fetch resolving the previous incarnation's update.

Solution

try_get_multi_until_slots_match now raises its effective min_context_slot floor to the highest slot of any found result it has observed — including results injected by subscription updates. A lagging RPC response can no longer satisfy the slot match below that floor, so the retry loop waits for the RPC view to catch up (within the existing retry budget) and resolves with the fresh state instead of regressing to an older one.

Tests

  • test_repro_redelegation_update_delivered_but_consumed_by_inflight_fetch reproduces the loss deterministically (blocked RPC + update injected through the provider loop) and enforces that the consumed state is honored — red before the fix, green after.
  • Additional repro/control tests pin the surrounding behavior: the same delivery is forwarded when no fetch is in flight, and a stale non-delegated bank entry suppresses refetches until an update is processed.
  • test_get_accounts_until_slots_match_refetches_mixed_sources_as_rpc_batch updated: it previously asserted the regression (accepting an older RPC batch for an account already observed at a newer slot).

Summary by CodeRabbit

  • Bug Fixes
    • Fixed edge cases where accounts could stay stuck in an undelegated/placeholder state after undelegation followed by re-delegation.
    • Ensured redelegation updates are preserved and honored correctly, including during concurrent fetch activity and retry failures.
    • Improved slot-matching behavior using a dynamically raised context floor based on observed results.
    • Added replay-based forwarding so consumed subscription results aren’t lost when RPC reconciliation fails.
  • Tests
    • Expanded coverage with new re-delegation wedge, replay, and race-condition scenarios.

…umed by an in-flight fetch

Reproduces the flash-trade wedge: a subscription update that resolves a
pending fetch for an older update of the same account is never forwarded
to the fetch cloner, and the resolving fetch discards it when its
companion mismatches slots and the RPC-only retry still sees the closed
window. The bank placeholder then suppresses every later refetch.

The consumed-update test asserts the enforced behavior (the consumed
state must be honored) and fails until the min-context floor fix lands.
A pending fetch resolved by a subscription update (e.g. a rapid
re-delegation racing the previous incarnation's fetch) could be finalized
as NotFound by the slot-matching RPC-only retry when the RPC view lagged
the consumed update: the freshest state was silently swallowed, leaving
the account permanently stuck behind its bank placeholder (flash-trade
wedge, Jul 29-30).

try_get_multi_until_slots_match now raises the effective min-context
floor to the highest slot of any found result it has observed, so a
lagging RPC response cannot finalize an older view; the retry loop waits
for the RPC to catch up instead.
@coderabbitai

coderabbitai Bot commented Jul 30, 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db337b48-e0bb-4aa4-90a8-f3dba8cd03ab

📥 Commits

Reviewing files that changed from the base of the PR and between e6c3bda and 6580e03.

📒 Files selected for processing (2)
  • magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs
  • magicblock-chainlink/src/remote_account_provider/mod.rs

📝 Walkthrough

Walkthrough

Adds fetch-cloner tests for missed redelegation updates, placeholder wedges, recovery paths, and in-flight races. Updates remote account retries to raise effective context slots from observed accounts and replay consumed subscription results when RPC resolution fails.

Suggested reviewers: thlorenz, bzawisto

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/consumed-subscription-update-discard

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12c31bba59

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs

@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 `@magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs`:
- Around line 10383-10411: Replace the fixed 150ms, 300ms, and 200ms sleeps in
the re-delegation race test with polling of observable state, using existing
helpers such as fetch_count(), bank contents, or the RPC-fetch counter. Ensure
each transition is explicitly awaited: the stale fetch is in flight, the
notification has been consumed, RPC fetches are unblocked and observed, and the
chain re-delegation is visible before continuing.
- Around line 10400-10404: Update the forward-channel assertions in the test
around the existing `try_recv()` checks, including the assertion near Line
10418, to match specifically on `TryRecvError::Empty` rather than accepting any
error. Ensure `TryRecvError::Disconnected` causes both assertions to fail while
preserving the intended empty-channel behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 75b17cc9-8e97-469c-8a85-8ae65148cd0b

📥 Commits

Reviewing files that changed from the base of the PR and between 26e5844 and 12c31bb.

📒 Files selected for processing (3)
  • magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs
  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/remote_account_provider/tests.rs

Comment thread magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs
Comment thread magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs
…h fails

A found result consumed from the update pipeline to resolve a pending
fetch was lost if the fetch subsequently failed, e.g. when the RPC view
lags the consumed slot beyond the slot-matching retry budget (~4s, while
pubsub commonly leads RPC by several seconds). The account state would
then only recover on the next on-chain write.

On failure the consumed results now re-enter the subscription update
pipeline; the retried resolution rides the lag-tolerant RPC fetch path
(RPC_FETCH_MAX_RETRIES) with the consumed slot as its min-context floor.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85d55dc886

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs Outdated
… unwatched drop

Re-forwarding as Account-sourced could be dropped by the unwatched-account
guard if the key's watch was released before the retry processed, for both
program- and account-sourced originals. A dedicated SubscriptionSource::Replay
variant is processed regardless of watch state and stays excluded from
program-only parking.

@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 `@magicblock-chainlink/src/remote_account_provider/mod.rs`:
- Around line 2428-2438: Ensure consumed subscription resolutions are recorded
during try_get_multi and replayed before every terminal error return, including
initial RPC fetch failures and classification errors propagated by ?. Update the
surrounding remote-account fetch flow, including consumed_subscription_results
and its replay calls, so no subscription-resolved account is lost when any
account fetch or classification fails.
- Around line 2616-2633: Update reforward_consumed_subscription_results so
replay updates are delivered through a bounded outbox/worker or equivalent
lossless, cancellation-aware mechanism instead of awaiting
subscription_forwarder.send directly. Ensure enqueueing does not block terminal
error handling indefinitely under downstream backpressure, while preserving
every consumed update and allowing cancellation to terminate delivery.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 68ae3b76-b47d-4bc8-bd99-7e5430d85ba9

📥 Commits

Reviewing files that changed from the base of the PR and between 12c31bb and e6c3bda.

📒 Files selected for processing (4)
  • magicblock-chainlink/src/chainlink/fetch_cloner/tests.rs
  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/remote_account_provider/pubsub_common.rs
  • magicblock-chainlink/src/remote_account_provider/tests.rs

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs
Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6c3bdae81

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f04ed45cbc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs Outdated
Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs
Replay sends now run in a detached task so a failing resolution inside a
listener-permit task cannot block on the update pipeline's own input
capacity. The classification error exit in the slot-matching retry loop
re-forwards consumed results like the other failure exits.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5ecfa0dbf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs
Replays try_send first; a full pipeline falls back to a detached sender
capped by a semaphore (256), beyond which replays are dropped with a
warning instead of accumulating unbounded waiters.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf3f57a8d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs Outdated
Replays are per-account latest-state snapshots, not an event queue: a
Pubkey -> latest-update outbox (newest slot wins) drained by one detached
worker is simultaneously lossless, bounded by the number of affected
accounts, and never blocks a failing resolution. Mirrors the submux
debounce pattern of forwarding only the freshest pending payload.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6580e03baa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread magicblock-chainlink/src/remote_account_provider/mod.rs
@GabrielePicco
GabrielePicco merged commit b8c1ddd into master Jul 30, 2026
38 checks passed
@GabrielePicco
GabrielePicco deleted the fix/consumed-subscription-update-discard branch July 30, 2026 10:59
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