Roman Storozhenko Roman Storozhenko - 1 month ago 19
Python Question

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 ?

Answer

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.