6

我正在共享托管计划上运行一个小型 Web 应用程序。我有一个包含无限循环的“工作函数”;循环检查数据库中的任务队列是否有新的事情要做。这需要使用@transaction.commit_manually以击败 Django 的缓存并在每次迭代中获取最新信息。

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

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

 DatabaseError: (1305, 'SAVEPOINT s140364713719520_x1 does not exist')

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

这是我的代码大纲,如果有帮助的话;我尽量保持简洁。

如果那里有任何仁慈的 Python 大师,请帮助我。我对此感到非常沮丧,尽管我认为我在以冷静的方式处理它方面做得相当好。

4

2 回答 2

13

我偶尔会遇到同样令人讨厌的错误:

OperationalError: (1305, 'SAVEPOINT {{name}} does not exist')

谷歌搜索并没有让它更清楚,除了它是一种“正常”的并发问题。所以它是非确定性的,很难在开发环境中重现。

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

原因

在 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

更新

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

于 2013-02-14T12:34:53.117 回答
0

如果您查看 Django 上与Savepoints相关的文档,则会提到并非所有MySQL 存储引擎都支持它们。基本上 MyISAM 不处理事务,因此您不会回滚,而 InnoDB 可以。所以我会检查你的 dev 和 prod 表是否都使用相同的存储引擎类型。您可以通过运行检查这一点:

SHOW CREATE TABLE mytable
于 2012-12-05T21:01:00.560 回答