python TorMySQL 異常處理不顯示traceback的坑

背景:

最近找到一個異步數據庫驅動,TorMySQL,ab測試後效果是其他幾個異步驅動最好的,遂用之。

因爲儘可能業務邏輯層和數據訪問層分離,並且考慮到單一負責原則,把每一個數據庫的操作簡稱爲service,如user_service,work_service之類的。

很多情況下,每一個邏輯業務都是要N個service合作才完成的。

所以就不在單個service中try except,而是在業務邏輯層中try except。

一是:

少寫很多代碼,不然每一個service都要try except之後,logging.error(ex, exc_info=1)打印traceback,然後再raise ex,多麻煩...

二是:

考慮到很多情況下有用到事務,只能在業務邏輯層捕獲exception,捕獲到就conn.rollback(),沒捕獲到就conn.commit()。


然而,我上面這種設計竟然沒捕獲到traceback,簡直匪夷所思。

好了,背景交代完畢。


版本:

python 2.7
tormysql==0.2.9
tornado==4.2


測試1:

import logging

from tornado.ioloop import IOLoop
from tornado import gen
import tormysql

pool = tormysql.ConnectionPool(
    max_connections = 20, #max open connections
    idle_seconds = 7200, #conntion idle timeout time, 0 is not timeout
    wait_connection_timeout = 3, #wait connection timeout
    host = "127.0.0.1",
    user = "root",
    passwd = "123qwe",
    db = "chanzai_dev",
    charset = "utf8"
)

@gen.coroutine
def aa(conn):
        sql = "SELECT * FROM UserDevice where user_id = %s" % 100002
        with conn.cursor() as cursor:
            yield cursor.execute(sql)
            cursor.fetchone()
            # 下面是故意出錯代碼
            a = '10'
            b = a / 10
            print b
@gen.coroutine
def bb(conn):
    yield aa(conn)

@gen.coroutine
def cc():
    with (yield pool.Connection()) as conn:
        try:
            yield bb(conn)
        except KeyError:
            pass
        except Exception, ex:
            yield conn.rollback()
            logging.error(ex, exc_info=1)

ioloop = IOLoop.instance()
ioloop.run_sync(cc)
測試結果:

G:\projects\myApp\tmp>a.py
ERROR:root:unsupported operand type(s) for /: 'str' and 'int'
None


測試2:

import logging

from tornado.ioloop import IOLoop
from tornado import gen
import tormysql

pool = tormysql.ConnectionPool(
    max_connections = 20, #max open connections
    idle_seconds = 7200, #conntion idle timeout time, 0 is not timeout
    wait_connection_timeout = 3, #wait connection timeout
    host = "127.0.0.1",
    user = "root",
    passwd = "123qwe",
    db = "chanzai_dev",
    charset = "utf8"
)

@gen.coroutine
def aa(conn):
        sql = "SELECT * FROM UserDevice where user_id = %s" % 100002
        with conn.cursor() as cursor:
            yield cursor.execute(sql)
            cursor.fetchone()
            # 下面是故意出錯代碼
            a = '10'
            b = a / 10
            print b
@gen.coroutine
def bb(conn):
    yield aa(conn)

@gen.coroutine
def cc():
    with (yield pool.Connection()) as conn:
        try:
            yield bb(conn)
        except KeyError:
            pass
        except Exception, ex:
            logging.error(ex, exc_info=1)
            yield conn.rollback()

ioloop = IOLoop.instance()
ioloop.run_sync(cc)
測試結果:
G:\projects\myApp\tmp>a.py
ERROR:root:unsupported operand type(s) for /: 'str' and 'int'
Traceback (most recent call last):
  File "G:\projects\myApp\tmp\a.py", line 40, in cc
    yield bb(conn)
  File "G:\Python27\lib\site-packages\tornado\gen.py", line 1015, in run
    value = future.result()
  File "G:\Python27\lib\site-packages\tornado\concurrent.py", line 237, in result
    raise_exc_info(self._exc_info)
  File "G:\Python27\lib\site-packages\tornado\gen.py", line 1021, in run
    yielded = self.gen.throw(*exc_info)
  File "G:\projects\myApp\tmp\a.py", line 34, in bb
    yield aa(conn)
  File "G:\Python27\lib\site-packages\tornado\gen.py", line 1015, in run
    value = future.result()
  File "G:\Python27\lib\site-packages\tornado\concurrent.py", line 237, in result
    raise_exc_info(self._exc_info)
  File "G:\Python27\lib\site-packages\tornado\gen.py", line 1024, in run
    yielded = self.gen.send(value)
  File "G:\projects\myApp\tmp\a.py", line 30, in aa
    b = a / 10
TypeError: unsupported operand type(s) for /: 'str' and 'int'

G:\projects\myApp\tmp>


觀察結果:

根據兩個測試,其實只改了一個地方,就是:
        except Exception, ex:
            logging.error(ex, exc_info=1)
            yield conn.rollback()
        except Exception, ex:
            yield conn.rollback()
            logging.error(ex, exc_info=1)
前者才能得到想要的結果,爲什麼有後者這種寫法呢,是因爲,想着對於數據庫的操作出錯了還是儘早rollback爲好,沒想到就踩了坑。



原因分析:

看了下pymysql的connections.py源碼(因爲TorMySQL是對pymysql的封裝),發現確實有很多try except,我都不用具體往下看了,因爲很大可能性是我們的ex被pymysql給捕獲到了,但是raise出來的是pymysql處理過的ex,所以看不到traceback...吧
解決方案就是先logging.error(ex, exc_info=1) 再 yield conn.rollback()



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章