Summary
Support cancelling tasks by group, type, or custom filter, with an on_cancel hook in TaskExecutor for cleanup logic.
Motivation
When a user cancels a sync profile or the connectivity monitor goes offline, we need to:
- Cancel all pending tasks for that profile without affecting other profiles
- Clean up in-flight work (e.g. call
AbortMultipartUpload for uploads that won't complete)
- Give running executors a signal to stop gracefully
Without cancellation hooks, orphaned multipart uploads accumulate on S3 and incur storage charges. Without scoped cancellation, stopping one profile requires stopping the entire scheduler.
Proposed Behavior
- Scoped cancellation — cancel by group, type, or predicate:
// Cancel all tasks for a specific endpoint
scheduler.cancel_group("s3://play.min.io").await;
// Cancel all tasks of a type
scheduler.cancel_type("upload-part").await;
// Cancel with a predicate
scheduler.cancel_where(|task| task.dedup_key().starts_with("profile:dr-backup")).await;
- Cancellation token —
TaskContext exposes a cancellation token that running executors can check:
async fn execute<'a>(&'a self, ctx: &'a TaskContext) -> Result<(), TaskError> {
for chunk in data.chunks(BUFFER_SIZE) {
ctx.check_cancelled()?; // returns Err(TaskError::Cancelled) if cancelled
self.upload(chunk).await?;
}
Ok(())
}
- Abort hook —
TaskExecutor gains an optional on_cancel method:
async fn on_cancel<'a>(&'a self, ctx: &'a TaskContext) -> Result<(), TaskError> {
let upload_id: String = ctx.state().get("upload_id")?;
self.s3.abort_multipart_upload(&upload_id).await?;
Ok(())
}
- Parent cancellation cascades — cancelling a parent task cancels all its pending/running children and invokes their
on_cancel hooks
- Cancelled tasks are recorded in task history with
HistoryStatus::Cancelled
Design Considerations
on_cancel should have a timeout to prevent cleanup from blocking indefinitely
- Cancellation of a running task should be cooperative (via token check), not forced (task kill)
- Cancelled children should not cause the parent's
finalize to run — the parent should also transition to cancelled
- Batch cancellation should be atomic where possible (cancel all matching tasks in one operation, not one at a time)
Summary
Support cancelling tasks by group, type, or custom filter, with an
on_cancelhook inTaskExecutorfor cleanup logic.Motivation
When a user cancels a sync profile or the connectivity monitor goes offline, we need to:
AbortMultipartUploadfor uploads that won't complete)Without cancellation hooks, orphaned multipart uploads accumulate on S3 and incur storage charges. Without scoped cancellation, stopping one profile requires stopping the entire scheduler.
Proposed Behavior
TaskContextexposes a cancellation token that running executors can check:TaskExecutorgains an optionalon_cancelmethod:on_cancelhooksHistoryStatus::CancelledDesign Considerations
on_cancelshould have a timeout to prevent cleanup from blocking indefinitelyfinalizeto run — the parent should also transition to cancelled