Tasks#
The application uses Celery for asynchronous and periodic work. This page describes how Celery is configured, the queue / routing model, and gives a complete inventory of every scheduled task.
Per-module task references (auto-generated from docstrings) live with each app: see Tasks, Tasks, Tasks, Tasks, Tasks, Tasks, Tasks.
Configuration#
Broker: Redis –
redis://redis:6379/1(configurable via theREDIS_URLenv var).Result backend:
django-db– task results are persisted in the application database viadjango_celery_results.Beat scheduler:
django_celery_beat.schedulers:DatabaseScheduler– the schedule lives in the database; on startup it is seeded from thecelery_beatfixture (app/richy/core/fixtures/celery_beat.json).Serializer: JSON for both task arguments and results.
Beat#
Beat is started with:
$ celery -A richy beat -l error -S django
Notes:
-l error– log level iserrorso only failures are emitted.No
--pidfileis supplied. When beat hangs the stale PID file would otherwise block a fresh process from starting; omitting it side-steps that failure mode.-S django– use theDatabaseScheduler, so the schedule is managed in the Django admin and survives restarts.
Workers#
Three worker services are deployed, one per queue. Production
configuration (from compose.yaml.skel):
$ celery -A richy worker -c 2 -Q celery -O fair -l error --max-tasks-per-child=3
$ celery -A richy worker -c 1 -Q slow -O fair -l error --max-tasks-per-child=3
$ celery -A richy worker -c 2 -Q fast -O fair -l error --max-tasks-per-child=3
Notes:
-c N– concurrency. Theslowworker runs single-threaded so long scrapes cannot trample each other.-O fair– distribute tasks fairly to children rather than prefetching ahead, which suits long-running tasks.--max-tasks-per-child=3– recycle each worker process after three tasks. This bounds memory growth from libraries that leak (notably the scraping stack) and ensures a fresh interpreter for each batch.
Queues#
The application uses three queues:
celery– default queue for general tasksslow– long-running scraping / network-bound tasksfast– short tasks expected to complete in seconds
Routing#
A task can be routed to different queues depending on whether it is invoked directly or fired by beat. The current routing table:
task |
direct |
periodic (via beat) |
|---|---|---|
richy.core.tasks.generate_performance_charts |
celery |
celery |
richy.coins.tasks.fetch_historical_data |
celery |
slow |
richy.coins.tasks.fetch_basic_info |
fast |
celery |
richy.coins.tasks.fetch_current_price |
fast |
fast |
richy.shares.tasks.fetch_historical_data |
celery |
slow |
richy.shares.tasks.fetch_financial_data |
celery |
slow |
richy.shares.tasks.fetch_basic_info |
fast |
celery |
richy.shares.tasks.fetch_current_price |
fast |
celery |
richy.shares.tasks.fetch_dividends |
fast |
celery |
richy.shares.tasks.fetch_ratings |
fast |
fast |
richy.shares.tasks.fetch_price_ratings |
fast |
fast |
richy.etfs.tasks.fetch_historical_data |
celery |
celery |
richy.etfs.tasks.fetch_basic_info |
fast |
fast |
richy.etfs.tasks.fetch_current_price |
fast |
fast |
richy.etfs.tasks.fetch_holdings |
fast |
fast |
richy.etfs.tasks.fetch_dividends |
fast |
fast |
richy.indexes.tasks.fetch_historical_data |
celery |
celery |
richy.news.tasks.download_share_news |
slow |
slow |
richy.news.tasks.download_coin_news |
slow |
slow |
richy.news.tasks.download_index_news |
slow |
slow |
richy.news.tasks.download_etfs_news |
slow |
slow |
richy.transactions.tasks.generate_graphs |
fast |
– |
Note
The “direct” column is the queue set on the @app.task
decorator – i.e. the queue used when the task is invoked
programmatically (task.delay() / apply_async()).
The “periodic” column is the effective queue when beat fires
the task: the value from celery_beat.json if set, otherwise
the decorator’s queue. The fixture lives at
app/richy/core/fixtures/celery_beat.json and is editable
through the Django admin – changes are picked up by beat
without a restart.
richy.transactions.tasks.generate_graphs is not scheduled by
beat; it is invoked directly from application code.
Periodic schedule#
Every enabled periodic task in the system, sourced from the beat fixture, ordered by frequency:
Task |
Schedule |
Queue |
Purpose |
|---|---|---|---|
|
every 30 minutes |
celery |
Cached current share market price |
|
every 30 minutes |
fast |
Cached ETF market price |
|
every 30 minutes |
fast |
Cached coin market price |
|
every 2 hours |
slow |
Share news feed |
|
every 2 hours |
slow |
Coin news feed |
|
every 2 hours |
slow |
Index news feed |
|
every 2 hours |
slow |
ETF news feed |
|
every 3 hours |
slow |
Coin price history |
|
every 3 hours |
celery |
Generate per-item performance charts |
|
daily, 04:00 UTC |
celery |
Built-in result-backend cleanup |
|
daily, 05:00 UTC |
slow |
Daily share price history fetch |
|
daily, 06:00 UTC |
celery |
ETF price history |
|
daily, 06:00 UTC |
celery |
Index price history |
|
every 1 day |
slow |
Earnings / revenue / EPS |
|
every 1 day |
celery |
Basic share info into |
|
every 1 day |
celery |
Share dividend records |
|
every 1 day |
fast |
Analyst ratings |
|
every 1 day |
fast |
Analyst price targets |
|
every 1 day |
celery |
Basic coin info |
|
every 1 day |
fast |
Basic ETF info |
|
every 1 day |
fast |
ETF holdings composition |
|
every 1 day |
fast |
ETF dividend records |
Note
The fixture also contains an enabled entry for
richy.trends.tasks.fetch_trends_chart_data (daily, 06:00 UTC)
pointing to a Django app (trends) that no longer exists.
Beat will attempt to dispatch it and the worker will fail to
import. This fixture entry should be removed – a code change,
tracked separately from these docs.