Python background job patterns including task queues

Python Background Jobs & Task Queues

by wshobson · MIT license · ★ 39,857 on GitHub · updated Sep 21

Python Background Jobs & Task Queues cover

Python background job patterns including task queues, workers, and event-driven architecture. Use when implementing async task processing, job queues, long-running operations, or decoupling work from request/response cycles.

---
name: python-background-jobs
description: Python background job patterns including task queues, workers, and event-driven architecture. Use when implementing async task processing, job queues, long-running operations, or decoupling work from request/response cycles.
---

# Python Background Jobs & Task Queues

Decouple long-running or unreliable work from request/response cycles. Return immediately to the user while background workers handle the heavy lifting asynchronously.

## When to Use This Skill

- Processing tasks that take longer than a few seconds
- Sending emails, notifications, or webhooks
- Generating reports or exporting data
- Processing uploads or media transformations
- Integrating with unreliable external services
- Building event-driven architectures

Overview

Decouple long-running or unreliable work from request/response cycles. Return immediately to the user while background workers handle the heavy lifting asynchronously.

Manual install (npx, ZIP)
Claude Codenpx skills add wshobson/agents --skill python-background-jobs -g -a claude-code
Claude Code (manual)mkdir -p ~/.claude/skills && curl -sL https://codeload.github.com/wshobson/agents/tar.gz/4236bb91f839 \ | tar -xz -C ~/.claude/skills --strip-components=4 agents-4236bb91f839/plugins/python-development/skills/python-background-jobs
Windows (PowerShell)New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Invoke-WebRequest "https://m.agentalley.io/zip/8ef01bd551ccdfe837d7/python-background-jobs.zip" -OutFile "$env:TEMP\python-background-jobs.zip"; Expand-Archive "$env:TEMP\python-background-jobs.zip" -DestinationPath "$HOME\.claude\skills" -Force
Claude.ai / desktopDownload the ZIP and upload it under Settings → Capabilities → Skills.
Codex, Cursor, other agentsPoint them at SKILL.md. The other files are plain text.

Connects with Agent Alley MCP · agentalley.io/mcp

Files included

  • SKILL.md7.1 KB
  • references/1 file · 3.3 KB
Read the files here
wshobson/4236bb92 files
SKILL.md
Show the full text242 lines

Python Background Jobs & Task Queues

Decouple long-running or unreliable work from request/response cycles. Return immediately to the user while background workers handle the heavy lifting asynchronously.

When to Use This Skill

  • Processing tasks that take longer than a few seconds
  • Sending emails, notifications, or webhooks
  • Generating reports or exporting data
  • Processing uploads or media transformations
  • Integrating with unreliable external services
  • Building event-driven architectures

Core Concepts

1. Task Queue Pattern

API accepts request, enqueues a job, returns immediately with a job ID. Workers process jobs asynchronously.

2. Idempotency

Tasks may be retried on failure. Design for safe re-execution.

3. Job State Machine

Jobs transition through states: pending → running → succeeded/failed.

4. At-Least-Once Delivery

Most queues guarantee at-least-once delivery. Your code must handle duplicates.

Quick Start

This skill uses Celery for examples, a widely adopted task queue. Alternatives like RQ, Dramatiq, and cloud-native solutions (AWS SQS, GCP Tasks) are equally valid choices.

from celery import Celery

app = Celery("tasks", broker="redis://localhost:6379")

@app.task
def send_email(to: str, subject: str, body: str) -> None:
    # This runs in a background worker
    email_client.send(to, subject, body)

# In your API handler
send_email.delay("[email protected]", "Welcome!", "Thanks for signing up")

Fundamental Patterns

Pattern 1: Return Job ID Immediately

For operations exceeding a few seconds, return a job ID and process asynchronously.

from uuid import uuid4
from dataclasses import dataclass
from enum import Enum
from datetime import datetime

class JobStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    SUCCEEDED = "succeeded"
    FAILED = "failed"

@dataclass
class Job:
    id: str
    status: JobStatus
    created_at: datetime
    started_at: datetime | None = None
    completed_at: datetime | None = None
    result: dict | None = None
    error: str | None = None

