Skip to content

refactor: decouple preparation/cleanup tasks from BaseTasks#1382

Open
taco-paco wants to merge 2 commits into
masterfrom
refactor/committor/committor-program-removal
Open

refactor: decouple preparation/cleanup tasks from BaseTasks#1382
taco-paco wants to merge 2 commits into
masterfrom
refactor/committor/committor-program-removal

Conversation

@taco-paco

@taco-paco taco-paco commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Breaking Changes

  • None
  • Yes — migration path described below

Test Plan

Summary by CodeRabbit

  • New Features

    • Buffer-based commits now use a simpler ready/not-ready flag and a new preparation/cleanup flow for buffer handling.
    • Cleanup tasks can now be derived directly for commit processing and transaction preparation.
  • Bug Fixes

    • Improved buffer retry handling when a buffer account is already initialized.
    • Reduced unnecessary buffer-stage rebuilding when switching commit delivery modes.
  • Tests

    • Updated integration coverage to match the new buffer preparation and cleanup behavior.

@taco-paco taco-paco requested a review from snawaz July 2, 2026 08:52
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR replaces the CommitBufferStage enum-based buffer preparation model with a simpler prepared: bool flag on CommitDelivery::StateInBuffer/DiffInBuffer variants. A new commit_stage_task module introduces standalone PreparationTask and CleanupTask types constructed from CommitTask/CommitFinalizeTask, handling buffer initialization, chunk writing, and cleanup instruction building. DeliveryPreparator and TransactionPreparatorImpl are updated to use these new types instead of stage-matching logic. Corresponding integration tests are updated to construct buffer deliveries with the new prepared flag and derive cleanup tasks via CleanupTask::from_commit/from_commit_finalize.

Changes

Area Change
commit_task.rs, commit_finalize_task.rs Removed CommitBufferStage; StateInBuffer/DiffInBuffer now use prepared: bool; removed stage()/stage_mut(); added/updated is_buffer(), reset_commit_id, try_optimize_tx_size
commit_stage_task.rs (new) Adds PreparationTask and CleanupTask with instruction-building, compute-unit estimation, PDA derivation, and lifecycle methods
tasks/mod.rs Exposes commit_stage_task module publicly; removes old in-module PreparationTask/CleanupTask; updates tests
delivery_preparator.rs, transaction_preparator/mod.rs Reworks buffer preparation/cleanup flows to use PreparationTask/CleanupTask directly
Integration tests Updated to use prepared: false and CleanupTask::from_commit/from_commit_finalize

Sequence Diagram(s)

sequenceDiagram
  participant DeliveryPreparator
  participant PreparationTask
  participant CleanupTask
  DeliveryPreparator->>PreparationTask: from_commit / from_commit_finalize
  DeliveryPreparator->>PreparationTask: initialize_buffer_account
  DeliveryPreparator->>PreparationTask: write_buffer_with_retries
  DeliveryPreparator->>PreparationTask: done()
  DeliveryPreparator->>CleanupTask: cleanup on AccountAlreadyInitializedError
Loading

Suggested reviewers: snawaz, GabrielePicco, bmuddha

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/committor/committor-program-removal

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.

@taco-paco taco-paco marked this pull request as ready for review July 2, 2026 09:02

