"commands out of sync; you can't run this command now" on first use of cursor

63 Views Asked by At

I am using MySQL in Django (Python).

The draft version of this code worked and was stable, but it created cursors without closing them, and I just rewrote it to manage the cursor with enter/exit. Now it is unstable.

class Cursor:

  def __init__ (self, db):
    self.db     = db
    self.cursor = db.DB_connection.cursor ()

  def __enter__ (self):
    return self

  def __exit__ (self, ex_type, ex_value, ex_traceback):
    if ex_value is not None:
      # log stuff
    self.cursor.close ()
    self.cursor = None

  def __del__ (self):
    if self.cursor is not None:
      # log stuff
      self.cursor.close ()

  def one (self, query, args):
    self.cursor.execute (query, args)
    return self.cursor.fetchone ()

class DB:

  def __init__ (self, my_stuff):
    self.DB_connection = connect (...)

  def get (self):
    return Cursor (self)

and in the application:

with DB_connection.get() as db:
  result = db.one ("SELECT ...", ...)

Sometimes this works as before, but sometimes randomly it will fail calling db.one()

_mysql_connector.MySQLInterfaceError: Commands out of sync; you can't run this command now

this exception is seen in Cursor's __exit__

Googling this error tells me that this means an earlier SELECT on that cursor still has un-fetched results. But this is nonsense since with DB_connection.get() as db creates a new cursor.

Also sometimes the process simply exists without printing any exception info. Docker log looks like this

www_1   | "GET /test_page HTTP/1.1" 200 16912
docker_www_1 exited with code 245

These crashes are non-deterministic even though the code in the view which creates the cursor is entirely deterministic.

I have added some print statements which show that there are only ever 0 or 1 cursors simultaneously in existence during the flow of the application.

In the draft version, instead of

with DB_connection.get() as db:
  result = db.one ("SELECT ...", ...)

it would have been something like

db = DB_connection.get()
result = db.one ("SELECT ...", ...)

And that was very stable, although it leaks the cursor resource. I don't think anything else significantly changed since it was stable, other than wrapping the cursor in enter/exit.

Is this the correct way to use the API?

0

There are 0 best solutions below