TrustRide runs several time-sensitive operations that cannot block an HTTP request — releasing seats when payment windows expire, transitioning trip statuses as departures approach, and purging stale chat messages. All of these are handled by Celery, an asynchronous task queue backed by Redis. Celery Beat acts as the in-process cron daemon, triggering periodic tasks on configurable schedules. Task results are stored in Redis viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Muhammadbugaje/trustride/llms.txt
Use this file to discover all available pages before exploring further.
CELERY_RESULT_BACKEND, and the periodic schedule is managed by django-celery-beat using the DatabaseScheduler so schedules can be edited in Django admin without a restart.
Task Architecture
Celery Worker
Consumes tasks from the Redis broker queue and executes them. In production, one or more worker processes run as a systemd service.
Celery Beat
A single scheduler process that enqueues periodic tasks on their configured intervals. Uses
django_celery_beat.schedulers:DatabaseScheduler so schedules can be edited via Django admin without a restart.Redis Broker
Stores the task queue. The
CELERY_BROKER_URL setting points Celery at the same Redis instance used for channel layers.Redis Result Backend
Task return values and status (
SUCCESS, FAILURE, PENDING) are stored in Redis via CELERY_RESULT_BACKEND. The django-celery-beat package manages the periodic schedule in the database, editable from Django admin.trust_ride package. Both task modules (apps.rides.tasks and apps.chat.tasks) use the @shared_task decorator so they do not need to import the app instance directly.
Starting Workers
Start the Celery worker
The worker process picks up tasks from the Redis queue and executes them. Run this in a terminal or as a systemd service (see the Setup guide).
Start Celery Beat (scheduler)
Beat must run as a single instance. Running more than one Beat process will fire duplicate tasks.
Key Tasks
apps.chat.tasks.expire_bookings_and_cleanup
Schedule: every 120 seconds (2 minutes)
This is TrustRide’s primary housekeeping task. It performs three operations in a single run:
-
Release expired reserved seats — calls
rides.utils.release_expired_bookings(), which finds all bookings inreservedorpending_verificationstatus whosepayment_expirytimestamp has passed. For each expired booking it incrementstrip.available_seats(inside aSELECT FOR UPDATEtransaction to prevent race conditions) and deletes the booking record. After each release, it callsnotify_next_in_waitlist(trip)to alert the next person in the queue. -
Archive old inquiry threads — any
InquiryThreadthat has had no activity for 7 days is markedis_archived=True. -
Delete stale chat messages —
ChatMessagerecords older than 30 days are deleted, excluding those tied to bookings with aPENDINGrefund. Cloudinary-hosted receipt images are deleted from storage before the database record is removed.
apps.rides.tasks.update_trip_statuses
Schedule: every 60 seconds (1 minute)
Keeps trip lifecycle state in sync with the clock without any manual driver intervention:
- Published → In Progress — any
publishedtrip whosedeparture_dateis today anddeparture_timeis at or before the current time is transitioned toin_progress. - In Progress → Completed — any
in_progresstrip whoseestimated_arrival_timehas passed is markedcompletedandis_activeis set toFalse. All confirmed bookings on that trip are also markedcompleted.
Both tasks use
timezone.now() with TrustRide’s configured timezone of Africa/Lagos (TIME_ZONE = 'Africa/Lagos' in settings.py). Ensure your server’s system clock is correct and that Redis is on the same network to avoid clock-skew issues.Waitlist notification (utility, called inline)
Whenexpire_bookings_and_cleanup releases a seat, it immediately calls apps.interest.utils.notify_next_in_waitlist(trip). This function:
- Finds the next user in the waitlist queue for that trip.
- Creates an in-app notification and sends an email (rendered from
emails/waitlist_alert.html). - Sets a claim window — the number of minutes the user has to book before the seat is offered to the next person — controlled by the
waitlist_claim_minutesapp setting (default: 15 minutes).
Celery Beat Schedule
Periodic tasks are defined insettings.py under CELERY_BEAT_SCHEDULE and loaded by the database scheduler. The database scheduler means you can also add, disable, or reschedule tasks directly in Django admin under Periodic Tasks, without editing code.
The built-in schedule from settings.py:
Redis Configuration
Celery reads its broker and result backend URLs from environment variables. The defaults insettings.py point to a local Redis instance:
.env file for production:
/0 for the broker, /1 for results) is optional but keeps key namespaces clean.
Task Monitoring
Celery stores task results in Redis (configured viaCELERY_RESULT_BACKEND). You can inspect task state and history using Flower (see the tip below) or by querying Celery directly from the Django shell:
django-celery-beat and is visible in Django admin at:
Production Considerations
Run as systemd services
Both the Celery worker and Celery Beat should run as dedicated systemd units. See the Setup guide for ready-to-use unit files that restart on failure and log to the journal.
One Beat, many workers
Run exactly one Celery Beat instance across your entire deployment. Scale the Celery worker by increasing the
--concurrency flag or by adding worker nodes. Beat only enqueues — workers do the heavy lifting.Scale by booking volume
During peak booking periods (e.g. public holidays), increase worker concurrency:
celery -A trust_ride worker --concurrency=4. Monitor queue depth in Flower to decide when to scale.Result backend
Task results are stored in Redis via
CELERY_RESULT_BACKEND. Set a Redis key expiry or use Celery’s result_expires setting to prevent unbounded memory growth. For durable result storage that survives a Redis restart, consider switching CELERY_RESULT_BACKEND to a database URL using django-celery-results.