Skip to content

Conversation

@GabrielePicco
Copy link
Collaborator

@GabrielePicco GabrielePicco commented Jan 5, 2026

Summary

  • Subscribe to accounts in parallel, avoiding several round-trips in awaiting sequentially subscriptions confirmations

Compatibility

  • No breaking changes
  • Config change (describe):
  • Migration needed (describe):

Testing

  • tests (or explain)

Checklist

  • docs updated (if needed)
  • closes #

Summary by CodeRabbit

  • Bug Fixes & Performance

    • Subscriptions are now initiated concurrently and batched, improving responsiveness and throughput when monitoring many accounts.
    • Subscription errors are aggregated and logged after parallel attempts for clearer diagnostics and consistent failure handling.
  • Refactor

    • Update handling reworked to process updates non‑blockingly via isolated async tasks, draining in‑flight work before continuing to improve stability and maintainability.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

The PR makes subscription and update-handling more concurrent. ATA projection collects ATA/eATA pubkeys during iteration and issues all subscription requests in parallel with join_all, then logs per-pubkey subscription errors. The remote_account_provider setup_subscriptions similarly dispatches concurrent subscribe calls and aggregates failures. The subscription listener (fetch_cloner) was refactored to use a JoinSet, drain in-flight tasks non-blockingly, and extract per-update logic into a new process_subscription_update helper; a new public helper fetch_and_clone_accounts_with_dedup was added.

Suggested reviewers

  • thlorenz
  • bmuddha
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/refactor-cloning-pipeline-v3

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.

@GabrielePicco GabrielePicco mentioned this pull request Jan 5, 2026
6 tasks
Copy link
Collaborator Author

GabrielePicco commented Jan 5, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@GabrielePicco GabrielePicco mentioned this pull request Jan 5, 2026
6 tasks
@GabrielePicco GabrielePicco marked this pull request as ready for review January 5, 2026 20:32
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @magicblock-chainlink/src/remote_account_provider/mod.rs:
- Around line 810-817: The use of try_join_all over
subscribe_and_fetch.iter().map(|(pubkey, _)| self.subscribe(pubkey)) must be
replaced with non-fail-fast logic: call join_all (or otherwise spawn all
subscribe futures) so every subscribe attempt is executed, collect
per-subscription results, log individual errors instead of returning early from
setup_subscriptions/self.subscribe, and ensure failed subscriptions clean up any
entries in fetching_accounts and the pubsub_client/LRU cache or schedule a
cleanup task so senders are not leaked; update the code paths around
try_get_multi and fetch to only proceed after all subscribe attempts are
resolved (success or logged failure) so receivers aren’t left waiting and ensure
any in-flight subscribe futures are observed for completion rather than relying
on being dropped.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de09c36 and 7281fe9.

