Django + MySQL:保存点不存在?

avr*_*mov 2 python mysql django

我正在共享主机方案上运行一个小型Web应用程序.我有一个"工人功能",其中包含一个无限循环; 循环检查数据库中的任务队列以查找要执行的新操作.这需要使用@transaction.commit_manually以击败Django的缓存并获得每次迭代的最新信息.

我最近实现了数据库日志记录,因此需要使用保存点来引入我的worker函数 - 这样,如果出现任何问题,我可以回滚到一个好的保存点,登录到数据库,继续直到我到达最终 transaction.commit()

现在,与我的开发服务器不同,生产服务器给出了错误:

 DatabaseError: (1305, 'SAVEPOINT s140364713719520_x1 does not exist')
Run Code Online (Sandbox Code Playgroud)

指向transaction.savepoint_rollback()一个except块中的调用(参见下面的源代码).开发服务器没有这样的问题; 如果我输入transaction.savepoint()交互式shell ,生产服务器会愉快地生成保存点ID .

这是我的代码的大纲,如果它有任何帮助; 我试着保持简洁.

如果那里有任何仁慈的Python专家,请帮助我.我对此感到非常沮丧,虽然我觉得我在以冷静的方式处理它时做得相当不错.

saa*_*aaj 11

我有同样偶然反复出现的令人讨厌的错误:

OperationalError: (1305, 'SAVEPOINT {{name}} does not exist')
Run Code Online (Sandbox Code Playgroud)

谷歌和谷歌没有说清楚,除了它是一种"正常"的并发问题.因此,它是非确定性的,难以在开发环境中重现.

幸运的是,我能够通过使生产应用程序日志记录足够详细来进行本地化.

原因

在MySQL中,有些操作可以隐式结束事务:

  • DDL语句(例如CREATE TABLE,ALTER TABLE等)导致隐式提交.众所周知,MySQL中的DDL不是事务性的,
  • OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')导致隐式回滚.

所以第二种情况确实有些"正常".它可以用以下代码表示:

# db is an example database connection object, which 
#   - supports nested (stacked) transactions, 
#   - has autocommit on.

db.begin() # START TRANSACTION
try:
  # no-conflict op
  db.update() 

  db.begin() # SAVEPOINT sp1
  try:
    # conflict op, 
    # e.g. attempt to change exclusively locked rows by another transaction
    db.update() 

    db.commit() # RELEASE SAVEPOINT sp1
  except:
    # Everything interesting happens here:
    #   - the change attempt failed with OperationalError: (1213, 'Deadlock...'),
    #   - the transaction is rolled back with all the savepoints,
    #   - next line will attempt to rollback to savepoint which no longer exists,
    #   - so will raise OperationalError: (1305, 'SAVEPOINT sp1 does not exist'),
    #   - which will shadow the original exception.

    db.rollback() # ROLLBACK TO SAVEPOINT sp1
    raise

  db.commit() # COMMIT 
except:
  db.rollback() # ROLLBACK
  raise
Run Code Online (Sandbox Code Playgroud)

更新

请注意,上面提到的关于异常阴影的内容是针对Python 2的.Python 3实现了异常链接,并且在死锁的情况下,回溯将具有所有相关信息.