Django + MySQL:保存点不存在?

时间:2012-12-05 17:22:27

标签: python mysql django

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

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

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

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

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

This is the outline of my code,如果有任何帮助的话;我试着保持简洁。

如果那里有任何仁慈的Python专家,请帮助我。我对此感到非常沮丧,虽然我认为我在平静地处理它方面做得相当不错。

2 个答案:

答案 0 :(得分:11)

我偶尔会出现同样的恶意错误:

OperationalError: (1305, 'SAVEPOINT {{name}} does not exist')
除了它的正常情况外,谷歌并没有让它变得更加清晰。并发问题。因此,它在开发环境中是非确定性的,难以重现。

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

原因

在MySQL中,有些操作可以隐含地结束交易:

  • DDL语句(例如CREATE TABLEALTER 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实现了exception chaining,如果出现死锁,则回溯将包含所有相关信息。

答案 1 :(得分:0)

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

SHOW CREATE TABLE mytable