2009-06-23 5 views
7

私は最終的なPOSTでデータベースにコミットする(または元に戻す)前に一連のGET/POSTで一連の変更を構築できるDjango Webアプリケーションを作成しています。私は、各POSTの後にコミットすることを排除する(これは設定フロントエンドです)確認が行われるまで、同時実行のデータベースユーザーから更新を隔離しておく必要があります。Djangoでのセッション単位のトランザクション

私の好ましい解決策は、セッションごとのトランザクションを使用することです。これにより、変更された内容(それが後続のクエリにどのように影響するか)と、コミット/ロールバックの実装と、それが属するデータベース内のすべての問題が保持されます。デッドロックとロング・ロックは問題ではありません。なぜなら、外部の制約のために、一度に1人のユーザーしかシステムを構成できず、正常に動作しているからです。

しかし、この種のトランザクションモデルを使用するためのDjangoのORMの設定に関するドキュメントは見つかりません。私は問題を解決するために最小の猿パッチ(ew!)を一緒に投げましたが、そのような壊れやすい解決策を嫌います。他の誰もこれを前にしていますか?私はどこかの文書を見逃したことがありますか?

(ジャンゴの私のバージョンは1.0.2決勝で、私は、Oracleデータベースを使用しています。)

答えて

9

複数、同時セッション規模なトランザクションは、一般的に長い、(デッドロックや悪いことを悪い方==ライブロックをリードします他のセッションでロックが保持されている間に遅延が発生します)。

このデザインは、Djangoが推奨していない最良のポリシーではありません。

より良い解決策は次のとおりです。

  1. ユーザーの変更を記録メメントクラスを設計します。これはフォーム入力の保存されたコピーです。状態の変更が複雑な場合は、追加情報を記録する必要があります。それ以外の場合は、フォーム入力のコピーで十分です。

  2. メメントのオブジェクトをセッション中に累積します。トランザクションの各ステップには、データからのフェッチと、記念品チェーンが依然として「機能する」かどうかを確認することが含まれます。他の誰かがこの記念碑の中で何かを変えたため、時には彼らはうまくいかないでしょう。今何?

  3. 「コミット準備ができていますか?あなたはメメントスのシーケンスを再生していて、彼らはうまくいくと確信しています。 「Commit」を提出すると、メメントスを最後に再生しなければなりません。彼らはまだ動作することを望んでいます。もしそうなら、素晴らしい。もしそうでなければ、誰かが何かを変えて、ステップ2に戻ります:今は何ですか?

これは複雑なようです。

はい、あります。ただし、ロックを保持していないため、速度が遅くなり、デッドロックが発生する可能性が低くなります。トランザクションは実際にメメントスのシーケンスをデータベースに適用して結果を保存し、トランザクションを終了するための最終コミットを行う「コミット」ビュー機能に限定されています。

n-1番目のステップn-1でユーザーがコーヒーのクイックカップを脱出している間に、代替錠をロックすることはできません。

メメントの詳細については、thisを参照してください。

+0

デッドロックとコーヒーを飲むユーザーは問題ありません(1台のコントローラがあり、設計上、全体の更新は1つのロックで行われます)。 私が間違っていれば私を訂正してください。しかし、メメントスは実際にORMで働かないでしょうか? –

+0

メメントはデザインパターンとして、すべてのものと機能します。ゆっくりと積み重なるロックを伴う長時間実行の複数ステップトランザクションはデッドロックにつながります。デッドロックを回避する唯一の方法は、単一のユーザーを持つことです。 –

+0

Django ORMは、テーブルからフォームへの自動マッピングを提供します。これをMementoパターンにフックすることはできますか、またはこの機能の使用をやめる必要がありますか? –

1

他の誰かが私とまったく同じ問題を抱えている(私は願っていない)場合は、ここに私のモンキーパッチがあります。それは壊れ易くて醜いですし、私的な方法を変えますが、ありがたいことにそれは小さいです。本当にする必要がある場合を除き、使用しないでください。他の人に言及されているように、それを使用するアプリケーションは、デッドロックのペナルティを受けて、複数のユーザーが同時に更新を行うことを効果的に防ぎます。 (私のアプリケーションでは、多くの読者がいるかもしれませんが、複数の同時アップデートは意図的に除外されています)。

私は、ユーザーセッション全体で永続的な接続オブジェクトを含む "user"オブジェクトを持っています。特定のHTTPインタラクションがセッションの一部であることを検証するとき、スレッドオブジェクトであるdjango.db.connectionにユーザオブジェクトも格納します。

def monkeyPatchDjangoDBConnection(): 
    import django.db 
    def validConnection(): 
     if django.db.connection.connection is None: 
      django.db.connection.connection = django.db.connection.user.connection 
     return True 
    def close(): 
     django.db.connection.connection = None 
    django.db.connection._valid_connection = validConnection 
    django.db.connection.close = close 
monkeyPatchDBConnection() 

def setUserOnThisThread(user): 
    import django.db 
    django.db.connection.user = user 

この最後は私のコードのように99%が、このハックの詳細から絶縁され、@login_requiredの注釈を付けた任意の方法の開始時に自動的に呼び出されます。

2

私はMementoパターンに似たものを思いつきましたが、私はそれが投稿するのに十分だと思います。ユーザーが編集セッションを開始すると、ターゲットオブジェクトをデータベースの一時オブジェクトに複製します。以降の編集操作はすべて複製に影響します。それぞれの変更時にの記念品にオブジェクト状態を保存する代わりに、操作オブジェクトを保存します。オブジェクトに操作を適用すると、それは操作を返します。これは私が格納します。

節約操作は、編集中のオブジェクトがはるかに大きいうちにいくつかの小さなデータ項目で操作を記述できるので、私にとってはメモ帳よりもずっと安いです。また、私が行った操作を適用してアンドゥを保存するので、dbの一時的な値は常にユーザーのブラウザのバージョンに対応します。私は変更のコレクションを再生する必要はありません。一時的なものは、常に次のバージョンから1つの操作だけです。

"元に戻す"を実装するには、スタックから最後の元に戻すオブジェクトをポップします(そのまま、一時オブジェクトの最新の操作をデータベースから取得することによって)一時的に適用し、変換された一時的なオブジェクトを返します。もし私がリドゥを実装することができたら、結果のオペレーションをリドゥスタックにプッシュすることもできます。

「変更を保存」、つまりコミットを実装するには、元のオブジェクトを非アクティブ化してタイムスタンプし、その場所で一時的に有効にします。

「キャンセル」、つまりロールバックを実装するには、何もしません。私は一時的なものを削除することができました。もちろん、編集セッションが終了したらユーザーがそれを取得する方法はないからです。しかし、キャンセルされた編集セッションを維持して、cronジョブでそれらをクリアする前に。

関連する問題