Search code examples
pythontornadoansible

How can I use tornado_mysql from a non tornado python script?


I have a tornado application that launches a non tornado process with tornado.process.Subprocess (specifically ansible-playbook). I have a callback plugin in ansible-playbook that runs database queries at various stages. From the plugin, I want to use the same tornado_mysql library I am using in my tornado process. I was able to do this with tornado's run_sync() method:

callback_plugin.py:

import db.py
from tornado import ioloop
io_loop = ioloop.IOLoop.instance()
def playbook_on_play_start(self, play):
    io_loop.run_sync(lambda: db.update_job_play(job_id, play))

db.py:

def get_conn():
    return pools.Pool(
        connect_kwargs={
            'host': settings.get('mysql_host'),
            'port': settings.get('mysql_port'),
            'user': settings.get('mysql_username'),
            'passwd': settings.get('mysql_password'),
            'db': settings.get('mysql_database_name'),
            'charset': 'utf8mb4',
            'init_command': 'SET NAMES \'utf8mb4\'',
            'cursorclass': DictCursor
        },
        max_idle_connections=settings.get('mysql_max_idle_connections'),
        max_open_connections=settings.get('mysql_open_connections')
    )

@tornado.gen.coroutine
def update_job_play(job_id, play):
    sql = """
    UPDATE `job`
    SET `current_play` = %(play)s
    WHERE `id` = %(job_id)s
    """
    yield get_conn().execute(sql, {
        'job_id': job_id,
        'play': play})

It works, and the database is updated, but I get this error:

 ERROR:tornado.application:Exception in callback None        
 Traceback (most recent call last):
    File "/usr/local/lib/python2.7/site-packages/tornado/ioloop.py", line 882, in start
      fd_obj, handler_func = self._handlers[fd]
  KeyError: 13
  ERROR:tornado.application:Exception in callback None
  Traceback (most recent call last):
    File "/usr/local/lib/python2.7/site-packages/tornado/ioloop.py", line 882, in start
      fd_obj, handler_func = self._handlers[fd]
  KeyError: 13
  ERROR:tornado.application:Exception in callback None

I found this: Tornado IOLoop Exception in callback None in Celery worker which seems related. ansible-playbook is a separate process from the main tornado application, but then ansible_playbook also forks a new thread for each host it connects to.

My question is, how do I get rid of that error?


Solution

  • You can't share IOLoops across threads, which is what you're doing by calling IOLoop.instance() in the playbook thread. Instead, create a new IOLoop each time:

    def playbook_on_play_start(self, play):
        with contextlib.closing(tornado.ioloop.IOLoop()) as io_loop:
            io_loop.run_sync(lambda: db.update_job_play(job_id, play))