Skip to main content

Documentation 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.

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 via 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.
The Celery application is loaded from the 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

1

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).
celery -A trust_ride worker --loglevel=info
2

Start Celery Beat (scheduler)

Beat must run as a single instance. Running more than one Beat process will fire duplicate tasks.
celery -A trust_ride beat \
  --loglevel=info \
  --scheduler django_celery_beat.schedulers:DatabaseScheduler
3

Combined mode (development only)

For local development you can combine the worker and Beat scheduler into one process. Never use this in production.
celery -A trust_ride worker --beat --loglevel=info

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:
  1. Release expired reserved seats — calls rides.utils.release_expired_bookings(), which finds all bookings in reserved or pending_verification status whose payment_expiry timestamp has passed. For each expired booking it increments trip.available_seats (inside a SELECT FOR UPDATE transaction to prevent race conditions) and deletes the booking record. After each release, it calls notify_next_in_waitlist(trip) to alert the next person in the queue.
  2. Archive old inquiry threads — any InquiryThread that has had no activity for 7 days is marked is_archived=True.
  3. Delete stale chat messagesChatMessage records older than 30 days are deleted, excluding those tied to bookings with a PENDING refund. Cloudinary-hosted receipt images are deleted from storage before the database record is removed.
# apps/chat/tasks.py
@shared_task
def expire_bookings_and_cleanup():
    released = release_expired_bookings()
    # archive threads inactive > 7 days
    # delete messages older than 30 days (skip open refunds)

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 published trip whose departure_date is today and departure_time is at or before the current time is transitioned to in_progress.
  • In Progress → Completed — any in_progress trip whose estimated_arrival_time has passed is marked completed and is_active is set to False. All confirmed bookings on that trip are also marked completed.
# apps/rides/tasks.py
@shared_task
def update_trip_statuses():
    now = timezone.now()
    # published → in_progress when departure_time reached
    # in_progress → completed when estimated_arrival_time passed
    # marks associated confirmed bookings as completed
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)

When expire_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_minutes app setting (default: 15 minutes).

Celery Beat Schedule

Periodic tasks are defined in settings.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:
CELERY_BEAT_SCHEDULE = {
    'expire-bookings-and-cleanup': {
        'task': 'apps.chat.tasks.expire_bookings_and_cleanup',
        'schedule': 120.0,  # every 2 minutes
    },
    'update-trip-statuses': {
        'task': 'apps.rides.tasks.update_trip_statuses',
        'schedule': 60.0,   # every 1 minute
    },
}
To add a new periodic task via the admin, navigate to Django Admin → Periodic Tasks → Add Periodic Task and fill in the task path, interval, and enabled state. The Beat process picks up changes from the database on each tick without requiring a restart.

Redis Configuration

Celery reads its broker and result backend URLs from environment variables. The defaults in settings.py point to a local Redis instance:
CELERY_BROKER_URL    = os.getenv('CELERY_BROKER_URL',    'redis://localhost:6379')
CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', 'redis://localhost:6379')

CELERY_ACCEPT_CONTENT    = ['json']
CELERY_TASK_SERIALIZER   = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE          = 'Africa/Lagos'
CELERY_BEAT_SCHEDULER    = 'django_celery_beat.schedulers:DatabaseScheduler'
Set both variables in your .env file for production:
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0
Using separate Redis database indexes (e.g. /0 for the broker, /1 for results) is optional but keeps key namespaces clean.

Task Monitoring

Celery stores task results in Redis (configured via CELERY_RESULT_BACKEND). You can inspect task state and history using Flower (see the tip below) or by querying Celery directly from the Django shell:
from celery.result import AsyncResult

result = AsyncResult("your-task-id")
print(result.status)   # SUCCESS, FAILURE, PENDING, etc.
print(result.result)   # return value or exception
The periodic task schedule is managed by django-celery-beat and is visible in Django admin at:
/admin/django_celery_beat/periodictask/
Use Flower for real-time visibility in production. Flower is a web-based Celery monitoring tool that shows active workers, task throughput, failure rates, and individual task details in a live dashboard. Install it with pip install flower and start it with:
celery -A trust_ride flower --port=5555
Restrict access to Flower behind Nginx basic auth or a VPN — it exposes task arguments and results which may contain booking data.

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.

Build docs developers (and LLMs) love