Search code examples
pythonmysqlmysql-connectormysql-connector-python

Python Exception: RuntimeError: maximum recursion depth exceeded/unprintable runtime error


I'm getting this exception randomly on a script that receives items to insert into a mysql database using mysql-connector:

Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 77, in emit
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 77, in emit
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 79, in emit
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 79, in emit
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 79, in emit
    logging.FileHandler.emit(self, record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 930, in emit
    StreamHandler.emit(self, record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 874, in emit
    self.handleError(record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 801, in handleError
    None, sys.stderr)
  File "/opt/python/current2/lib/python2.7/traceback.py", line 124, in print_exception
    _print(file, 'Traceback (most recent call last):')
RuntimeError: maximum recursion depth exceeded
Logged from file foo.py, line 47

I also occasionally get:

Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 77, in emit
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 77, in emit
    if self.shouldRollover(record):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 156, in shouldRollover
    msg = "%s\n" % self.format(record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 723, in format
    return fmt.format(record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 464, in format
    record.message = record.getMessage()
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 322, in getMessage
    if not isinstance(msg, basestring):
Traceback (most recent call last):
  File "/opt/python/current2/lib/python2.7/logging/handlers.py", line 79, in emit
    logging.FileHandler.emit(self, record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 930, in emit
    StreamHandler.emit(self, record)
  File "/opt/python/current2/lib/python2.7/logging/__init__.py", line 874, in emit
    self.handleError(record)
RuntimeError: <unprintable RuntimeError object>
Logged from file foo.py, line 47

The code:

import sys
import datetime
import mysql.connector
import config
from logwatch import log
import time, os

class DBConnection:
    _dbconn = None

    @staticmethod
    def get_instance():
        if not DBConnection._dbconn:
            DBConnection._dbconn = DBConnection()
        return DBConnection._dbconn

    def __init__(self):
        self.connection = None

    def connect(self):
        if not self.connection:
            self.connection = mysql.connector.connect(**config.CONFIG)

    def get_cursor(self):
        retries = 2
        while retries > 0:
            try:
                self.connect()
                cursor = self.connection.cursor(buffered=True)
                return cursor
            except mysql.connector.errors.InterfaceError, iErr:
                log.error("%s: Connection failed. Retrying. " % iErr)
                self.connection = None
                retries -= 1
                if retries == 0:
                    raise

    def execute(self, query, params=None):
        cursor = self.get_cursor()
        cursor.execute(query, params)
        return cursor.rowcount

    def commit(self):
        try:
            self.commit()
        except Exception, cExc:
            log.error("Error committing the operation to MySQL: %s" % cExc)

def foo(**kwargs):
    dml = None
    values = None
    rows = None

    if kwargs['action'] == "check":
        dml = "SELECT `log_type` FROM `register` WHERE `record_num` = \
        %s AND `log_type` IN ('1', '3')" % kwargs['record_num']
        log.info("%s %s Checking if record exists in F/U or SA of db: %s" % (kwargs['rectype'], kwargs['record_num'], dml))

    if kwargs['action'] == "moveto":
        sections = ['Trash','Follow up','Problem','Scheduled Actions','Information']
        if kwargs['section'] == 0:
            dml = "UPDATE `register` SET `log_type` = %s WHERE `record_num` = %s"
            values = (kwargs['section'], kwargs['record_num'])
        else:
            dml = "UPDATE `register` SET `log_type` = %s, `comments` = %s WHERE `record_num` = %s"
            values = (kwargs['section'], kwargs['status'], kwargs['record_num'])
        log.info("%s %s Moving to section '%s' %d: %s" %\
        (kwargs['rectype'], kwargs['record_num'], sections[kwargs['section']], kwargs['section'], (dml % values) ))

    if kwargs['action'] == "schedule":
        yyyymmdd = datetime.datetime.strptime(kwargs['start_date'], '%d%b%y').strftime('%Y-%m-%d')
        start_hour = kwargs['start_time'][0:2]
        dml = "INSERT INTO `register` (`record_num`,`title`,`sch_date`, \
        `sch_time`,`rectype`,`log_type`,`user_id`,`entry`,`site`,`comments`,`category`) VALUES \
        (%s, %s, %s, %s, %s, 3, 'FOO', 2, %s, %s, 63)"
        values = (kwargs['record_num'], kwargs['title'], yyyymmdd, kwargs['start_time'], kwargs['rectype'], get_site_flag(start_hour), kwargs['comment'])
        log.info("%s %s Adding to scheduled actions up for review: %s" % (kwargs['rectype'], kwargs['record_num'], (dml % values) ))

    if kwargs['action'] == "update_to_schedule":
        yyyymmdd = datetime.datetime.strptime(kwargs['start_date'], '%d%b%y').strftime('%Y-%m-%d')
        start_hour = kwargs['start_time'][0:2]
        dml = "UPDATE `register` SET `title` = %s,`sch_date` = %s, \
        `sch_time` = %s,`rectype` = %s,`log_type` = '3',`user_id` = 'FOO', \
        `entry` = '2',`site` = %s WHERE `record_num` = %s LIMIT 1"
        values = (kwargs['title'], yyyymmdd, kwargs['start_time'], kwargs['rectype'], get_site_flag(start_hour), kwargs['record_num'])
        log.info("%s %s Updating to scheduled actions for review: %s" % (kwargs['rectype'], kwargs['record_num'], (dml % values) ))

    if kwargs['action'] == "update_to_follow":
        dml = "UPDATE `register` SET `title` = %s,`rectype` = %s, \
        `log_type` = '1',`user_id` = 'FOO', `entry` = '2', \
        `site` = 'M' WHERE `record_num` = %s LIMIT 1"
        values = (kwargs['title'], kwargs['rectype'], kwargs['record_num'])
        log.info("%s %s Updating to follow up for review: %s" % (kwargs['rectype'], kwargs['record_num'], (dml % values) ))

    if kwargs['action'] == "follow-up":
        dml = "INSERT INTO `register` (`record_num`,`title`,`sch_date`, \
        `rectype`,`log_type`,`user_id`,`entry`,`site`,`comments`,`category`) VALUES \
        (%s, %s, NOW(), %s, 1, 'FOO', 2, 'M',%s, 63)"
        values = (kwargs['record_num'], kwargs['title'], kwargs['rectype'], kwargs['comment'] + ', Please follow-up')
        log.info("%s %s Adding to follow up for review: %s" % (kwargs['rectype'], kwargs['record_num'], (dml % values) ))    
    try:
            connection = DBConnection.get_instance()
            rows = connection.execute(dml, values)
            connection.commit()
            log.info("%s %s EXECUTE affected %s rows." % (kwargs['rectype'], kwargs['record_num'], rows))
    except:
        log.error("Error: could not execute %s on db for %s %s.,%s" % ((dml % values), kwargs['rectype'], kwargs['record_num'], str(sys.exc_info())))
    return rows

line 47 being:

log.error("Error committing the operation to MySQL: %s" % cExc)

Am I supposed to be encapsulating this in something else? Sorry I don't know python, I know Java, I'm just trying to fix this script that I've adopted. There's other random exceptions as well but one step at a time, these seem to stream in at every commit? Trying to figure out if it's the cause of why the script seems to fail to insert into the database under heavy stress, the commit is a new addition to this script which was someone else's attempted fix.

Whenever the script fails to insert into the database we also get:

(<class 'mysql.connector.errors.OperationalError'>, OperationalError(), <traceback object at 0x2aaaaaf76050>)  
(<class 'mysql.connector.errors.InterfaceError'>, InterfaceError(), <traceback object at 0x2aaaaaf75ab8>)

This shows up in the official logging via that log.error line above instead of the stdout error file where the exceptions are dumped.

Any help would be greatly appreciated, thanks.


Solution

  • This looks like the culprit:

    def commit(self):
        try:
            self.commit()
        except Exception, cExc:
            log.error("Error committing the operation to MySQL: %s" % cExc)
    

    This is an infinite loop until your recursion depth exceeds your system's limit (typically 1000). At the end since you are catching all exceptions, you end up at

    log.error("Error committing the operation to MySQL: %s" % cExc)
    

    Try

    # self.commit() - WRONG
    self.connection.commit()
    

    Also consider not catching all exceptions after the commit (Considered very bad practice). Replace with the base class for all mysql exceptions instead:

    try:
        self.commit()
    except mysql.connector.Error as e:
        log.error("Error committing the operation to MySQL: %s" % e)