Asynchronous Task Queues
Kay Ashaolu - Instructor
Aishwarya Sriram - TA
Chapter 2: A Full Python Refresher
Object-Oriented Programming Fundamentals
- Transition from procedural to object-based design
- Emphasis on encapsulation, inheritance, and composition
- Practical coding examples for real-world analogies
Introduction to Object-Oriented Programming (OOP)
- OOP models real-world entities (students, devices, etc.)
- Shifts code from procedural (functions + data) to encapsulated objects
- Improves code organization & readability
Data: Dictionary vs. Object
Traditional Approach:
student = {"name": "Rolf", "grades": (90, 88, 87)}
def average(seq):
return sum(seq) / len(seq)
print(average(student["grades"]))
Limitation: Lacks semantic connection between data and behavior
Defining a Python Class
- Use the
class
keyword to create a blueprint - The
__init__
method initializes instance attributes - The
self
parameter references the instance
class Student:
def __init__(self):
self.name = "Rolf"
self.grades = (90, 88, 87)
Adding Behavior with Methods
- Methods are functions defined within a class
- Access & modify instance attributes via
self
- Encapsulates data and functionality together
class Student:
def __init__(self):
self.name = "Rolf"
self.grades = (90, 88, 87)
def average_grade(self):
return sum(self.grades) / len(self.grades)
Example: Calculating a Student's Average
- Create a Student object and call its method
student = Student()
print(student.average_grade()) # Outputs: 88.33...
- Emphasizes encapsulated data & behavior
Class Inheritance in Python
- Inheritance allows a class to derive properties and methods from another
- Models “is-a” relationships (e.g., Printer is a Device)
- Reduces redundancy by reusing code
class Device:
def __init__(self, name, connected_by):
self.name = name
self.connected_by = connected_by
self.connected = True
def __str__(self):
return f"device {self.name} {self.connected_by}"
def disconnect(self):
self.connected = False
print("disconnected")
Extending with the Printer Class
- Printer inherits from Device and adds extra features
class Printer(Device):
def __init__(self, name, connected_by, capacity):
super().__init__(name, connected_by)
self.capacity = capacity
self.remaining_pages = capacity
def __str__(self):
return f"{super().__str__()} - remaining pages {self.remaining_pages}"
def print_pages(self, pages):
if not self.connected:
print("printer is not connected")
return
print(f"printing {pages} pages")
self.remaining_pages -= pages
Class Composition vs. Inheritance
When to Use Composition
-
Conceptual Clarity:
- A Book is not a Bookshelf.
- Bookshelf has-a collection of Book objects, rather than being one.
-
Technical Benefits:
- Modularity: Changes in one component (e.g., Book) do not force changes in the container (Bookshelf).
- Flexibility: Easier to mix and match behaviors without rigid parent-child constraints.
- Reduced Coupling: Keeps classes focused on their primary responsibilities..
Class Composition vs. Inheritance
When to Use Composition
-
Example Comparison:
- Inheritance: A Book inheriting from Bookshelf forces unnecessary attributes.
- Composition: A Bookshelf holds Book objects, reflecting real-world relationships.
Composition Example: Bookshelf & Book
- Using Composition:
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"Book: {self.title}"
class Bookshelf:
def __init__(self, *books):
self.books = books
def __str__(self):
return f"Bookshelf with {len(self.books)} books"
- Clear separation: A bookshelf contains books; a book remains an independent entity.
Summary: Chapter 2 (Python OOP)
- Transition from dictionaries to objects
- Use of methods, inheritance, and composition
- Composition offers flexibility and modularity over inheritance in many scenarios
Chapter 12: Task Queues with rq & Sending Emails
Background Processing in Web Architecture
- Offload heavy or time-consuming tasks
- Enhance API responsiveness by processing tasks asynchronously
- Use of Redis as a message broker with the rq library
What is a Queue Data Structure?
-
Definition:
- A queue is a First-In-First-Out (FIFO) data structure
- Items are added at the rear and removed from the front
What is a Queue Data Structure?
-
Comparison:
- Dictionary: Key-value mapping with fast lookup
- Array (List): Ordered collection accessed by index
- Queue: Enforces order for processing tasks sequentially
What is a Queue Data Structure?
-
Real-World Analogy:
- Think of a queue as a line at a ticket counter: first come, first served.
Setting Up Redis for Task Queues
- Redis acts as an in-memory data store and message broker
- Use Render.com or Docker to host Redis
# Example Docker command to run Redis locally:
docker run -p 6379:6379 redis
Integrating rq with a Flask Application
- rq (Redis Queue): A Python library for managing task queues
- Enqueue tasks from your Flask app to be processed asynchronously
- Steps include:
- Installing rq (
pip install rq
) - Connecting to Redis
- Enqueuing background tasks (e.g., sending emails)
- Installing rq (
Code Example: Enqueueing Tasks
- tasks.py: Define the email sending task
import os
from dotenv import load_dotenv
load_dotenv()
def send_user_registration_email(email, username):
# Simulated email sending function
print(f"Sending registration email to {email} for {username}")
Flask App Integration with rq
- app.py: Connect Flask with Redis and enqueue tasks
import os
import redis
from rq import Queue
from flask import Flask, request, current_app
from tasks import send_user_registration_email
app = Flask(__name__)
connection = redis.from_url(os.getenv("REDIS_URL"))
app.queue = Queue('emails', connection=connection)
@app.route('/register', methods=['POST'])
def register():
# ... (user registration logic)
email = request.form['email']
username = request.form['username']
current_app.queue.enqueue(send_user_registration_email, email, username)
return "User created successfully", 201
Processing Background Tasks with rq Worker
- Run the worker as a separate process to consume queued tasks
- The worker monitors the Redis queue and processes tasks asynchronously
# Docker example command:
docker run -w /app rest-api-recording-email sh -c "rq worker -u $REDIS_URL emails"
Recap: Chapter 12 (Task Queues)
- Task Queue: Offloads heavy tasks to improve API responsiveness
- Redis: In-memory data store serving as the broker
- rq Library: Simplifies task management and background processing
- Workflow: Enqueue tasks from Flask → Worker processes tasks → e.g., Sending emails
Questions?
Async Task Queues - Backend Webarch
By kayashaolu
Async Task Queues - Backend Webarch
Course Website: https://groups.ischool.berkeley.edu/i253/sp25
- 85