Cron-based scheduling #8
Replies: 6 comments 8 replies
-
I've mentioned this in issue #22. I think this is a vital feature for a 1.0 release. Even the database backend for django-tasks itself needs it, for expiring old tasks from the database. |
Beta Was this translation helpful? Give feedback.
-
Any thought to if scheduling should be possibly database backed? Similar to how django-celery-beat works? Or just in code registration? One thing I would like would be a syntax similar to Laravel, it just reads better compared to cron syntax: ScheduleTask("tasks.send_email").daily().weekdays().at("01:30") |
Beta Was this translation helpful? Give feedback.
-
You could have a standard managment command "cron_task" with "queue name" as argument that an external process can run. Then you don't have to think about scheduling syntax at all and leave that to cron/systemd/k8s/whatever. |
Beta Was this translation helpful? Give feedback.
-
I've implemented a very hacky task scheduler, to avoid having to have a separate container just for scheduling, using a view middleware and the Django cache. This does add overhead to my page loads, but I have a very small throughput of requests, and pay for my hosting by the instance, so this makes sense for me for now. Might be useful to someone else! from datetime import UTC, datetime, timedelta
from typing import Any
from django.core.cache import cache
from django.utils import timezone
from django_tasks import Task
from .tasks import a_task_that_runs_every_hour, a_task_that_runs_every_day
TASK_SCHEDULE: tuple[tuple[Task[Any, Any], int]] = (
(a_task_that_runs_every_hour, 1),
(a_task_that_runs_every_day, 24),
)
class ScheduledTaskMiddleware:
"""
Hacky task scheduler, to avoid having to have another server
ONLY SAFE TO BE USED WITH EXACTLY 1 WEB SERVER, AND 1 TASK SERVER
Configure by adding tasks, and how many hours between each run, to the TASK_SCHEDULE variable above
Stores time since last run of each task in Django cache
this is wiped on server restart, so all tasks will run on each restart
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
for task, hours_between_runs in TASK_SCHEDULE:
cache_key = f"scheduled_task_middleware_{task.name}_last_started"
# Run the task on first request, or after the allotted time has expired
task_last_started_timestamp = cache.get(cache_key)
if task_last_started_timestamp is not None:
task_last_started = datetime.fromtimestamp(
task_last_started_timestamp,
tz=UTC,
)
if (timezone.now() - task_last_started) < timedelta(
hours=hours_between_runs,
):
continue
task.enqueue()
cache.set(cache_key, timezone.now().timestamp())
return response |
Beta Was this translation helpful? Give feedback.
-
I very much like the way that huey handled periodic tasks with the crontab function included. https://huey.readthedocs.io/en/latest/guide.html#periodic-tasks You can build on this fairly easily by having a scheduled task run every minute that is responsible for scheduling any tasks that were missed or that need to run in the next minute. This allows for running tasks as many times per minute as you need and adjustments using It is also possible to use a custom function that decides to run much less often if crontab doesn't fit your scheduling requirements. The one thing that I don't enjoy with that design is the separation between periodic and normal tasks. It makes sense why they are different and not precisely interchangeable though. The coordination between consumer processes could be improved as well. Consumers should really communicate and select which is responsible for scheduling new tasks into the queue without manual intervention. I want to run as many consumers as I prefer without multiple tasks being injected into the queue for the same timestamp. |
Beta Was this translation helpful? Give feedback.
-
I really wanted to run a few cron tasks for a personal project so I "forked" the db_worker command and added a few lines to enqueue tasks defined in a CRON_TASKS = [
("apps.tasks.send_daily_report", "0 9 * * *"), # daily at 9 AM
("apps.tasks.backup_database", "0 0 * * 0"), # weekly on Sunday at midnight
("apps.tasks.cleanup_old_files", "*/30 * * * *"), # every 30 minutes
("apps.tasks.sync_external_data", "0 */4 * * *"), # every 4 hours
("apps.tasks.send_reminders", "0 12 * * 1-5"), # weekdays at noon
("apps.tasks.update_cache", "*/15 * * * *"), # every 15 minutes
("apps.tasks.generate_reports", "0 1 1 * *"), # monthly on 1st at 1 AM
]
Appreciation
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
It's currently possible to execute a task after a given time with
run_after
. However, it's not possible to schedule a task "At 3am every day", for example.Beta Was this translation helpful? Give feedback.
All reactions