简体   繁体   中英

Threading database issues in celery tasks using eventlet

I have a project under dev VM 'ubuntu/trusty'. I use virtualenv with the following packages:

celery 3.1.23
eventlet 0.18.4
django 1.8.15
mysqlclient 1.3.9

Python version is 3.4.3. MySql version is 5.5.

I use supervisor with eventlet as a worker for run some services and celery tasks among them. This is the part of the supervisor's configuration:

[program:celery-worker-default]
command=/home/vagrant/.virtualenvs/meridian/bin/python /vagrant/meridian/meridian/manage.py celery worker --loglevel=INFO -P eventlet -c 3 -Q default -E -n default.queue
directory=/vagrant/meridian/meridian
user=vagrant
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/celery-worker-default-stdout.log
stderr_logfile=/var/log/supervisor/celery-worker-default-stderr.log
priority=999
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stderr_logfile_maxbytes=10MB
stderr_logfile_backups=5

When tasks are running they fail with the following stack trace:

Traceback (most recent call last):
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/celery/app/trace.py", line 231, in trace_task
    loader_task_init(uuid, task)
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/djcelery/loaders.py", line 114, in on_task_init
    self.close_database()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/djcelery/loaders.py", line 85, in close_database
    return self._close_database()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/djcelery/loaders.py", line 76, in _close_database
    close()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/django/db/__init__.py", line 64, in close_old_connections
    conn.close_if_unusable_or_obsolete()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/django/db/backends/base/base.py", line 403, in close_if_unusable_or_obsolete
    self.close()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/django/db/backends/base/base.py", line 191, in close
    self.validate_thread_sharing()
  File "/home/vagrant/.virtualenvs/meridian/lib/python3.4/site-packages/django/db/backends/base/base.py", line 421, in validate_thread_sharing
    % (self.alias, self._thread_ident, thread.get_ident()))
django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 140089123953704 and this is thread id 140088751748512.

Any task that contains DB operations fails.

I tried to use select_for_update() and with atomic(): in the task bodies, but without any success.

Also I searched in the Net about that issue, but have found no solution.

Does anybody know how to solve this?

Find something like on_load hook for Celery and put the following code there:

import eventlet
eventlet.monkey_patch(MySQLdb=True)

It has to be executed before any Django code.

I have found an answer. The issue was that I used to run celery under the manage.py managment script:

[program:celery-worker-default]
command=/home/vagrant/.virtualenvs/meridian/bin/python /vagrant/meridian/meridian/manage.py celery worker --loglevel=INFO -P eventlet -c 3 -Q default -E -n default.queue

I have rewritten that line in this way:

[program:celery-worker-default]
command=bash -c "/vagrant/meridian/meridian/start_default_queue.sh"

And this the content of start_default_queue.sh :

source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
source /home/vagrant/.virtualenvs/meridian/bin/activate
workon meridian
exec celery worker --loglevel=INFO -P eventlet -c 3 -Q default -E -n default.queue

I don't know why, but running celery worker directly have fixed that mysterious issue.

I just ran into the same problem and was able to fix it by adding

numprocs = 1

to the supervisord configuration for my celery service. In your example this could look like this:

[program:celery-worker-default]
command=/home/vagrant/.virtualenvs/meridian/bin/python /vagrant/meridian/meridian/manage.py celery worker --loglevel=INFO -P eventlet -c 3 -Q default -E -n default.queue
directory=/vagrant/meridian/meridian
numprocs = 1
…

It's not really clear to me why this changes anything though, because a value of 1 should also be the default.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM