レールでのThread.current []の使用の安全性


81

情報をThread.currentハッシュに格納する方法(たとえば、current_user、現在のサブドメインなど)について、相反する意見が寄せられています。この手法は、モデルレイヤー内でのその後の処理(クエリスコープ、監査など)を簡素化する方法として提案されています。

多くの人は、MVCパターンを壊すため、このプラクティスは受け入れられないと考えています。他の人はアプローチの信頼性/安全性について懸念を表明し、私の2部構成の質問は後者の側面に焦点を当てています。

  1. Thread.currentハッシュは、そのサイクル全体を通じて、1つの応答に対してのみ使用可能でプライベートであることが保証されていますか?

  2. 応答の最後にスレッドが他の着信要求に引き渡され、それによってに格納されてThread.currentいる情報が漏洩する可能性があることを理解しています。応答が終了する前に(たとえばThread.current[:user] = nil、コントローラーから実行することによって)そのような情報をクリアすることは、after_filterそのようなセキュリティ違反を防ぐのに十分でしょうか?

ありがとう!ジュゼッペ


9
こちらの「Thread.currentで汚れる」セクションを確認してください。m.onkey.org/thread-safety-for-your-rails。それはJrubyの作者の一人によって書かれました。#1 RORコード自体は、I18Nとtime_zoneにThread.currentを使用します。それはその保証について話しますか?#2。#1が真であれば、それで十分です。
so_mv 2011年

おかげで、私は参照を追加しました。
ジュゼッペ

3
リンク先の投稿では、例はコントローラーレイヤーのみを扱っており、提案されたソリューションは明らかに適切です。ただし、ほとんどの人が興味を持っているのは、モデルへの各呼び出しに追加のパラメーターを追加せずに、通常は除外されている1〜2個の情報にモデルにアクセスできるようにするクリーンな方法だと思います。この点で、これらの大きな恐ろしい「Thread.currentから離れてください」という警告サインは、特別な理由なしに、これまでのところ私に不確かなままになっています。ありがとう
ジュゼッペ

回答:


48

スレッドローカル変数から離れる特別な理由はありません。主な問題は次のとおりです。

  • それを使用するコードをテストするときは、スレッドローカル変数を設定することを忘れないようにする必要があるため、それらをテストするのは困難です。
  • スレッドローカルを使用するクラスは、これらのオブジェクトは使用できないが、スレッドローカル変数内にあるという知識が必要になります。この種の間接参照は通常、デメテル法則に違反します。
  • フレームワークがスレッドを再利用する場合、スレッドローカルをクリーンアップしないことが問題になる可能性があります(スレッドローカル変数はすでに開始されており、変数を初期化するための|| =呼び出しに依存するコードが失敗する可能性があります

したがって、使用することは完全に問題外ではありませんが、最善のアプローチはそれらを使用しないことですが、時々、ローカルスレッドが非常に多くのコードを変更せずに可能な限り最も簡単な解決策になる壁にぶつかります。妥協するか、スレッドローカルを使用した完全ではないオブジェクト指向モデルを使用するか、同じことを行うために非常に多くのコードを変更する必要があります。

したがって、どちらがあなたのケースに最適な解決策になるかを考えることがほとんどです。本当にスレッドローカルパスをたどる場合は、後でクリーンアップすることを忘れないブロックでそれを行うことをお勧めします。それらは次のように行われます。

around_filter :do_with_current_user

def do_with_current_user
    Thread.current[:current_user] = self.current_user
    begin
        yield
    ensure
        Thread.current[:current_user] = nil
    end      
end

これにより、このスレッドがリサイクルされる場合、スレッドローカル変数が使用される前にクリーンアップされます。


1
あまり注意しないと、ensureブロックでaroundフィルターを使用すると、例外処理/ロギングが混乱します。ミドルウェアベースのよりクリーンなソリューションについては、Dejanの回答にあるRequestStoreを確認してください。
irongaze.com 2014

(上記のように確認するのではなく)最初に例外を救済するという点で、問題に対する私のアプローチであった可能性があります。例外のコールスタックは、私が再起動した場所にリセットされ、Railsエラーログなどを台無しにしていました。キャプチャした例外を自分でログファイルに手動でダンプして保存することになりましたが、見苦しいものでした。ミドルウェアベースのアプローチを使用することは、はるかにクリーンな私見です。
irongaze.com 2014

1
そのため、上記のコードにはensureブロックしかなく、例外をキャッチしようとはしていません。
マウリシオ・リニャレス

あなたがそれを見ると簡単です:)
244an 2017年



4

受け入れられた回答は技術的に正確ですが、回答で穏やかに指摘されているように、http//m.onkey.org/thread-safety-for-your-railsではそれほど穏やかではありません。

Thread.current絶対に必要がない場合は、スレッドローカルストレージを使用しないでください

のgemrequest_storeは別の解決策(より良い)ですが、スレッドローカルストレージから離れる理由が他にもあるので、readmeを読んでください。

ほとんどの場合、より良い方法があります。


3
RailsマルチテナントアプリでUnicornを使用しています。「より良い方法」の例を提案できますか?投稿したリンクはアプリケーションエラーをスローします。ありがとう。
lacostenycoder 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.