📒 Files selected for processing (2)
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/remote_account_provider/mod.rs
🧰 Additional context used
📓 Path-based instructions (1)
{magicblock-*,programs,storage-proto}/**

⚙️ CodeRabbit configuration file

{magicblock-*,programs,storage-proto}/**: Treat any usage of .unwrap() or .expect() in production Rust code as a MAJOR issue.
These should not be categorized as trivial or nit-level concerns.
Request proper error handling or explicit justification with invariants.

Files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).
📚 Learning: 2025-12-03T09:36:01.527Z
Learnt from: Dodecahedr0x
Repo: magicblock-labs/magicblock-validator PR: 639
File: magicblock-chainlink/src/remote_account_provider/mod.rs:1350-1353
Timestamp: 2025-12-03T09:36:01.527Z
Learning: Repo: magicblock-labs/magicblock-validator
File: magicblock-chainlink/src/remote_account_provider/mod.rs
Context: consolidate_fetched_remote_accounts
Learning: For unexpected result counts (>2), the project prefers logging an error and returning an empty Vec over panicking; acceptable during development per maintainer (Dodecahedr0x).

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
📚 Learning: 2025-11-07T14:20:31.457Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 621
File: magicblock-chainlink/src/remote_account_provider/chain_pubsub_actor.rs:457-495
Timestamp: 2025-11-07T14:20:31.457Z
Learning: In magicblock-chainlink/src/remote_account_provider/chain_pubsub_client.rs, the unsubscribe closure returned by PubSubConnection::account_subscribe(...) resolves to () (unit), not a Result. Downstream code should not attempt to inspect an unsubscribe result and can optionally wrap it in a timeout to guard against hangs.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
📚 Learning: 2025-12-01T16:02:05.367Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 703
File: magicblock-chainlink/src/submux/mod.rs:652-654
Timestamp: 2025-12-01T16:02:05.367Z
Learning: In magicblock-chainlink/src/submux/mod.rs, the subscribe_program method intentionally adds program_id to program_subs before attempting the subscription. This ensures that even if the initial subscription fails or only partially succeeds across clients, the reconnection logic will retry the subscription. This is a deliberate design pattern for resilience in the multi-client architecture and should not be "fixed" to remove entries on failure.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
📚 Learning: 2025-11-19T09:34:37.917Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 621
File: test-integration/test-chainlink/tests/ix_remote_account_provider.rs:62-63
Timestamp: 2025-11-19T09:34:37.917Z
Learning: In test-integration/test-chainlink/tests/ix_remote_account_provider.rs and similar test files, the `_fwd_rx` receiver returned by `init_remote_account_provider()` is intentionally kept alive (but unused) to prevent "receiver dropped" errors on the sender side. The pattern `let (remote_account_provider, _fwd_rx) = init_remote_account_provider().await;` should NOT be changed to `let (remote_account_provider, _) = ...` because dropping the receiver would cause send() operations to fail.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
📚 Learning: 2025-12-17T12:46:36.207Z
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
📚 Learning: 2025-10-26T16:53:29.820Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 587
File: magicblock-chainlink/src/remote_account_provider/mod.rs:134-0
Timestamp: 2025-10-26T16:53:29.820Z
Learning: In magicblock-chainlink/src/remote_account_provider/mod.rs, the `Endpoint::separate_pubsub_url_and_api_key()` method uses `split_once("?api-key=")` because the api-key parameter is always the only query parameter right after `?`. No additional query parameter parsing is needed for this use case.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
📚 Learning: 2025-10-21T14:00:54.642Z
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
📚 Learning: 2025-12-03T09:33:48.707Z
Learnt from: Dodecahedr0x
Repo: magicblock-labs/magicblock-validator PR: 639
File: test-integration/test-committor-service/tests/test_ix_commit_local.rs:867-881
Timestamp: 2025-12-03T09:33:48.707Z
Learning: Repo: magicblock-labs/magicblock-validator PR: 639
Context: test-integration/test-committor-service/tests/test_ix_commit_local.rs (ix_commit_local)
Learning: The PhotonIndexer used for compressed account fetches (get_compressed_account) has built‑in retry logic (defaults to ~10 attempts), so tests should not add separate retry loops around compressed fetches unless there’s a specific need.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
🧬 Code graph analysis (1)
magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs (1)
magicblock-core/src/token_programs.rs (1)
  • try_derive_eata_address_and_bump (81-89)
🔇 Additional comments (3)
magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs (3)

46-72: LGTM: Clean separation of subscription batching from fetch logic.

The refactoring to collect pubkeys first and defer subscriptions is well-structured:

  • Maintains existing fetch task spawning via JoinSet
  • Enables parallel subscription execution (lines 75-80)
  • Preserves the logic for deriving eATA addresses and determining effective slots

75-80: LGTM: join_all provides better error handling than try_join_all.

Using join_all here is the right choice because:

  • It waits for all subscription futures to complete, rather than failing fast on the first error
  • Allows individual error logging without aborting the entire operation
  • Consistent with the learning that eATA subscriptions should be kept active to watch for future creation/delegation (per maintainer GabrielePicco)
  • Prevents the partial subscription registration issues that try_join_all can cause (as seen in the other file under review)

Based on learnings, eATA subscriptions are intentionally kept active even when accounts don't exist.


83-89: LGTM: Appropriate error logging without blocking the cloning pipeline.

The error handling approach correctly:

  • Logs individual subscription failures for debugging and monitoring
  • Continues processing even if some subscriptions fail
  • Allows the fetch and delegation projection logic to proceed regardless of subscription errors

This resilient approach ensures that temporary subscription failures don't block the account cloning pipeline.

Copy link
Collaborator

@bmuddha bmuddha left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49703d2 and 253e5f7.

📒 Files selected for processing (1)
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
🧰 Additional context used
📓 Path-based instructions (1)
{magicblock-*,programs,storage-proto}/**

⚙️ CodeRabbit configuration file

{magicblock-*,programs,storage-proto}/**: Treat any usage of .unwrap() or .expect() in production Rust code as a MAJOR issue.
These should not be categorized as trivial or nit-level concerns.
Request proper error handling or explicit justification with invariants.

Files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).
📚 Learning: 2025-12-17T12:46:36.207Z
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).

Applied to files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-12-01T16:02:05.367Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 703
File: magicblock-chainlink/src/submux/mod.rs:652-654
Timestamp: 2025-12-01T16:02:05.367Z
Learning: In magicblock-chainlink/src/submux/mod.rs, the subscribe_program method intentionally adds program_id to program_subs before attempting the subscription. This ensures that even if the initial subscription fails or only partially succeeds across clients, the reconnection logic will retry the subscription. This is a deliberate design pattern for resilience in the multi-client architecture and should not be "fixed" to remove entries on failure.

Applied to files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-11-07T14:20:31.457Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 621
File: magicblock-chainlink/src/remote_account_provider/chain_pubsub_actor.rs:457-495
Timestamp: 2025-11-07T14:20:31.457Z
Learning: In magicblock-chainlink/src/remote_account_provider/chain_pubsub_client.rs, the unsubscribe closure returned by PubSubConnection::account_subscribe(...) resolves to () (unit), not a Result. Downstream code should not attempt to inspect an unsubscribe result and can optionally wrap it in a timeout to guard against hangs.

Applied to files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-12-03T09:36:01.527Z
Learnt from: Dodecahedr0x
Repo: magicblock-labs/magicblock-validator PR: 639
File: magicblock-chainlink/src/remote_account_provider/mod.rs:1350-1353
Timestamp: 2025-12-03T09:36:01.527Z
Learning: Repo: magicblock-labs/magicblock-validator
File: magicblock-chainlink/src/remote_account_provider/mod.rs
Context: consolidate_fetched_remote_accounts
Learning: For unexpected result counts (>2), the project prefers logging an error and returning an empty Vec over panicking; acceptable during development per maintainer (Dodecahedr0x).

Applied to files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-10-21T14:00:54.642Z
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579

Applied to files:

  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
🧬 Code graph analysis (1)
magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs (1)
magicblock-chainlink/src/chainlink/account_still_undelegating_on_chain.rs (1)
  • account_still_undelegating_on_chain (22-89)
🔇 Additional comments (1)
magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs (1)

227-336: LGTM!

The process_subscription_update helper cleanly encapsulates the per-update processing logic. The implementation correctly:

  • Handles out-of-order updates by comparing remote slots
  • Checks undelegation state using account_still_undelegating_on_chain
  • Unsubscribes from delegated accounts to avoid redundant updates
  • Logs errors without propagating them (appropriate for background tasks)

The decoupling of update processing into spawned tasks improves parallelism while maintaining correctness.

@GabrielePicco GabrielePicco force-pushed the chore/refactor-cloning-pipeline-v3 branch from 253e5f7 to 38259f9 Compare January 6, 2026 21:44
@GabrielePicco GabrielePicco force-pushed the chore/refactor-cloning-pipeline-v2 branch from de09c36 to 381c479 Compare January 7, 2026 09:48
Base automatically changed from chore/refactor-cloning-pipeline-v2 to master January 7, 2026 10:44
@GabrielePicco GabrielePicco force-pushed the chore/refactor-cloning-pipeline-v3 branch from 38259f9 to f043ad0 Compare January 7, 2026 12:18
@github-actions
Copy link

github-actions bot commented Jan 7, 2026

Manual Deploy Available

You can trigger a manual deploy of this PR branch to testnet:

Deploy to Testnet 🚀

Alternative: Comment /deploy on this PR to trigger deployment directly.

⚠️ Note: Manual deploy requires authorization. Only authorized users can trigger deployments.

Comment updated automatically when the PR is synchronized.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 253e5f7 and f043ad0.

⛔ Files ignored due to path filters (1)
  • test-integration/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
  • magicblock-chainlink/src/remote_account_provider/mod.rs
🧰 Additional context used
📓 Path-based instructions (1)
{magicblock-*,programs,storage-proto}/**

⚙️ CodeRabbit configuration file

{magicblock-*,programs,storage-proto}/**: Treat any usage of .unwrap() or .expect() in production Rust code as a MAJOR issue.
These should not be categorized as trivial or nit-level concerns.
Request proper error handling or explicit justification with invariants.

Files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).
📚 Learning: 2025-12-03T09:36:01.527Z
Learnt from: Dodecahedr0x
Repo: magicblock-labs/magicblock-validator PR: 639
File: magicblock-chainlink/src/remote_account_provider/mod.rs:1350-1353
Timestamp: 2025-12-03T09:36:01.527Z
Learning: Repo: magicblock-labs/magicblock-validator
File: magicblock-chainlink/src/remote_account_provider/mod.rs
Context: consolidate_fetched_remote_accounts
Learning: For unexpected result counts (>2), the project prefers logging an error and returning an empty Vec over panicking; acceptable during development per maintainer (Dodecahedr0x).

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-11-07T14:20:31.457Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 621
File: magicblock-chainlink/src/remote_account_provider/chain_pubsub_actor.rs:457-495
Timestamp: 2025-11-07T14:20:31.457Z
Learning: In magicblock-chainlink/src/remote_account_provider/chain_pubsub_client.rs, the unsubscribe closure returned by PubSubConnection::account_subscribe(...) resolves to () (unit), not a Result. Downstream code should not attempt to inspect an unsubscribe result and can optionally wrap it in a timeout to guard against hangs.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-12-01T16:02:05.367Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 703
File: magicblock-chainlink/src/submux/mod.rs:652-654
Timestamp: 2025-12-01T16:02:05.367Z
Learning: In magicblock-chainlink/src/submux/mod.rs, the subscribe_program method intentionally adds program_id to program_subs before attempting the subscription. This ensures that even if the initial subscription fails or only partially succeeds across clients, the reconnection logic will retry the subscription. This is a deliberate design pattern for resilience in the multi-client architecture and should not be "fixed" to remove entries on failure.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-11-19T09:34:37.917Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 621
File: test-integration/test-chainlink/tests/ix_remote_account_provider.rs:62-63
Timestamp: 2025-11-19T09:34:37.917Z
Learning: In test-integration/test-chainlink/tests/ix_remote_account_provider.rs and similar test files, the `_fwd_rx` receiver returned by `init_remote_account_provider()` is intentionally kept alive (but unused) to prevent "receiver dropped" errors on the sender side. The pattern `let (remote_account_provider, _fwd_rx) = init_remote_account_provider().await;` should NOT be changed to `let (remote_account_provider, _) = ...` because dropping the receiver would cause send() operations to fail.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-12-17T12:46:36.207Z
Learnt from: GabrielePicco
Repo: magicblock-labs/magicblock-validator PR: 738
File: magicblock-chainlink/src/chainlink/fetch_cloner.rs:1109-1196
Timestamp: 2025-12-17T12:46:36.207Z
Learning: In magicblock-chainlink/src/chainlink/fetch_cloner.rs, eATA account subscriptions are intentionally NOT cancelled even when the eATA doesn't exist or delegation conversion fails. The subscriptions are kept active to watch for future creation or delegation of eATA accounts (per maintainer GabrielePicco).

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-10-26T16:53:29.820Z
Learnt from: thlorenz
Repo: magicblock-labs/magicblock-validator PR: 587
File: magicblock-chainlink/src/remote_account_provider/mod.rs:134-0
Timestamp: 2025-10-26T16:53:29.820Z
Learning: In magicblock-chainlink/src/remote_account_provider/mod.rs, the `Endpoint::separate_pubsub_url_and_api_key()` method uses `split_once("?api-key=")` because the api-key parameter is always the only query parameter right after `?`. No additional query parameter parsing is needed for this use case.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
📚 Learning: 2025-10-21T14:00:54.642Z
Learnt from: bmuddha
Repo: magicblock-labs/magicblock-validator PR: 578
File: magicblock-aperture/src/requests/websocket/account_subscribe.rs:18-27
Timestamp: 2025-10-21T14:00:54.642Z
Learning: In magicblock-aperture account_subscribe handler (src/requests/websocket/account_subscribe.rs), the RpcAccountInfoConfig fields data_slice, commitment, and min_context_slot are currently ignored—only encoding is applied. This is tracked as technical debt in issue #579: https://github.com/magicblock-labs/magicblock-validator/issues/579

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs
📚 Learning: 2025-12-03T09:33:48.707Z
Learnt from: Dodecahedr0x
Repo: magicblock-labs/magicblock-validator PR: 639
File: test-integration/test-committor-service/tests/test_ix_commit_local.rs:867-881
Timestamp: 2025-12-03T09:33:48.707Z
Learning: Repo: magicblock-labs/magicblock-validator PR: 639
Context: test-integration/test-committor-service/tests/test_ix_commit_local.rs (ix_commit_local)
Learning: The PhotonIndexer used for compressed account fetches (get_compressed_account) has built‑in retry logic (defaults to ~10 attempts), so tests should not add separate retry loops around compressed fetches unless there’s a specific need.

Applied to files:

  • magicblock-chainlink/src/remote_account_provider/mod.rs
  • magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs
🧬 Code graph analysis (1)
magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs (1)
magicblock-core/src/token_programs.rs (1)
  • try_derive_eata_address_and_bump (81-89)
🔇 Additional comments (3)
magicblock-chainlink/src/chainlink/fetch_cloner/ata_projection.rs (1)

74-89: LGTM! Correct parallel subscription pattern.

The implementation properly:

  • Attempts all subscriptions concurrently using join_all (line 75)
  • Logs individual failures without stopping the operation (lines 82-89)
  • Continues execution even if some subscriptions fail

This pattern maintains operational resilience by treating subscription errors as non-fatal optimization failures while ensuring the core fetch/clone operations proceed.

magicblock-chainlink/src/chainlink/fetch_cloner/mod.rs (2)

157-222: Well-structured concurrency refactor.

The refactoring to use JoinSet for processing subscription updates concurrently (with non-blocking draining) is a solid improvement that:

  • Enables parallel processing of updates without blocking on delegation record fetches
  • Properly handles channel closure scenarios
  • Extracts update logic into a dedicated function for better maintainability

788-947: LGTM! Proper deduplication implementation.

The fetch_and_clone_accounts_with_dedup method correctly implements request deduplication:

  • Synchronous bank checks avoid unnecessary fetches
  • Proper handling of pending requests using oneshot channels
  • Race-condition-safe entry management in pending_requests
  • Cleanup of pending entries after fetch completion

Comment on lines +190 to +194
let Some(update) = maybe_update else {
// Channel closed, drain remaining tasks and exit
while pending_tasks.join_next().await.is_some() {}
break;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Inconsistent panic handling in channel-close drain loops.

The drain loops at lines 192 and 216 silently discard task results (including panics), while the drain loops at lines 178-184 and 206-208 properly log panicked tasks. This inconsistency could hide critical issues when the subscription channel closes.

🔎 Proposed fix to log panics during drain
                             let Some(update) = maybe_update else {
                                 // Channel closed, drain remaining tasks and exit
-                                while pending_tasks.join_next().await.is_some() {}
+                                while let Some(result) = pending_tasks.join_next().await {
+                                    if let Err(e) = result {
+                                        error!("Subscription update task panicked during drain: {e:?}");
+                                    }
+                                }
                                 break;
                             };
                     Err(
                         tokio::sync::mpsc::error::TryRecvError::Disconnected,
                     ) => {
                         // Channel closed, drain remaining tasks and exit
-                        while pending_tasks.join_next().await.is_some() {}
+                        while let Some(result) = pending_tasks.join_next().await {
+                            if let Err(e) = result {
+                                error!("Subscription update task panicked during drain: {e:?}");
+                            }
+                        }
                         break;
                     }

Also applies to: 215-218

Comment on lines +832 to 843
// Fail if ANY subscription failed
if !errors.is_empty() {
return Err(
RemoteAccountProviderError::AccountSubscriptionsTaskFailed(
format!(
"{} subscription(s) failed: [{}]",
errors.len(),
errors.join(", ")
),
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Returning error on subscription failure causes memory leak in fetching_accounts.

While switching to join_all (line 814) correctly ensures all subscriptions are attempted, the code still returns an error if ANY subscription fails (lines 832-843). This creates a critical resource leak:

  1. Oneshot channels created (lines 716-726): For each pubkey, a sender/receiver pair is created and the sender is stored in fetching_accounts.
  2. Early return on error (line 730 await?): If setup_subscriptions returns an error, the function exits immediately.
  3. Fetch never called (lines 734-740): The fetch task that would send results through the channels is never spawned.
  4. Memory leak: The senders remain in fetching_accounts indefinitely. The receivers are dropped, but nothing cleans up the senders.

The correct pattern (as shown in ata_projection.rs lines 82-89) is to log subscription errors but continue to fetch rather than returning an error. Subscriptions are an optimization for receiving updates; their failure should not prevent the initial fetch.

Based on learnings, the project maintains subscription resilience where partial failures are tolerated and reconnection logic retries subscriptions. Failing the entire fetch operation over subscription errors contradicts this pattern.

🔧 Recommended fix: Log errors but allow fetch to proceed
     // Collect errors and log each individual failure
-    let mut errors = Vec::new();
     for (result, (pubkey, _)) in
         subscription_results.iter().zip(subscribe_and_fetch.iter())
     {
         if let Err(err) = result {
             error!("Failed to subscribe to account {}: {}", pubkey, err);
-            errors.push(format!("{}: {}", pubkey, err));
         }
     }
 
-    // Fail if ANY subscription failed
-    if !errors.is_empty() {
-        return Err(
-            RemoteAccountProviderError::AccountSubscriptionsTaskFailed(
-                format!(
-                    "{} subscription(s) failed: [{}]",
-                    errors.len(),
-                    errors.join(", ")
-                ),
-            ),
-        );
-    }
-
     Ok(())

Committable suggestion skipped: line range outside the PR's diff.

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.

4 participants