# API endpoint
async def start_export(request: ExportRequest) -> JobResponse:
    """Start export job and return job ID."""
    job_id = str(uuid4())

    # Persist job record
    await jobs_repo.create(Job(
        id=job_id,
        status=JobStatus.PENDING,
        created_at=datetime.utcnow(),
    ))

    # Enqueue task for background processing
    await task_queue.enqueue(
        "export_data",
        job_id=job_id,
        params=request.model_dump(),
    )

    # Return immediately with job ID
    return JobResponse(
        job_id=job_id,
        status="pending",
        poll_url=f"/jobs/{job_id}",
    )
Pattern 2: Celery Task Configuration

Configure Celery tasks with proper retry and timeout settings.

from celery import Celery

app = Celery("tasks", broker="redis://localhost:6379")

# Global configuration
app.conf.update(
    task_time_limit=3600,          # Hard limit: 1 hour
    task_soft_time_limit=3000,      # Soft limit: 50 minutes
    task_acks_late=True,            # Acknowledge after completion
    task_reject_on_worker_lost=True,
    worker_prefetch_multiplier=1,   # Don't prefetch too many tasks
)

@app.task(
    bind=True,
    max_retries=3,
    default_retry_delay=60,
    autoretry_for=(ConnectionError, TimeoutError),
)
def process_payment(self, payment_id: str) -> dict:
    """Process payment with automatic retry on transient errors."""
    try:
        result = payment_gateway.charge(payment_id)
        return {"status": "success", "transaction_id": result.id}
    except PaymentDeclinedError as e:
        # Don't retry permanent failures
        return {"status": "declined", "reason": str(e)}
    except TransientError as e:
        # Retry with exponential backoff
        raise self.retry(exc=e, countdown=2 ** self.request.retries * 60)
Pattern 3: Make Tasks Idempotent

Workers may retry on crash or timeout. Design for safe re-execution.

@app.task(bind=True)
def process_order(self, order_id: str) -> None:
    """Process order idempotently."""
    order = orders_repo.get(order_id)

    # Already processed? Return early
    if order.status == OrderStatus.COMPLETED:
        logger.info("Order already processed", order_id=order_id)
        return

    # Already in progress? Check if we should continue
    if order.status == OrderStatus.PROCESSING:
        # Use idempotency key to avoid double-charging
        pass

    # Process with idempotency key
    result = payment_provider.charge(
        amount=order.total,
        idempotency_key=f"order-{order_id}",  # Critical!
    )

    orders_repo.update(order_id, status=OrderStatus.COMPLETED)

Idempotency Strategies:

  1. Check-before-write: Verify state before action
  2. Idempotency keys: Use unique tokens with external services
  3. Upsert patterns: INSERT ... ON CONFLICT UPDATE
  4. Deduplication window: Track processed IDs for N hours
Pattern 4: Job State Management

Persist job state transitions for visibility and debugging.

class JobRepository:
    """Repository for managing job state."""

    async def create(self, job: Job) -> Job:
        """Create new job record."""
        await self._db.execute(
            """INSERT INTO jobs (id, status, created_at)
               VALUES ($1, $2, $3)""",
            job.id, job.status.value, job.created_at,
        )
        return job

    async def update_status(
        self,
        job_id: str,
        status: JobStatus,
        **fields,
    ) -> None:
        """Update job status with timestamp."""
        updates = {"status": status.value, **fields}

        if status == JobStatus.RUNNING:
            updates["started_at"] = datetime.utcnow()
        elif status in (JobStatus.SUCCEEDED, JobStatus.FAILED):
            updates["completed_at"] = datetime.utcnow()

        await self._db.execute(
            "UPDATE jobs SET status = $1, ... WHERE id = $2",
            updates, job_id,
        )

        logger.info(
            "Job status updated",
            job_id=job_id,
            status=status.value,
        )

Detailed worked examples and patterns

