Rails에서 Thread.current [] 사용의 안전성


81

Thread.current해시 (예 : current_user, 현재 하위 도메인 등)에 정보를 저장하는 관행에 대해 상충되는 의견을 계속 받고 있습니다. 이 기술은 모델 계층 (쿼리 범위 지정, 감사 등) 내에서 이후의 처리를 단순화하는 방법으로 제안되었습니다.

많은 사람들은 MVC 패턴을 깨뜨리기 때문에 이러한 관행을 받아 들일 수 없다고 생각합니다. 다른 사람들은 접근 방식의 신뢰성 / 안전성에 대해 우려를 표명하고 2 부로 구성된 질문은 후자 측면에 중점을 둡니다.

  1. 는 IS Thread.current해시는 전체주기 동안, 오직 하나의 응답 가능하고 개인이 보장?

  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

감사합니다. 참조를 추가했습니다.
Giuseppe

3
링크 한 게시물에서 예제는 컨트롤러 계층 만 다루며 제안 된 솔루션은 분명히 적절합니다. 하지만 대부분의 사람들이 관심을 가질만한 것은 모델에 대한 각 호출에 추가 매개 변수를 추가하지 않고 일반적으로 제외되는 1-2 개의 정보에 대한 액세스 권한을 모델에 제공하는 깨끗한 방법이라고 생각합니다. 이 점에서 모든 무서운 "Thread.current에서 멀리 떨어져"경고 신호는 왜 지금까지 나를 불확실하게 만들 었는지에 대한 특별한 이유없이. 감사합니다
Giuseppe

답변:


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 블록과 함께 주위 필터를 사용하면 예외 처리 / 로깅이 엉망이됩니다. 미들웨어 기반의 깨끗한 솔루션에 대한 Dejan의 답변에서 RequestStore를 확인하십시오.
Irongaze.com 2014

위와 같이 확인하는 것이 아니라 예외를 먼저 구했다는 점에서 문제에 대한 접근 방식이었을 수 있습니다. 예외의 호출 스택은 내가 다시 제기 한 위치로 재설정되어 Rails 오류 로그 및 다른 곳을 엉망으로 만듭니다. 캡처 된 예외를 직접 로그 파일에 직접 덤프하여 보존했지만 추악했습니다. 미들웨어 기반 접근 방식을 사용하는 것은 훨씬 더 깨끗한 IMHO입니다.
Irongaze.com 2014

1
그렇기 때문에 위의 코드에는 ensure블록 만 있고 예외를 포착하려고하지 않습니다.
Maurício Linhares 2014

당신이 그것을 볼 때 간단합니다 :)
244an



4

허용되는 답변은 기술적으로 정확하지만 답변에서 부드럽게 지적하고 http://m.onkey.org/thread-safety-for-your-rails 에서 그렇게 부드럽게 지적 하지 않습니다.

Thread.current꼭 필요하지 않은 경우 스레드 로컬 저장소를 사용하지 마십시오.

에 대한 gem request_store은 또 다른 솔루션 (더 나은)이지만 스레드 로컬 저장소에서 멀리 떨어져 있어야하는 더 많은 이유 때문에 readme를 읽으십시오.

거의 항상 더 나은 방법이 있습니다.


3
Rails Multi-tenant 앱과 함께 Unicorn을 사용하고 있습니다. "더 나은 방법"의 예를 제안 해 주시겠습니까? 게시 한 링크에서 애플리케이션 오류가 발생합니다. 감사.
lacostenycoder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.