我正在共享主机方案上运行一个小型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专家,请帮助我。我对此感到非常沮丧,虽然我认为我在平静地处理它方面做得相当不错。
答案 0 :(得分:11)
我偶尔会出现同样的恶意错误:
OperationalError: (1305, 'SAVEPOINT {{name}} does not exist')
除了它的正常情况外,谷歌并没有让它变得更加清晰。并发问题。因此,它在开发环境中是非确定性的,难以重现。
幸运的是,我能够通过使生产应用程序日志记录足够详细来进行本地化。
在MySQL中,有些操作可以隐含地结束交易:
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实现了exception chaining,如果出现死锁,则回溯将包含所有相关信息。
答案 1 :(得分:0)
如果您查看Django上与Savepoints相关的文档,则会提到并非所有MySQL Storage Engines都支持它们。基本上MyISAM不处理事务,所以你不能回滚和InnoDB。所以我会检查你的dev和prod表都使用相同的存储引擎类型。您可以通过运行来检查:
SHOW CREATE TABLE mytable