Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When I use Django Celery apply_async with eta, it does the job immediately

i looked at celery documentation and trying something from it but it not work like the example. maybe i'm wrong at some point, please give me some pointer if i'm wrong about the following code

in views.py i have something like this:

class Something(CreateView):
  model = something

  def form_valid(self, form):
    obj = form.save(commit=False)
    number = 5
    test_limit = datetime.now() + timedelta(minutes=5)
    testing_something.apply_async((obj, number), eta=test_limit)
    obj.save()

and in celery tasks i wrote something like this:

@shared_task()
def add_number(obj, number):
    base = Base.objects.get(id=1)
    base.add = base.number + number
    base.save()
return obj

my condition with this code is the celery runs immediately after CreateView runs, my goal is to run the task add_number once in 5 minutes after running Something CreateView. Thank You so much

Edit:

  1. i've tried change the eta into countdown=180 but it still running function add_number immediately. i also tried longer countdown but still running immediately
  2. i've tried @johnmoustafis answer but still the same, the task run immediately
  3. i've also tried @dana answer but it still the same, the task run immediately
like image 513
knightzoid Avatar asked Jul 13 '17 14:07

knightzoid


People also ask

How does Celery delay work?

Celery provides two function call options, delay() and apply_async() , to invoke Celery tasks. delay() has comes preconfigured and only requires arguments to be passed to the task — that's sufficient for most basic needs. Apply_async is more complex, but also more powerful then preconfigured delay.

How does Django Celery work?

If a long process is part of your application's workflow, you can use Celery to execute that process in the background, as resources become available, so that your application can continue to respond to client requests. This keeps the task out of the application's context.

How do you call Celery tasks?

If the task isn't registered in the current process you can use send_task() to call the task by name instead. So delay is clearly convenient, but if you want to set additional execution options you have to use apply_async .

How do I perform tasks asynchronously in Django?

To perform tasks asynchronously, we use a task queue to queue all pending tasks. In our case, we will use Celery, an asynchronous task queue based on distributed message passing and Redis as the message broker. To integrate Celery with Django, create a __init__.py in the project root directory.

How do I integrate celery with Django?

In our case, we will use Celery, an asynchronous task queue based on distributed message passing and Redis as the message broker. To integrate Celery with Django, create a __init__.py in the project root directory.

How to define an async task in celery?

To define an async task, simply import and add the decorator @shared_task to each method or function. You may find some tutorial suggest you to define all async task in a separate module. Well, it is entirely up to you, as by enabling autodisover, Celery will look for all task with @shared_task decorator.

How do I use celery tasks in Python?

Celery Tasks. Celery utilizes tasks, which can be thought of as regular Python functions that are called with Celery. For example, let’s turn this basic function into a Celery task: def add(x, y): return x + y. First, add a decorator: from celery.decorators import task @task(name="sum_two_numbers") def add(x, y): return x + y.


2 Answers

Celery by default uses UTC time.
If your timezone is "behind" the UTC (UTC - HH:MM) the datetime.now() call will return a timestamp which is "behind" UTC, thus causing your task to be executed immediately.

You can use datetime.utcnow() instead:

test_limit = datetime.utcnow() + timedelta(minutes=5)

Since you are using django, there exist another option:

If you have set the USE_TZ = True in your setting.py, you have enabled the django timezone settings and you can use timezone.now() instead of datetime.utcnow():

from django.utils import timezone

...

test_limit = timezone.now() + timedelta(minutes=5)
like image 179
John Moutafis Avatar answered Sep 21 '22 13:09

John Moutafis


'test_limit' variable hasn't got timezone information. So Celery will understand eta param as UTC time.

Please use modified code:

class Something(CreateView):
    model = something

    def form_valid(self, form):
        obj = form.save(commit=False)
        number = 5

        test_limit = datetime.now()
        test_limit = test_limit.replace(tzinfo=tz.tzlocal())
        test_limit = test_limit + timedelta(minutes=5)

        testing_something.apply_async((obj, number), eta=test_limit)
        obj.save()
like image 23
aladeck Avatar answered Sep 20 '22 13:09

aladeck