@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-committor-service/src/tasks/commit_stage_task.rs`:
- Around line 34-116: The constructors in PreparationTask::from_commit and
PreparationTask::from_commit_finalize are duplicating the same matching and
buffer/chunk setup logic, with only the task wrapper and delivery field name
differing. Refactor this shared behavior into a small trait over both CommitTask
and CommitFinalizeTask that exposes commit_id, pubkey, committed_account data,
and mutable delivery access, then have both constructors delegate to one
implementation. Apply the same pattern to CleanupTask::from_commit and
CleanupTask::from_commit_finalize so the commit/finalize paths stay consistent
and easier to maintain.

In
`@magicblock-committor-service/src/transaction_preparator/delivery_preparator.rs`:
- Around line 177-196: The retry cleanup path in
delivery_preparator::prepare_task is rebuilding a full PreparationTask just to
obtain a CleanupTask, which repeats the BaseTaskImpl matching and can trigger
expensive buffer cloning/compute_diff work. Change this branch to derive
CleanupTask directly from the existing task variants used in prepare_task, using
only the commit_id and pubkey needed for cleanup, and remove the intermediate
PreparationTask::from_commit/from_commit_finalize construction before calling
cleanup_buffers.
🪄 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

Run ID: 53bc9cb4-b5d2-4dfa-a247-906785281c37

📥 Commits

Reviewing files that changed from the base of the PR and between 32b1c4c and e304b5a.

📒 Files selected for processing (9)
  • magicblock-committor-service/src/tasks/commit_finalize_task.rs
  • magicblock-committor-service/src/tasks/commit_stage_task.rs
  • magicblock-committor-service/src/tasks/commit_task.rs
  • magicblock-committor-service/src/tasks/mod.rs
  • magicblock-committor-service/src/transaction_preparator/delivery_preparator.rs
  • magicblock-committor-service/src/transaction_preparator/mod.rs
  • test-integration/test-committor-service/tests/common.rs
  • test-integration/test-committor-service/tests/test_delivery_preparator.rs
  • test-integration/test-committor-service/tests/test_transaction_preparator.rs

Comment on lines +34 to +116
pub fn from_commit(task: &'a mut CommitTask) -> Option<Self> {
match &mut task.delivery_details {
CommitDelivery::StateInArgs | CommitDelivery::DiffInArgs { .. } => {
None
}
CommitDelivery::StateInBuffer { prepared } => {
let buffer_data = task.committed_account.account.data.clone();
let chunks = Chunks::from_data_length(
buffer_data.len(),
MAX_WRITE_CHUNK_SIZE,
);
Some(Self {
commit_id: task.commit_id,
pubkey: task.committed_account.pubkey,
buffer_data,
chunks,
prepared,
})
}
CommitDelivery::DiffInBuffer {
base_account,
prepared,
} => {
let diff = compute_diff(
base_account.data.as_ref(),
&task.committed_account.account.data,
)
.to_vec();
let chunks =
Chunks::from_data_length(diff.len(), MAX_WRITE_CHUNK_SIZE);
Some(Self {
commit_id: task.commit_id,
pubkey: task.committed_account.pubkey,
buffer_data: diff,
chunks,
prepared,
})
}
}
}

pub fn from_commit_finalize(
task: &'a mut CommitFinalizeTask,
) -> Option<Self> {
match &mut task.delivery {
CommitDelivery::StateInArgs | CommitDelivery::DiffInArgs { .. } => {
None
}
CommitDelivery::StateInBuffer { prepared } => {
let buffer_data = task.committed_account.account.data.clone();
let chunks = Chunks::from_data_length(
buffer_data.len(),
MAX_WRITE_CHUNK_SIZE,
);
Some(Self {
commit_id: task.commit_id,
pubkey: task.committed_account.pubkey,
buffer_data,
chunks,
prepared,
})
}
CommitDelivery::DiffInBuffer {
base_account,
prepared,
} => {
let diff = compute_diff(
base_account.data.as_ref(),
&task.committed_account.account.data,
)
.to_vec();
let chunks =
Chunks::from_data_length(diff.len(), MAX_WRITE_CHUNK_SIZE);
Some(Self {
commit_id: task.commit_id,
pubkey: task.committed_account.pubkey,
buffer_data: diff,
chunks,
prepared,
})
}
}
}

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate logic between CommitTask and CommitFinalizeTask handlers.

PreparationTask::from_commit/from_commit_finalize (Lines 34-116) are near-identical (only differing in the task type and field name delivery_details vs delivery), and the same duplication repeats for CleanupTask::from_commit/from_commit_finalize (Lines 233-258). Consider a small trait abstracting commit_id(), pubkey(), committed_data(), and delivery_mut()/delivery() over both task types so both constructors can share one implementation.

♻️ Sketch of a shared-trait refactor
+trait DeliveryHolder {
+    fn commit_id(&self) -> u64;
+    fn pubkey(&self) -> Pubkey;
+    fn committed_data(&self) -> &[u8];
+    fn delivery_mut(&mut self) -> &mut CommitDelivery;
+    fn delivery(&self) -> &CommitDelivery;
+}
+
+impl DeliveryHolder for CommitTask { /* map to delivery_details */ }
+impl DeliveryHolder for CommitFinalizeTask { /* map to delivery */ }
+
 impl<'a> PreparationTask<'a> {
-    pub fn from_commit(task: &'a mut CommitTask) -> Option<Self> { ... }
-    pub fn from_commit_finalize(task: &'a mut CommitFinalizeTask) -> Option<Self> { ... }
+    pub fn from_task<T: DeliveryHolder>(task: &'a mut T) -> Option<Self> {
+        let commit_id = task.commit_id();
+        let pubkey = task.pubkey();
+        let data = task.committed_data().to_vec();
+        match task.delivery_mut() {
+            CommitDelivery::StateInArgs | CommitDelivery::DiffInArgs { .. } => None,
+            CommitDelivery::StateInBuffer { prepared } => Some(Self { commit_id, pubkey, chunks: Chunks::from_data_length(data.len(), MAX_WRITE_CHUNK_SIZE), buffer_data: data, prepared }),
+            CommitDelivery::DiffInBuffer { base_account, prepared } => { /* compute diff against base_account.data */ }
+        }
+    }
 }

Same pattern applies to CleanupTask::from_commit/from_commit_finalize.

Note this touches code that may be further relocated to magic-program per the PR's stated plan; weigh against near-term rework.

Also applies to: 233-258

🤖 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 `@magicblock-committor-service/src/tasks/commit_stage_task.rs` around lines 34
- 116, The constructors in PreparationTask::from_commit and
PreparationTask::from_commit_finalize are duplicating the same matching and
buffer/chunk setup logic, with only the task wrapper and delivery field name
differing. Refactor this shared behavior into a small trait over both CommitTask
and CommitFinalizeTask that exposes commit_id, pubkey, committed_account data,
and mutable delivery access, then have both constructors delegate to one
implementation. Apply the same pattern to CleanupTask::from_commit and
CleanupTask::from_commit_finalize so the commit/finalize paths stay consistent
and easier to maintain.

Comment on lines +177 to +196
// Preparation failed due to buffer existing - cleanup and retry
let preparation_task = match task {
BaseTaskImpl::Commit(commit_task) => {
PreparationTask::from_commit(commit_task)
}
BaseTaskImpl::CommitFinalize(commit_finalize_task) => {
commit_finalize_task.stage_mut()
PreparationTask::from_commit_finalize(commit_finalize_task)
}
_ => None,
};
let Some(stage) = stage else {
let Some(preparation_task) = preparation_task else {
return Ok(());
};
let CommitBufferStage::Preparation(preparation_task) = stage else {
return Ok(());
};
let preparation_task = preparation_task.clone();
let cleanup_task = preparation_task.cleanup_task();

// Cleanup
let cleanup_task = preparation_task.cleanup_task();
self.cleanup_buffers(authority, &[cleanup_task]).await?;
self.rpc_client.invalidate_cached_blockhash().await;

// Restore preparation stage for retry
*stage = CommitBufferStage::Preparation(preparation_task);

// Retry preparation

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.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid rebuilding a full PreparationTask just to get a CleanupTask.

This block duplicates the BaseTaskImpl → task-extraction match from prepare_task (lines 104-113), and worse, PreparationTask::from_commit/from_commit_finalize eagerly clone buffer_data and (for DiffInBuffer) run compute_diff over the full account data — all just to discard everything except commit_id/pubkey via cleanup_task(). For large committed accounts (tests use up to 500KB) this is wasted CPU/memory on every AccountAlreadyInitializedError retry.

Since CleanupTask only needs commit_id and pubkey, construct it directly from the task without going through PreparationTask:

♻️ Proposed fix
-        // Preparation failed due to buffer existing - cleanup and retry
-        let preparation_task = match task {
-            BaseTaskImpl::Commit(commit_task) => {
-                PreparationTask::from_commit(commit_task)
-            }
-            BaseTaskImpl::CommitFinalize(commit_finalize_task) => {
-                PreparationTask::from_commit_finalize(commit_finalize_task)
-            }
-            _ => None,
-        };
-        let Some(preparation_task) = preparation_task else {
-            return Ok(());
-        };
-
-        // Cleanup
-        let cleanup_task = preparation_task.cleanup_task();
+        // Preparation failed due to buffer existing - cleanup and retry
+        let cleanup_task = match task {
+            BaseTaskImpl::Commit(commit_task) => CleanupTask {
+                commit_id: commit_task.commit_id,
+                pubkey: commit_task.committed_account.pubkey,
+            },
+            BaseTaskImpl::CommitFinalize(commit_finalize_task) => CleanupTask {
+                commit_id: commit_finalize_task.commit_id,
+                pubkey: commit_finalize_task.committed_account.pubkey,
+            },
+            _ => return Ok(()),
+        };
         self.cleanup_buffers(authority, &[cleanup_task]).await?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Preparation failed due to buffer existing - cleanup and retry
let preparation_task = match task {
BaseTaskImpl::Commit(commit_task) => {
PreparationTask::from_commit(commit_task)
}
BaseTaskImpl::CommitFinalize(commit_finalize_task) => {
commit_finalize_task.stage_mut()
PreparationTask::from_commit_finalize(commit_finalize_task)
}
_ => None,
};
let Some(stage) = stage else {
let Some(preparation_task) = preparation_task else {
return Ok(());
};
let CommitBufferStage::Preparation(preparation_task) = stage else {
return Ok(());
};
let preparation_task = preparation_task.clone();
let cleanup_task = preparation_task.cleanup_task();
// Cleanup
let cleanup_task = preparation_task.cleanup_task();
self.cleanup_buffers(authority, &[cleanup_task]).await?;
self.rpc_client.invalidate_cached_blockhash().await;
// Restore preparation stage for retry
*stage = CommitBufferStage::Preparation(preparation_task);
// Retry preparation
// Preparation failed due to buffer existing - cleanup and retry
let cleanup_task = match task {
BaseTaskImpl::Commit(commit_task) => CleanupTask {
commit_id: commit_task.commit_id,
pubkey: commit_task.committed_account.pubkey,
},
BaseTaskImpl::CommitFinalize(commit_finalize_task) => CleanupTask {
commit_id: commit_finalize_task.commit_id,
pubkey: commit_finalize_task.committed_account.pubkey,
},
_ => return Ok(()),
};
self.cleanup_buffers(authority, &[cleanup_task]).await?;
self.rpc_client.invalidate_cached_blockhash().await;
// Retry preparation
🤖 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
`@magicblock-committor-service/src/transaction_preparator/delivery_preparator.rs`
around lines 177 - 196, The retry cleanup path in
delivery_preparator::prepare_task is rebuilding a full PreparationTask just to
obtain a CleanupTask, which repeats the BaseTaskImpl matching and can trigger
expensive buffer cloning/compute_diff work. Change this branch to derive
CleanupTask directly from the existing task variants used in prepare_task, using
only the commit_id and pubkey needed for cleanup, and remove the intermediate
PreparationTask::from_commit/from_commit_finalize construction before calling
cleanup_buffers.

@taco-paco taco-paco requested review from GabrielePicco and snawaz and removed request for snawaz July 2, 2026 09:15
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.

1 participant