---
title: Empowering Django with Background Workers
class: text-center
highlighter: shiki
transition: slide-left
mdc: true
monaco: false
themeConfig:
primary: '#0c4b33'
---
# Empowering with Background Workers
## Jake Howard{.mt-5}
### Djangocon Europe 2024{.mt-5}
---
layout: full
---
# `$ whoami`
- Senior Systems Engineer @ Torchbox
- Core, Security & Performance teams @ Wagtail
- I'm an avid self-hoster
- I help students build robots in my "spare" time
- theorangeone.net
- @RealOrangeOne
- @RealOrangeOne
- @jake@theorangeone.net
---
layout: center
---
# Django is a web framework
```mermaid
flowchart LR
U(User 🧑💻)
D[\Django/]
U---->|Request|D
D---->|Response|U
```
---
layout: full
---
# Django isn't _just_ for websites
```mermaid
flowchart BT
U[User 🧑💻]
D[\Django/]
DB[(Database)]
E>Email]
EA[External API]
V[[Video Transcoding]]
R[Reporting]
ML((Machine
Learning))
U<--->D
D---DB
D-..-E & EA & V & R & ML
```
---
layout: full
---
# Background Workers?
```mermaid
flowchart BT
U[User 🧑💻]
D[\Django/]
E>Email]
EA[External API]
V[[Video Transcoding]]
R[Reporting]
ML((Machine
Learning))
B{{Background Worker}}
U<-->D
D-..-B
B---E & EA & V & R & ML
```
---
layout: section
---
## Background worker architecture
```mermaid
flowchart LR
D[\Django/]
S[(Queue Store)]
R1{Runner}
R2{Runner}
R3{Runner}
D<----->S<-....->R1 & R2 & R3
```
---
layout: section
---
# When?
---
layout: cover
background: https://images.unsplash.com/photo-1518729371765-043e54eb5674?q=80&w=1807&auto=format&fit=crop&ixlib=rb-4.0.3
---
# Does it take time?{.text-right}
---
layout: fact
---
## Does it leave your infrastructure?{.mb-5}
```mermaid
flowchart BT
D[\Django/]
subgraph Slow / Unreliable
E>Email]
EA[External API]
V[[Video Transcode]]
R[Reporting]
ML((Machine
Learning))
end
subgraph Fast & Reliable
DB[(Database)]
C[(Cache)]
end
D---DB & C
D-.-E & EA & V & R & ML
```
---
layout: cover
background: https://images.unsplash.com/photo-1518770660439-4636190af475?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3
---
# Specialized hardware?
---
layout: image-right
image: https://images.unsplash.com/photo-1711606815631-38d32cdaec3e?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3
class: text-xl
---
# Examples
- Compiling code
- Complex reporting
- File uploads
- Model training
- PDF generation
- Resizing images
- Sending email
- Transcoding video
- ... 🤯
---
layout: section
---
# Background Workers in
---
layout: image-right
image: https://images.unsplash.com/photo-1444703686981-a3abbc4d4fe3?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3
---
# Libraries
- Celery
- arq
- Django DB Queue
- Django Lightweight Queue
- Django Too Simple Q
- Django-Q
- Django-Q2
- Dramatiq
- Huey
- RQ
- Taskiq
- ...
---
layout: cover
background: https://images.unsplash.com/photo-1522096823084-2d1aa8411c13?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3
---
## Example:
# Email
---
layout: center
---
# Sending an email
```python {all|7|8|9-14|all}
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from wagtail.models import Page
for user in page.subscribers.iterator():
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
```
---
layout: center
---
```python {all|18|19|10|11-16|all|18-19|all}
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
import django_rq
from wagtail.models import Page
def send_email_to_user(page: Page, user: User):
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
for user in page.subscribers.iterator():
django_rq.enqueue(send_email_to_user, user)
```
---
layout: center
---
# Using RQRQ Celery
````md magic-move
```python
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
import django_rq
from wagtail.models import Page
def send_email_to_user(page: Page, user: User):
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
for user in page.subscribers.iterator():
django_rq.enqueue(send_email_to_user, user)
```
```python {all|7-9,20|all}
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from wagtail.models import Page
from my_celery_config import app
@app.task
def send_email_to_user(page: Page, user: User):
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
for user in page.subscribers.iterator():
send_email_to_user.delay(user)
```
````
---
layout: image
image: /situation.png
backgroundSize: 50%
---
---
layout: image
image: /ridiculous.png
backgroundSize: 49%
---
---
layout: fact
---
## Introducing*:{.mb-5 .mt-3}
# `django.tasks`
---
layout: image-right
image: https://images.unsplash.com/photo-1674027444485-cec3da58eef4?q=80&w=1932&auto=format&fit=crop&ixlib=rb-4.0.3
class: flex items-center text-xl
---
- API contract between library and application developers
- Swappable backends through `settings.py`
- Built in implementations:
- ORM
- "Immediate"
- "Dummy"
- Django 5.2 🤞
- Backport for 4.2+
---
layout: center
---
# Using CeleryUsing django.tasks
````md magic-move
```python
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from wagtail.models import Page
from my_celery_config import app
@app.task
def send_email_to_user(page: Page, user: User):
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
for user in page.subscribers.iterator():
send_email_to_user.delay(user)
```
```python
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from wagtail.models import Page
from django.tasks import task
@task()
def send_email_to_user(page: Page, user: User):
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
for user in page.subscribers.iterator():
send_email_to_user.enqueue(user)
```
````
---
layout: center
---
```python
# settings.py
EMAIL_BACKEND = "django.core.mail.backends.tasks.SMTPEmailBackend"
```
```python
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from wagtail.models import Page
for user in page.subscribers.iterator():
email_content = render_to_string("notification-email.html", {"user": user, "page": page})
send_mail(
subject=f"A change to {page.title} has been published",
message=email_content
from_email=None, # Use the default sender email
recipient_list=[user.email]
)
```
---
layout: image-right
image: /soon.png
class: flex justify-center text-2xl flex-col
---
# Q: Why something new?
---
layout: image-right
image: https://images.unsplash.com/photo-1525683879097-8babce1c602a?q=80&w=1335&auto=format&fit=crop&ixlib=rb-4.0.3
class: flex justify-center text-xl flex-col
---
# Q: Why something built-in?
- Reduce barrier to entry
- Reduce cognitive load
- Reduce complexity for smaller projects
- Improve interoperability
- Use what's already there
- A common API
---
layout: center
transition: fade
---
![](/celery.svg){.h-32.mx-auto}
## vs
![](/postgres.png){.h-36.mx-auto}
---
layout: center
---
![](/elasticsearch.png){.h-32.mx-auto}
## vs
![](/postgres.png){.max-h-36.mx-auto}
---
layout: section
---
# Where are we now?
---
layout: image
image: /dep.png
---
---
layout: section
---
# `pip install django-tasks`
---
layout: section
---
# Where will we be _soon_™?
---
layout: cover
background: /celery.svg
---
# Is this the end?
---
layout: image-right
image: https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=1744&auto=format&fit=crop&ixlib=rb-4.0.3
class: flex justify-center flex-col text-xl
---
# Out of scope (_for now_)
- Completion / failed hooks
- Bulk queueing
- Automated task retrying
- Task runner API
- Unified observability
- Cron-based scheduling
- Task timeouts
- Swappable argument serialization
- ...
---
layout: cover
background: https://images.unsplash.com/photo-1519187903022-c0055ec4036a?q=80&w=1335&auto=format&fit=crop&ixlib=rb-4.0.3
---
# The future is bright
---
layout: section
---
# What's next?
## `pip install django-tasks`
---
layout: section
class: text-center text-2xl
---
# Let's chat!
- theorangeone.net
- @RealOrangeOne
- @RealOrangeOne
- @jake@theorangeone.net
Me
django-tasks
---
layout: end
---
END