ehfeng ehfeng - 4 months ago 40
Python Question

In flask's documentation for celery, why do celery tasks need names?

In the documentation, the @celery.task decorator is not passed arguments, yet in the GitHub example, it is named "tasks.add". Why is it? When I remove the name, the example no longer works, complaining of


KeyError: '__main__.add'



[1] http://flask.pocoo.org/docs/0.10/patterns/celery/
[2] https://github.com/thrisp/flask-celery-example/blob/master/app.py#L25

Answer

In the Flask documentation the task name was not set because the code is assumed to be inside a tasks module, so the task's name will be automatically generated as tasks.add, in the Celery docs:

Every task must have a unique name, and a new name will be generated out of the function name if a custom name is not provided

Check the Names section of the Celery docs for more info.

In the other example on Github, the author sets the name explicitly instead of relying on the automatic naming, which will be __main__.tasks if running as the main module, which is the case when running the Flask server.

Update on why you're having this issue:

The task is being sent from the function hello_world when you access the /test page by passing x and y:

res = add.apply_async((x, y))

Because the task add is inside the __main__ module it will be named __main__.add and sent to the worker with this name, but on the other hand the worker that you started using:

celery worker -A app.celery

Has this task registered as app.add that's why you're getting this error:

[2014-10-10 10:32:29,540: ERROR/MainProcess] Received unregistered task of type '__main__.add'.
The message has been ignored and discarded.

Did you remember to import the module containing this task?
Or maybe you are using relative imports?
Please see http://docs.celeryq.org/en/latest/userguide/tasks.html#task-names for more information.

The full contents of the message body was:
{'timelimit': (None, None), 'utc': True, 'chord': None, 'args': (2787476, 36096995), 'retries': 0, 'expires': None, 'task': '__main__.add', 'callbacks': None, 'errbacks': None, 'taskset': None, 'kwargs': {}, 'eta': None, 'id': '804e10a0-2569-4338-a5e3-f9e07689d1d1'} (218b)
Traceback (most recent call last):
  File "/home/peter/env/celery/lib/python2.7/site-packages/celery/worker/consumer.py", line 455, in on_task_received
    strategies[name](message, body,
KeyError: '__main__.add'

Check the output of the worker:

[tasks]
  . app.add
  . celery.backend_cleanup
  . celery.chain
  . celery.chord
  . celery.chord_unlock
  . celery.chunks
  . celery.group
  . celery.map
  . celery.starmap

Celery only sends the task name to the worker to execute it, so when you explicitly set the task name the hello_world function will send the task with this name which is registered in the worker.

Update:

The task name can be whatever you want, it can just be add, and your celery tasks don't have to be in a tasks module at all, to understand more about task names try this:

Remove the explicit task name and start a worker:

celery worker -A app.celery

in another terminal window, cd to the code directory and start an interactive python shell and try this:

>>> import app
>>> app
<module 'app' from 'app.pyc'>
>>> app.add
<@task: app.add of app:0xb6a29a6c>
>>> # check the name of the task
... app.add.name
'app.add'
>>> t = app.add.delay(2, 3)
>>> t.result
5

As you can see we didn't use an explicit name and it worked as expected because the name of task from where we sent it is the same as registered in the worker (see above).

Now back to why you got this error when you removed the task name, the task is sent from app.py right, in the same directory run this:

$ python -i app.py

Then interrupt the Flask server with Ctrl + C, and try this:

>>> add.name
'__main__.add'

As you can see this is why you got this error and not because you removed the task name.

Comments