Celery Task Revocation: Cancelling Running and Pending Tasks
In this tutorial, you will learn about Celery Task Revocation: Cancelling Running and Pending Tasks. We cover key concepts, practical examples, and best practices to help you master this topic.
Celery task revocation enables cancelling pending or running tasks by task ID, using broker-based revocation lists and worker broadcast commands to prevent specific tasks from executing or terminate them in progress.
flowchart LR
S[Submit Task] --> Q[(Queue)]
Q -->|Pending| R{Revoked?}
R -->|Yes| D[Discarded]
R -->|No| W[Worker Executes]
W --> E{Revoke Command?}
E -->|Yes, terminate=True| K[Process Terminated]
E -->|No| Done[Task Complete]
Revoke[Revoke API] -->|Task ID| Broker[Broker]
Broker -->|Revocation List| Q
Broker -->|Broadcast| W
What You'll Learn
- Revoking pending tasks by ID
- Terminating running tasks
- Revocation broadcast to all workers
- Revocation with task name patterns
- Handling revocation in task code
Why It Matters
Without revocation, there is no way to stop a task once submitted. Stale, erroneous, or duplicate tasks consume resources unnecessarily. Revocation provides an emergency stop mechanism for production task management.
Real-World Use
DodaTech's backup system submits full backups nightly. When a user triggers an emergency manual backup, the system revokes all pending automatic backups to avoid duplicate work and I/O contention.
Basic Task Revocation
from celery import Celery
import time
app = Celery('revocation', broker='redis://localhost:6379/0')
app.conf.task_track_started = True
@app.task(bind=True)
def long_running_task(self, task_id, duration):
for i in range(duration):
time.sleep(1)
self.update_state(
state='PROGRESS',
meta={'current': i + 1, 'total': duration}
)
print(f"Task {task_id}: second {i + 1}/{duration}")
return f"Task {task_id} complete"
task = long_running_task.delay("backup-001", 30)
print(f"Submitted: {task.id}")
revoked = app.control.revoke(task.id, terminate=False)
time.sleep(1)
print(f"Revoked: {revoked}")
Start worker with:
celery -A revocation worker --loglevel=info
Expected output:
Submitted: 550e8400-e29b-41d4-a716-446655440000
Revoked: None
[2026-06-28 10:00:01: INFO] Task revoked: 550e8400-e29b-41d4-a716-446655440000
Task backup-001: second 1/30
[2026-06-28 10:00:02: INFO] Task skipped as revoked
Terminating Running Tasks
from celery import Celery
import time
app = Celery('terminate', broker='redis://localhost:6379/0')
app.conf.task_track_started = True
@app.task(bind=True)
def stuck_task(self, task_name):
try:
for i in range(60):
time.sleep(1)
print(f"{task_name}: iteration {i + 1}")
return "Completed"
except Exception as e:
print(f"Task terminated: {e}")
return f"Terminated at iteration unknown"
task = stuck_task.delay("data-migration-v2")
print(f"Submitted: {task.id}")
time.sleep(3)
revoked = app.control.revoke(task.id, terminate=True, signal='SIGTERM')
print(f"Terminate sent: {revoked}")
Expected output:
Submitted: id
data-migration-v2: iteration 1
data-migration-v2: iteration 2
data-migration-v2: iteration 3
Terminate sent: None
[2026-06-28 10:00:04: INFO] Task terminated by revoke
data-migration-v2: iteration 4 (not reached)
Revoke by Task Name
from celery import Celery
import time
app = Celery('revoke_by_name', broker='redis://localhost:6379/0')
@app.task
def email_campaign(campaign_id):
time.sleep(0.2)
result = f"Campaign {campaign_id} sent"
print(result)
return result
@app.task
def cleanup_temp_files():
time.sleep(0.1)
result = "Temp files cleaned"
print(result)
return result
for i in range(5):
email_campaign.delay(f"CAMP-{i}")
for i in range(3):
cleanup_temp_files.delay()
app.control.revoke_by_name('revoke_by_name.email_campaign')
time.sleep(0.5)
print("Revoked all email_campaign tasks")
failed = email_campaign.delay("CAMP-NEW")
time.sleep(0.1)
print(f"New campaign submitted after revoke: {failed.id}")
Expected output:
Revoked all email_campaign tasks
Campaign 0 sent (if already started)
[2026-06-28 10:00:00: WARNING] Revoked task email_campaign skipped
Temp files cleaned
Temp files cleaned
Temp files cleaned
New campaign submitted after revoke: new_id
Common Mistakes
- Forgetting terminate=True for running tasks -- revoke without terminate only stops pending tasks. Running tasks continue until completion. Add terminate=True and a signal to stop active execution.
- Using SIGKILL instead of SIGTERM -- SIGKILL (signal=9) kills the Process without cleanup. SIGTERM (signal=15) allows graceful shutdown. Use SIGTERM first, SIGKILL only as a last resort.
- Revocation persistence across worker restarts -- revocation list is stored in memory and lost on worker restart. Use persistent revocation with Redis or database backend for durable revocation.
- Revoke by name affecting future tasks -- revoke_by_name only affects currently queued tasks. Future tasks with the same name are not revoked. The revocation list is cleared or applies only to existing messages.
- Not checking task.revoked() status -- tasks in progress cannot check if they should exit unless the application code monitors its own state. Periodic cancellation checks prevent wasted work.
Practice Questions
- What is the difference between revoke and terminate in Celery?
- How does revoke_by_name differ from revoke by task ID?
- What signal should you send for graceful task termination?
- How does revocation survive a worker restart?
- Can a revoked task be un-revoked?
Challenge
Build a task management dashboard that: (1) lists all pending and running tasks with their IDs and names, (2) provides revoke buttons for individual tasks and bulk revoke by task name, (3) allows selecting SIGTERM or SIGKILL for running tasks, (4) shows revocation status (pending revoked, terminated, or missed), and (5) logs all revocation actions with timestamps to an audit trail.
FAQ
Mini Project
Build an emergency task kill switch system: (1) expose a Webhook that accepts task_id and signal type (SIGTERM/SIGKILL), (2) maintain a RevokedTasks set in Redis with TTL of 1 hour, (3) implement a custom base task that checks Redis for revocation every 5 seconds (cooperative cancellation), (4) on detection of revocation, save partial state and raise a custom TaskRevoked exception, (5) log all revocation events with task_id, requester IP, and timestamp.
What's Next
Continue with Worker Shutdown to learn graceful worker lifecycle management. Then explore Remote Control for managing workers programmatically.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro