6

共有ホスティング プランで小さな Web アプリを実行しています。無限ループを含む「ワーカー関数」があります。ループは、DB 内のタスク キューをチェックして、新しいことを行います。これ@transaction.commit_manuallyは、Django のキャッシングを打ち負かし、反復ごとに最新の情報を取得するために使用する必要がありました。

私は最近 DB ロギングを実装したので、ワーカー関数にセーブポイントの使用を導入する必要がありました。このようにして、何か問題が発生した場合、適切なセーブポイントにロールバックし、データベースにログを記録し、最終的に到達するまで続行できます。transaction.commit()

現在、私の開発サーバーとは異なり、本番サーバーでは次のエラーが表示されます。

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

transaction.savepoint_rollback()ブロック内の呼び出しを指していexceptます (以下のソースを参照)。開発サーバーにはそのような問題はありません。transaction.savepoint()対話型シェルを入力すると、本番サーバーは喜んでセーブポイント ID を生成します。

これが私のコードの概要です。簡潔にまとめてみました。

慈悲深い Python の達人がいる場合は、私を助けてください。私はこれについて本当にイライラしていますが、落ち着いて対処することについてはかなり良い仕事をしていると思います.

4

2 に答える 2

13

私は同じ時折繰り返される厄介なエラーを抱えていました:

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')暗黙的なロールバックが発生します。

そのため、2 番目のケースは確かにやや「正常」になります。次のコードで表すことができます。

# 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のドキュメントを確認すると、すべてのMySQLストレージエンジンがセーブポイントをサポートしているわけではないことがわかります。基本的に、MyISAMはトランザクションを処理しないため、ロールバックすることはできず、InnoDBは処理します。したがって、devテーブルとprodテーブルの両方が同じストレージエンジンタイプを使用していることを確認します。これは、次のコマンドを実行して確認できます。

SHOW CREATE TABLE mytable
于 2012-12-05T21:01:00.560 に答える