Week 9 · Asynchronous jobs: Redis and Celery
The one-minute story
A restaurant with a waiter, an order board and a kitchen. You (the browser) tell the waiter (the Flask server) "one biryani". If the waiter walked into the kitchen, cooked for 30 minutes and only then came back, nobody else in the restaurant would be served. So the waiter writes the order on a slip, pins it on the order board (the message broker, here Redis) and comes straight back: "Order taken, your token number is 17." The cooks (Celery workers) take slips from the board whenever they are free and cook. When the food is ready the cook leaves the plate on the pickup counter (the result backend). You come back with token 17 and ask "Is it ready?" (polling). There is also a clock on the wall: every morning at 6 it automatically pins a slip "Make the daily tea" on the board (Celery beat).
Slow jobs do not belong inside a web request. They belong on a board, for cooks.
By the end of this week you can:
- explain synchronous versus asynchronous work and why slow jobs must leave the request;
- explain the roles of broker, worker and result backend, and what Redis does in this setup;
- write a Celery task, call it with
.delay()and.apply_async(), and predict what it returns; - configure Celery with Flask (app context) and start a worker and a beat scheduler;
- design a CSV export flow (trigger, background build, status polling, download) and a daily reminder;
- explain retries, idempotency and failure handling.
Words you need
- Synchronous - do the job and wait until it finishes before moving on.
- Asynchronous - start the job and carry on; the result comes later.
- Message broker - a middleman that receives messages from senders (producers) and holds them until receivers (consumers) take them.
- Redis - a very fast in-memory data store, used here as the broker and result store (also as a cache).
- Celery - a Python library for a task queue: it runs functions in separate worker processes.
- Task - a Python function registered with Celery (
@celery.task). - Worker - a process that fetches tasks from the broker and runs them.
- Result backend - where task states and return values are stored.
- Producer - the code that sends a task (your Flask app).
- Polling - the client asks repeatedly "is it done yet?". Short polling asks on a timer.
- Celery beat - the scheduler that sends periodic tasks to the broker at the right times.
- Crontab - a schedule written with minute, hour, day fields, such as "every day at 18:00".
- Idempotent task - running it twice has the same effect as once.
- Webhook - the server calls a URL of the client when something happens (push, not poll).
Idea 1: Synchronous versus asynchronous, and why jobs leave the request
A Flask worker handles one request at a time (per worker process). A route that builds a PDF and sends an email takes, say, 12 seconds. During that time that worker is busy: other users queue up and the page feels dead, and the browser may time out. Using Celery does not make the job faster (it does not add CPU cores); it moves the job to another process so the web request can return in milliseconds.
# synchronous: the user waits 12 seconds, the server is blocked
@app.get("/report")
def report():
pdf = build_pdf() # 10 s
send_email(pdf) # 2 s
return {"done": True}
# asynchronous: respond at once, work happens elsewhere
@app.get("/report")
def report():
task = build_and_send.delay()
return {"task_id": task.id}, 202 # 202 Accepted: work has been queued
Good fits for Celery: sending emails, generating reports and CSV/PDF files, image or video processing, calling slow external APIs, nightly cleanups, periodic emails or reminders. Bad fits: handling real-time user interactions, generating the page the user is waiting for, serving live notifications (that is for WebSockets or Server-Sent Events).
Do not use time.sleep() as a trick to wait for a job inside a route: it blocks the worker, which is the very problem we are solving.
Idea 2: Message broker concept
Think of 50 servers that must talk to each other. Point-to-point: every pair needs its own connection, so the number of links grows as O(n squared). With a central broker: each server connects only to the broker, so links grow as O(n). This counting question appears again and again; remember: point-to-point O(n^2), broker O(n).
Benefits of a broker: it decouples producers and consumers (they need not run at the same time, or know each other), makes the system scalable (add more workers), absorbs traffic spikes (messages wait in the queue until processed) and enables batch processing. If the consumer is slower than the producer, messages accumulate in the queue (they are not dropped, the producer does not block). A topic is a named channel to which messages are published. A broker does not let servers talk "directly without an intermediary", that is the opposite.
Two delivery styles: push queues (the broker pushes to consumers, good for real-time) and pull queues (consumers fetch when ready, good for batch work). Celery workers pull.
Idea 3: Redis, the board in the kitchen
Redis is an in-memory data structure store. It keeps data in RAM (so it is extremely fast, hundreds of thousands of operations per second), is not a relational database and does not use SQL. It is used as a database, cache and message broker, supports Pub/Sub, and can be a Celery broker and a Celery result backend.
It is more than strings. Typical data types and commands (I checked the behaviour with a Redis emulation):
| Type | Commands | Meaning |
|---|---|---|
| String | SET name Asha, GET name, INCR visits | a value; numbers can be incremented atomically |
| List | LPUSH q a b, RPUSH q c, LRANGE q 0 -1, RPOP q | ordered; a natural queue |
| Set | SADD s x x y, SMEMBERS s | unique members: x stored once |
| Hash | HSET user:1 name A age 20, HGETALL user:1 | field-value map (like a small dict) |
| Sorted set | ZADD lb 10 a 5 b, ZRANGE lb 0 -1 WITHSCORES | members ordered by score (leaderboards) |
127.0.0.1:6379> SET name Asha
OK
127.0.0.1:6379> EXPIRE name 60
(integer) 1
127.0.0.1:6379> TTL name
(integer) 60 # seconds left; -1 = no expiry, -2 = key does not exist
127.0.0.1:6379> LPUSH q a b
(integer) 2
127.0.0.1:6379> LRANGE q 0 -1
1) "b"
2) "a"
LPUSH pushes to the left, so the last pushed (b) is first. A key with EXPIRE disappears by itself, which is how caches clean up. redis-cli opens the interactive shell; PING should answer PONG.
Persistence. Because data lives in memory, a restart would lose it unless persistence is on: RDB takes periodic snapshots to a file; AOF (append-only file) logs every write. Redis can use both. For Celery as a queue and cache, losing some data on restart is often acceptable; for your only copy of important data, keep it in the SQL database.
Python client: r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True), then r.set("k","v"), r.get("k"). Redis has numbered databases 0..15 inside one server; the URL redis://localhost:6379/1 means database 1. Redis also does not have to be the broker: Celery can use RabbitMQ and others, and a system can have several brokers.
Redis on Windows
Redis is built for Linux. On Windows: use WSL (install Ubuntu, then sudo apt install redis-server, sudo service redis-server start) or Docker (docker run -p 6379:6379 redis). Port 6379 is then reachable as localhost. Check with redis-cli ping.
Idea 4: Celery architecture and your first task
Flask app Redis (broker) Celery worker(s) Redis (backend)
(producer) --1--> [ task queue ] --2--> run task function --3--> [ state + result ]
| ^
+---------------------- 4. AsyncResult(id).state / .get() ---------+
| Component | Job |
|---|---|
| Producer / Celery app | your Flask code that calls .delay(); creates a message |
| Broker | stores messages until a worker takes them (Redis, RabbitMQ) |
| Worker | separate process; imports your tasks, runs them, can run in parallel with other workers |
| Result backend | stores state and return value (Redis, a database); optional but needed for .get() and status polling |
In the real paper the code Celery('tasks', broker='redis://localhost:6379/1', backend='redis://localhost:6379/2') is explained like this: the broker stores task requests until a worker picks and executes them; the backend stores the result after execution. They can use the same Redis server in different databases; nothing forces different ports. The broker does not "track which worker processes which task".
# tasks.py
from celery import Celery
import time
celery = Celery("tasks",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1")
@celery.task
def long_task():
time.sleep(10)
return "Task completed"
@celery.task
def add(x, y):
return x + y
Start the worker (in its own terminal, from the folder with tasks.py), then call from Python or Flask:
celery -A tasks worker -l info # for a Flask app: celery -A app.celery worker -l info
result = add.delay(4, 6)
print(result) # a task id string such as 9c1f...: it is an AsyncResult, NOT 10
print(type(result)) # <class 'celery.result.AsyncResult'>
print(result.status) # 'PENDING' right away (worker not finished or not started)
print(result.get(timeout=5)) # 10, waits up to 5 s for the worker
print(result.status) # 'SUCCESS'
check = add.delay(4, 6)
print(check.status) # (a)
print(check.get(timeout=5)) # (b)
(a) PENDING: .delay() returns immediately with an AsyncResult, the work has not finished. (b) 10 after the worker finishes. And for print(add.delay(4, 6)) you see a task id, never 10.
Calling long_task.delay() with workers running: the call returns at once; the task executes asynchronously and its result 'Task completed' is available after about 10 seconds. Tasks are asynchronous by default (not synchronous), can run on another machine than the Flask app, and need a running worker.
delay versus apply_async
task.delay(a, b) is a shortcut for task.apply_async(args=(a, b)). Use apply_async for options:
send_email.delay("a@x.com") # send now
send_email.apply_async(args=["a@x.com"], countdown=10) # run 10 s later
send_email.apply_async(args=["a@x.com"], eta=datetime_utc) # run at a time
send_email.apply_async(args=["a@x.com"], expires=60) # drop if not started in 60 s
With countdown=10 the message is sent to the queue immediately but the worker starts it only after the 10 seconds have passed (it does not wait 10 seconds before dispatching). .delay() dispatches immediately and does not wait for any user input.
Task states
| State | Meaning |
|---|---|
PENDING | not started, or unknown id (also what you see for a task id that never existed) |
STARTED | worker began (only recorded if task_track_started is on) |
RETRY | failed and will be retried |
SUCCESS | finished, .get() returns the value |
FAILURE | raised an exception; .get() re-raises it |
Idea 5: Celery with Flask, and the app context
The worker is a separate process. Your task often needs the database or current_app, which only exist inside a Flask app context. The standard pattern (from the Flask docs) makes every task run inside one:
# celery_app.py
from celery import Celery, Task
from flask import Flask
def celery_init_app(app: Flask) -> Celery:
class FlaskTask(Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery = Celery(app.name, task_cls=FlaskTask)
celery.config_from_object(app.config["CELERY"])
celery.set_default()
app.extensions["celery"] = celery
return celery
# app.py
app = Flask(__name__)
app.config["CELERY"] = dict(
broker_url="redis://localhost:6379/0",
result_backend="redis://localhost:6379/1",
timezone="Asia/Kolkata",
)
celery = celery_init_app(app)
I ran this pattern with eager mode: a task that returns current_app.name returned the app's name, proving the context works. Then start the worker with celery -A app.celery worker -l info (module app, the variable celery). Some course templates instead create the Celery object and then set celery.conf.update(...): check your course notes for the exact style. Rules:
- The worker must be able to import your tasks (
include=["tasks"]or put them in the module you pass to-A). - Restart the worker after you change task code; it loads code once.
- Arguments and return values travel as JSON, so pass simple things: ids, strings, numbers, not SQLAlchemy objects (
job_report.delay(user)is bad; passuser.idand load the user inside the task). - On Windows the default pool does not work well; start the worker with
celery -A app.celery worker -l info -P solo. WSL or Docker avoids this.
Idea 6: Scheduled (beat) versus on-demand tasks
| On-demand | Scheduled (periodic) | |
|---|---|---|
| Triggered by | your code calling .delay() (a user clicked "export") | the clock, via Celery beat |
| Example | export my data as CSV | daily reminder at 18:00, monthly report on the 1st |
| Processes needed | Redis + worker | Redis + worker + beat |
Celery beat is only a scheduler. At the due time it puts a message in the broker and stops. A worker must be running to pick it up. Without a worker the morning digest is never sent. (This exact claim appeared as a question: "the worker is optional, beat handles execution" is false.) You start beat separately: celery -A app.celery beat -l info. Do not schedule with @app.task_periodic (no such thing); a fixed-interval email needs Celery beat, no external scheduler.
from celery.schedules import crontab
celery.conf.beat_schedule = {
"daily-reminder": {
"task": "tasks.send_reminders",
"schedule": crontab(hour=18, minute=0), # every day 18:00
},
"monthly-report": {
"task": "tasks.monthly_report",
"schedule": crontab(minute=0, hour=8, day_of_month=1), # 1st of month, 08:00
},
"every-10-min": {
"task": "tasks.send_email",
"schedule": 600.0, # seconds
"args": ("admin@x.com",),
},
}
I printed these schedules: crontab(hour=18, minute=30) shows as 30 18 * * *; crontab(minute='*/10') is every 10 minutes; crontab(hour=7, minute=0, day_of_week='mon') is Mondays at 07:00. The task name is the module path plus function name (tasks.send_reminders). Set timezone so "18:00" means India time.
Your app has an "export CSV" button and a daily 6 pm reminder. List what must be running.
Need a hint?
Broker, worker, scheduler, web app.
Show the full solution
Redis (broker and backend), the Flask app, at least one Celery worker, and Celery beat (for the reminder). The export needs only the worker; the daily reminder needs beat to enqueue and the worker to run.
Idea 7: The async CSV export flow
Problem: exporting all of a user's data takes 20 to 40 seconds. A fetch('/static/data.csv') after a fixed setTimeout of 41 seconds is a bad design: the job may take longer, and the user waits blindly. The right design is trigger, build, poll, download:
- Browser calls
POST /api/export. Flask starts the task and returns202with a task id. - The task builds the file in the background and returns its filename.
- Browser short-polls
GET /api/export/<task_id>every few seconds until state isSUCCESS. - Browser then fetches the download link.
# tasks.py
import csv, os
from celery_app import celery
from models import Item
@celery.task
def export_items(user_id):
path = f"exports/items_{user_id}.csv"
os.makedirs("exports", exist_ok=True)
with open(path, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["id", "name"])
for it in Item.query.filter_by(user_id=user_id):
w.writerow([it.id, it.name])
return path
# routes
from celery.result import AsyncResult
@app.post("/api/export")
@jwt_required()
def start_export():
task = export_items.delay(int(get_jwt_identity()))
return {"task_id": task.id}, 202
@app.get("/api/export/<task_id>")
@jwt_required()
def export_status(task_id):
res = AsyncResult(task_id, app=celery)
if res.state == "SUCCESS":
return {"state": "SUCCESS", "download": f"/api/download/{task_id}"}
if res.state == "FAILURE":
return {"state": "FAILURE"}, 500
return {"state": res.state} # PENDING / STARTED / RETRY
// Vue method: short polling
async startExport() {
const r = await fetch('/api/export', { method: 'POST', headers: this.authHeader })
const { task_id } = await r.json()
const timer = setInterval(async () => {
const s = await (await fetch('/api/export/' + task_id, { headers: this.authHeader })).json()
if (s.state === 'SUCCESS') { clearInterval(timer); this.downloadUrl = s.download }
if (s.state === 'FAILURE') { clearInterval(timer); this.error = 'Export failed' }
}, 3000)
}
Short polling = asking on a fixed timer; it is the way to learn the state of an async task and trigger an action when done. Long polling = the server holds the request open until something happens, giving near real-time updates over plain HTTP (so "long polling cannot be done over HTTP" is false). A webhook is not short polling: the server pushes a message to a URL when an event happens. Server-Sent Events (SSE) keep one connection open for the server to push to the browser (one-way; Flask-SSE uses Redis as a shared channel).
Generate 1000 reports.
Approach 1: for user in users: job_report.delay(user.id) creates 1000 small tasks. Approach 2: one task job_report() loops over all users.
With more than one worker, approach 1 spreads work across workers and finishes sooner. With one worker, both take about the same time (a single worker runs them one after another; approach 1 only adds queue overhead). Likewise for sending bulk emails: one send_bulk_emails task is slower than many send_email(email) tasks when more than 2 workers exist, and about equal with a single worker. Many small tasks also fail independently: one bad email does not stop the others.
Idea 8: Retries, idempotency, failure handling, monitoring
@celery.task(bind=True, autoretry_for=(ConnectionError,), retry_backoff=True, max_retries=3)
def send_mail(self, to):
smtp_send(to)
# manual style
@celery.task(bind=True, max_retries=3)
def send_mail2(self, to):
try:
smtp_send(to)
except ConnectionError as exc:
raise self.retry(exc=exc, countdown=30) # try again in 30 s
- Retries: transient failures (network, SMTP) are retried a limited number of times. After
max_retriesthe state becomesFAILURE. - Idempotency: a message may be delivered twice (worker crash, retry). Design tasks so repeating them is harmless: check "already sent?" before sending, use
INSERT ... ON CONFLICTor unique keys, write output to a file named by task or user id so a rerun overwrites the same file instead of duplicating. - Failure handling: log exceptions, set a
FAILUREstate, show "Export failed, try again" in the UI; never let the polling loop run forever (set a maximum number of polls). - Pass ids not objects, and re-read the database inside the task so you work on fresh data.
- Monitoring: Flower is a web dashboard for Celery (
celery -A app.celery flower) showing workers, tasks and states.celery -A app.celery inspect activelists running tasks. - Queue growth: if tasks arrive faster than workers finish, messages accumulate in the queue; add workers (
-c 4concurrency, or more worker machines).
.delay()returns anAsyncResult(prints a task id), not the function's return value..statusright after.delay()isPENDING.apply_async(countdown=10)dispatches immediately and runs 10 s later.- Beat alone does not run tasks: a worker is needed.
- Celery does not need Redis: other brokers work; and Redis is not a relational database (no SQL).
- Passing a SQLAlchemy object as a task argument; use ids.
time.sleep()in a route is not asynchronous; it blocks.
Your project almost certainly needs a daily reminder and a monthly report, plus an export job. In the viva: "Show me the beat schedule", "What happens if the worker is down?" (messages wait in Redis until a worker starts), "Why is export a task?" (it takes long, the request would block), "How does the front end know it finished?" (polling a status endpoint). Keep a one-line start-up recipe: Redis, flask run, worker, beat.
A route calls report.delay() and then immediately returns {"ok": true}. The Celery worker is not running. What happens to the HTTP response and to the task?
Need a hint?
Where is the message stored meanwhile?
Show the full solution
The HTTP response still returns at once, because .delay() only puts a message in Redis. The task sits PENDING in the queue. When a worker starts later, it picks the message up and runs it.
Write a beat entry that runs tasks.weekly_digest every Monday at 07:30.
Need a hint?
Use crontab with day_of_week.
Show the full solution
celery.conf.beat_schedule = {
"weekly-digest": {
"task": "tasks.weekly_digest",
"schedule": crontab(hour=7, minute=30, day_of_week="mon"),
}
}Predict the output: RPUSH q a b c, then LPUSH q z, then LRANGE q 0 -1, then SADD s 1 1 2, then SCARD s.
Need a hint?
LPUSH adds at the head; sets keep unique members.
Show the full solution
LRANGE gives z a b c; SCARD s gives 2 (the member 1 was stored once).
Exam radar
About 39 distinct questions touch this week's keywords, almost all from the End Term (roughly 114 of 1471 End Term keyword matches, but only about 3 of 608 in Quiz 2, so this week is an End Term topic; counts are approximate and this week has fewer matched questions than week 7). The questions are mostly concept MCQ/MSQ, few code questions. They ask:
- Celery basics: it is a task queue, runs tasks asynchronously, needs a broker and workers, can schedule tasks; Redis is not the only broker; a system may have multiple brokers;
- delay versus apply_async(countdown=...), what
.delay()returns,PENDINGthen the value (a 2026 paper); - broker versus backend in
Celery(..., broker=..., backend=...); the role of Redis in Celery (store scheduled tasks, act as broker); - Beat: scheduling
send_emailevery 10 minutes; a 2026 question that beat needs a worker; - message broker theory: decoupling, scalability, O(n) versus O(n squared) connections, spike absorption, topics, slower consumer causes queue growth;
- polling: short versus long polling, webhook is not polling; the CSV-file question (poll the task state instead of
setTimeout); one big task versus many small tasks; why a slow Flask route blocks other users; - Redis facts: in-memory data structure store, cache and broker, pub/sub, not SQL, not strings only.
- "Redis is a relational database" and "Redis only stores strings": both false.
- "Message broker lets servers talk directly" is false.
- With one worker, one big task and many small tasks take about the same time.
- Wrong complexity pair: broker links grow as O(n), point-to-point as O(n squared).
@app.task_periodicand@app.on_periodicdo not exist.
Quick quiz
What will long_task.delay() do, when long_task sleeps 10 seconds and then returns 'Task completed', and workers are running?
- Block the caller for 10 seconds
- Return 'Task completed' at once
- Return immediately; the task runs in a worker and the result is available after about 10 seconds
- Fail, because tasks cannot sleep
.delay() only enqueues a message and returns an AsyncResult.You want a Celery task send_email to run every 10 minutes. How?
- With
@app.task_periodic - With
@app.on_periodic - With Celery beat and a beat schedule
- Celery cannot do it; use an external scheduler
Select all TRUE statements about Celery and Redis. (select all that apply)
- A Celery system needs worker processes.
- Redis can be a broker and a result backend.
- Redis must be used as the broker.
- Redis stores data with SQL.
In Celery('tasks', broker='redis://localhost:6379/1', backend='redis://localhost:6379/2'), what is the difference between broker and backend?
- The backend tracks which worker runs which task
- The broker holds task requests until a worker takes them; the backend stores the result afterwards
- They must run on different ports
- The backend is needed only for email tasks
Which is a TRUE statement about send_email.apply_async(countdown=10)?
- The message is sent after waiting 10 seconds
- The message is dispatched immediately and the worker starts the task after the 10 seconds
- It waits for user input
- It runs synchronously
countdown delays the start of execution, not the dispatch.A file is generated by a Celery task in 20 to 40 seconds. What is the best way for the page to fetch it?
- Call
setTimeoutfor 41 seconds, then fetch - Fetch the file in a tight loop forever
- Short-poll the task state every few seconds and fetch the file once it succeeds
- Block the Flask route until the file exists
Viva: say it out loud
Why did you use Celery in your project?
Some jobs, like exporting a CSV or sending monthly reports, take many seconds. If I did them inside the Flask request, the user would wait and the server would be blocked for others. With Celery the route queues the job and returns at once, and a worker does the job in the background.
Explain the roles of Redis, the worker and the backend.
My Flask app sends a task message to Redis, which acts as the broker and holds it in a queue. A Celery worker, which is a separate process, takes the message and runs the function. The result backend, also Redis in my setup, stores the state and return value, so the app can check the status later.
What is Celery beat and how is it different from a normal task?
Beat is a scheduler that puts periodic tasks, such as the daily reminder at 6 pm, into the broker at the right time using crontab schedules. A normal task is triggered by my code with .delay(). Beat does not run the task itself: a worker still has to be running.
How does the front end know that the CSV export is ready?
The export route returns a task id with status 202. The Vue app polls a status endpoint every few seconds, which uses AsyncResult(task_id).state. When it is SUCCESS, the response contains a download link.
What happens if the worker is down when a task is sent?
The message stays in the Redis queue and the task state stays PENDING. When a worker starts, it picks up the waiting messages and runs them. The web request itself was not affected.
What is idempotency and why does it matter for tasks?
An idempotent task gives the same result if it runs twice. Tasks can run twice after a retry or crash, so I check whether a reminder was already sent, or I overwrite the same export file, instead of creating duplicates.
What is Redis and what can it store?
Redis is an in-memory data structure store, very fast, used as a cache, a message broker and a database. It stores strings, lists, sets, hashes and sorted sets. It is not a SQL database. Data can be saved to disk with RDB snapshots or an append-only file.
How do you run Redis on Windows?
Redis is made for Linux, so I run it inside WSL or in Docker with docker run -p 6379:6379 redis. The Flask app and Celery then connect to localhost:6379.
Recap
- Slow work must leave the request: queue it, return
202quickly, let a worker do it. - A broker decouples producers and consumers; point-to-point links grow as O(n squared), with a broker as O(n); queues grow if consumers are slow.
- Redis is an in-memory data structure store (strings, lists, sets, hashes, sorted sets), usable as broker, result backend and cache; not SQL.
- Celery: app (producer), broker, worker, result backend.
@celery.task,.delay(),.apply_async(countdown=...),AsyncResult. .delay()returns anAsyncResultat once;.statusstartsPENDING;.get()waits for the value.- Flask integration needs the app context inside tasks; start
celery -A app.celery worker -l info(add-P soloon Windows). - Beat +
crontabschedules periodic tasks, but a worker still runs them. - CSV export: trigger, build in a task, poll status, download. Make tasks idempotent, pass ids, retry transient errors, watch with Flower.
celery = Celery("tasks", broker="redis://localhost:6379/0", backend="redis://localhost:6379/1")@celery.task/@celery.task(bind=True, max_retries=3);raise self.retry(exc=exc, countdown=30)t.delay(a, b)ist.apply_async(args=(a, b)); optionscountdown=,eta=,expires=res = AsyncResult(task_id, app=celery);res.state(PENDING,STARTED,RETRY,SUCCESS,FAILURE);res.get(timeout=5)celery -A app.celery worker -l info,celery -A app.celery beat -l info,celery -A app.celery flowercrontab(hour=18, minute=0);crontab(minute='*/10');crontab(hour=8, minute=0, day_of_month=1);crontab(hour=7, minute=30, day_of_week='mon')- Redis:
SET,GET,EXPIRE k 60,TTL k,INCR,LPUSH,RPUSH,LRANGE q 0 -1,SADD,SMEMBERS,HSET,HGETALL,ZADD,ZRANGE;redis-cli pinggivesPONG - Windows:
docker run -p 6379:6379 redisor Redis inside WSL
- MAD1 week 7 - Backend systems: indexing, search, caching and logging: MAD1's backend topics (logging, queues of work) grow into Redis and Celery.
Practise with real exam questions
These come straight from past papers (with the real figures and the official answer key). Open 60 real questions on this week All End Term papers