Detailed sections (starting with ## Advanced Patterns) live in references/details.md. Read that file when the navigation summary above is insufficient.

Best Practices Summary

  1. Return immediately - Don't block requests for long operations
  2. Persist job state - Enable status polling and debugging
  3. Make tasks idempotent - Safe to retry on any failure
  4. Use idempotency keys - For external service calls
  5. Set timeouts - Both soft and hard limits
  6. Implement DLQ - Capture permanently failed tasks
  7. Log transitions - Track job state changes
  8. Retry appropriately - Exponential backoff for transient errors
  9. Don't retry permanent failures - Validation errors, invalid credentials
  10. Monitor queue depth - Alert on backlog growth
1---
2name: python-background-jobs
3description: Python background job patterns including task queues, workers, and event-driven architecture. Use when implementing async task processing, job queues, long-running operations, or decoupling work from request/response cycles.
4---
5 
6# Python Background Jobs & Task Queues
7 
8Decouple long-running or unreliable work from request/response cycles. Return immediately to the user while background workers handle the heavy lifting asynchronously.
9 
10## When to Use This Skill
11 
12- Processing tasks that take longer than a few seconds
13- Sending emails, notifications, or webhooks
14- Generating reports or exporting data
15- Processing uploads or media transformations
16- Integrating with unreliable external services
17- Building event-driven architectures
18 
19## Core Concepts
20 
21### 1. Task Queue Pattern
22 
23API accepts request, enqueues a job, returns immediately with a job ID. Workers process jobs asynchronously.
24 
25### 2. Idempotency
26 
27Tasks may be retried on failure. Design for safe re-execution.
28 
29### 3. Job State Machine
30 
31Jobs transition through states: pending → running → succeeded/failed.
32 
33### 4. At-Least-Once Delivery
34 
35Most queues guarantee at-least-once delivery. Your code must handle duplicates.
36 
37## Quick Start
38 
39This skill uses Celery for examples, a widely adopted task queue. Alternatives like RQ, Dramatiq, and cloud-native solutions (AWS SQS, GCP Tasks) are equally valid choices.
40 
41```python
42from celery import Celery
43 
44app = Celery("tasks", broker="redis://localhost:6379")
45 
46@app.task
47def send_email(to: str, subject: str, body: str) -> None:
48 # This runs in a background worker
49 email_client.send(to, subject, body)
50 
51# In your API handler
52send_email.delay("[email protected]", "Welcome!", "Thanks for signing up")
53```
54 
55## Fundamental Patterns
56 
57### Pattern 1: Return Job ID Immediately
58 
59For operations exceeding a few seconds, return a job ID and process asynchronously.
60 
61```python
62from uuid import uuid4
63from dataclasses import dataclass
64from enum import Enum
65from datetime import datetime
66 
67class JobStatus(Enum):
68 PENDING = "pending"
69 RUNNING = "running"
70 SUCCEEDED = "succeeded"
71 FAILED = "failed"
72 
73@dataclass
74class Job:
75 id: str
76 status: JobStatus
77 created_at: datetime
78 started_at: datetime | None = None
79 completed_at: datetime | None = None
80 result: dict | None = None
81 error: str | None = None
82 
83# API endpoint
84async def start_export(request: ExportRequest) -> JobResponse:
85 """Start export job and return job ID."""
86 job_id = str(uuid4())
87 
88 # Persist job record
89 await jobs_repo.create(Job(
90 id=job_id,
91 status=JobStatus.PENDING,
92 created_at=datetime.utcnow(),
93 ))
94 
95 # Enqueue task for background processing
96 await task_queue.enqueue(
97 "export_data",
98 job_id=job_id,
99 params=request.model_dump(),
100 )
101 
102 # Return immediately with job ID
103 return JobResponse(
104 job_id=job_id,
105 status="pending",
106 poll_url=f"/jobs/{job_id}",
107 )
108```
109 
110### Pattern 2: Celery Task Configuration
111 
112Configure Celery tasks with proper retry and timeout settings.
113 
114```python
115from celery import Celery
116 
117app = Celery("tasks", broker="redis://localhost:6379")
118 
119# Global configuration
120app.conf.update(
121 task_time_limit=3600, # Hard limit: 1 hour
122 task_soft_time_limit=3000, # Soft limit: 50 minutes
123 task_acks_late=True, # Acknowledge after completion
124 task_reject_on_worker_lost=True,
125 worker_prefetch_multiplier=1, # Don't prefetch too many tasks
126)
127 
128@app.task(
129 bind=True,
130 max_retries=3,
131 default_retry_delay=60,
132 autoretry_for=(ConnectionError, TimeoutError),
133)
134def process_payment(self, payment_id: str) -> dict:
135 """Process payment with automatic retry on transient errors."""
136 try:
137 result = payment_gateway.charge(payment_id)
138 return {"status": "success", "transaction_id": result.id}
139 except PaymentDeclinedError as e:
140 # Don't retry permanent failures
141 return {"status": "declined", "reason": str(e)}
142 except TransientError as e:
143 # Retry with exponential backoff
144 raise self.retry(exc=e, countdown=2 ** self.request.retries * 60)
145```
146 
147### Pattern 3: Make Tasks Idempotent
148 
149Workers may retry on crash or timeout. Design for safe re-execution.
150 
151```python
152@app.task(bind=True)
153def process_order(self, order_id: str) -> None:
154 """Process order idempotently."""
155 order = orders_repo.get(order_id)
156 
157 # Already processed? Return early
158 if order.status == OrderStatus.COMPLETED:
159 logger.info("Order already processed", order_id=order_id)
160 return
161 
162 # Already in progress? Check if we should continue
163 if order.status == OrderStatus.PROCESSING:
164 # Use idempotency key to avoid double-charging
165 pass
166 
167 # Process with idempotency key
168 result = payment_provider.charge(
169 amount=order.total,
170 idempotency_key=f"order-{order_id}", # Critical!
171 )
172 
173 orders_repo.update(order_id, status=OrderStatus.COMPLETED)
174```
175 
176**Idempotency Strategies:**
177 
1781. **Check-before-write**: Verify state before action
1792. **Idempotency keys**: Use unique tokens with external services
1803. **Upsert patterns**: `INSERT ... ON CONFLICT UPDATE`
1814. **Deduplication window**: Track processed IDs for N hours
182 
183### Pattern 4: Job State Management
184 
185Persist job state transitions for visibility and debugging.
186 
187```python
188class JobRepository:
189 """Repository for managing job state."""
190 
191 async def create(self, job: Job) -> Job:
192 """Create new job record."""
193 await self._db.execute(
194 """INSERT INTO jobs (id, status, created_at)
195 VALUES ($1, $2, $3)""",
196 job.id, job.status.value, job.created_at,
197 )
198 return job
199 
200 async def update_status(
201 self,
202 job_id: str,
203 status: JobStatus,
204 **fields,
205 ) -> None:
206 """Update job status with timestamp."""
207 updates = {"status": status.value, **fields}
208 
209 if status == JobStatus.RUNNING:
210 updates["started_at"] = datetime.utcnow()
211 elif status in (JobStatus.SUCCEEDED, JobStatus.FAILED):
212 updates["completed_at"] = datetime.utcnow()
213 
214 await self._db.execute(
215 "UPDATE jobs SET status = $1, ... WHERE id = $2",
216 updates, job_id,
217 )
218 
219 logger.info(
220 "Job status updated",
221 job_id=job_id,
222 status=status.value,
223 )
224```
225 
226## Detailed worked examples and patterns
227 
228Detailed sections (starting with `## Advanced Patterns`) live in `references/details.md`. Read that file when the navigation summary above is insufficient.
229 
230## Best Practices Summary
231 
2321. **Return immediately** - Don't block requests for long operations
2332. **Persist job state** - Enable status polling and debugging
2343. **Make tasks idempotent** - Safe to retry on any failure
2354. **Use idempotency keys** - For external service calls
2365. **Set timeouts** - Both soft and hard limits
2376. **Implement DLQ** - Capture permanently failed tasks
2387. **Log transitions** - Track job state changes
2398. **Retry appropriately** - Exponential backoff for transient errors
2409. **Don't retry permanent failures** - Validation errors, invalid credentials
24110. **Monitor queue depth** - Alert on backlog growth
242 
View all files on GitHub →

Details

Updated
Sep 21, 2026
License
MIT
Stars on the repo
★ 39,857
Checked
Source
wshobson/agents ↗
Creator
wshobson · 179 listings
View creator

Similar skills

See all →
SkillA task is too large for one model passAdvisor orchestrator workerShubhamsaboo/awesome-llm-apps · by ShubhamsabooUse when a task is too large for one model pass, needs parallel research or generation across many subtasks (like researching a dozen competitors at once), or the user asks to orchestrate multiple models, split work across a model team, run an advisor-worker loop, have a stronger model review the plan while cheap workers execute, or says "too big for one model" or "fan this out". Not for single-file edits or tasks one model handles in one pass.★ 139k GitHub starsSkillRun the CCS Align seat's hourly breathing cycleCCS Align — Worker Watch seat (Phases 0–2 shipped; Phase 3 = verify / sign-off)thedotmack/claude-mem · by thedotmackRun the CCS Align seat's hourly breathing cycle — prove the local claude-mem worker is healthy, pull needle observations through search → timeline → get_observations, land them in a seat-owned middle cache via atomic grab → append → filter exclude-marks → replace, manage exclude marks, and walk house → project → seat rules to detect conflicts (SHADOW_HOUSE, DENY_ALLOW, DRIFT, CLOCK_HEADER) with an append-only rules-report.md. Use when asked to run CCS Align, breathe the alignment seat, refresh the middle cache, exclude or restore an observation, walk rules, check rules conflicts, or check the Worker Watch board.★ 95.6k GitHub starsSkillSummarize and review what changed while you were awayCatchup — Async-Team Return Briefingoliver-kriska/claude-elixir-phoenix · by oliver-kriskaSummarize and review what changed while you were away. Use after a weekend, vacation, or flight to check missed PRs, git commits, Linear tickets, and meetings — one prioritized brief, not a firehose.★ 560 GitHub starsSkillMaster Python asyncio, concurrent programmingAsync python patternswshobson/agents · by wshobsonMaster Python asyncio, concurrent programming, and async/await patterns for high-performance applications. Use when building async APIs, concurrent systems, or I/O-bound applications requiring non-blocking operations.★ 39.9k GitHub starsSkillMaster Go concurrency with goroutines, channelsGo concurrency patternswshobson/agents · by wshobsonMaster Go concurrency with goroutines, channels, sync primitives, and context. Use when building concurrent Go applications, implementing worker pools, or debugging race conditions.★ 39.9k GitHub starsSkillDesign microservices architectures with service boundariesMicroservices patternswshobson/agents · by wshobsonDesign microservices architectures with service boundaries, event-driven communication, and resilience patterns. Use when building distributed systems, decomposing monoliths, or implementing microservices.★ 39.9k GitHub stars

More from wshobson

SkillMaster C/.NET backend development patterns for buildingwshobson/agents · by wshobsonMaster C#/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications. Covers async/await, dependency injection, Entity Framework Core, Dapper, configuration, caching, and testing with xUnit. Use when developing .NET backends, reviewing C# code, or designing API architectures.★ 39.9k GitHub starsSkillImplement WCAG 2.2 compliant interfaces with mobileAccessibility compliancewshobson/agents · by wshobsonImplement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.★ 39.9k GitHub starsSkillMaster Material Design 3 and Jetpack Compose patternsAndroid Mobile Designwshobson/agents · by wshobsonMaster Material Design 3 and Jetpack Compose patterns for building native Android apps. Use when designing Android interfaces, implementing Compose UI, or following Google's Material Design guidelines.★ 39.9k GitHub starsSkillMigrate from AngularJS to Angular using hybrid modeAngular migrationwshobson/agents · by wshobsonMigrate from AngularJS to Angular using hybrid mode, incremental component rewriting, and dependency injection updates. Use when upgrading AngularJS applications, planning framework migrations, or modernizing legacy Angular code.★ 39.9k GitHub starsSkillUnderstand anti-reversing, obfuscationAnti reversing techniqueswshobson/agents · by wshobsonUnderstand anti-reversing, obfuscation, and protection techniques encountered during software analysis. Use this skill when analyzing malware evasion techniques, when implementing anti-debugging protections for CTF challenges, when reverse engineering packed binaries, or when building security research tools that need to detect virtualized environments.★ 39.9k GitHub starsSkillBuild production Apache Airflow DAGs with best practicesApache Airflow DAG Patternswshobson/agents · by wshobsonBuild production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs.★ 39.9k GitHub stars