Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use of Thread.current and ViewContext #390

Closed
elado opened this issue Dec 14, 2012 · 16 comments
Closed

Use of Thread.current and ViewContext #390

elado opened this issue Dec 14, 2012 · 16 comments

Comments

@elado
Copy link

elado commented Dec 14, 2012

I think this is pretty serious bug, where view context is kept globally and not per-request:

On a server like thin or unicorn, the Thread.current hash is not being cleared between requests. It means that this code:

before = Thread.current[:x]
Thread.current[:x] = 1
render text: [ before, Thread.current[:x]  ].inspect

in a controller, when executed twice (even from different sessions), will print nil, 1, and then 1, 1.

It means that Thread.current[:current_view_context] ||= build_view_context will keep previous value.
And h.current_user, on first hit, will return the current user, and on a different session will return the previous user because the Thread.current[:current_view_context] contains the old context.

I think that the solution is to clear the Thread.current[:current_view_context] in a Middleware, before the request, so it gets fetched on every request.

See more discussion and conclusions here:
http://stackoverflow.com/questions/13829257/rails-how-to-handle-thread-current-data-under-a-single-threaded-server-like-thi

Thoughts?

@steveklabnik
Copy link
Member

I've always been slightly weirded out by this code, so yes, I am totally open to changing it.

@steveklabnik
Copy link
Member

Related: rails/rails#8517

@tiegz
Copy link
Contributor

tiegz commented Dec 16, 2012

👍 on a solution for this. I've run into an issue with Authlogic in the past regarding this, since it sets Thread.current[:controller] in a before_filter but never unsets it. So when the filter chain halts before Authlogic's you're stuck with the last request's controller: binarylogic/authlogic#335

@mperham
Copy link

mperham commented Dec 16, 2012

Yes, either use begin/ensure to clear it or reset it at the start of request.

@steveklabnik
Copy link
Member

@mperham thanks for chiming in. ❤️.

By begin/ensure you mean in some sort of middleware? Would that be appropriate to push up into Rails proper, especially as we're making Rails 4 be threadsafe by default?

@mperham
Copy link

mperham commented Dec 16, 2012

Yeah, middleware, around_filter, etc. or just reset it at start of each request if you want the simplest thing possible.

@glebm
Copy link

glebm commented Dec 16, 2012

The way to got about it without breaking stuff is:

  • Clear that variable before request. Clearing it after request is difficult to do cleanly, because exceptions might result in after filter never being triggered
  • Clearing all the variables is a bit dangerous, because Thread.current may be used for keeping permanent per-thread data, such as stats, profiling information, separate log outputs per threads, etc... For a typical Rails application this is probably not an issue, but clearing Thread.current will cause issues when trying to debug local memory leaks and such.

@steveklabnik
Copy link
Member

@elado I've pushed a new commit for Draper that relies on my new gem, RequestStore, and I believe that it will fix this issue. Please try out Draper HEAD as soon as you're able, and let me know if this fixes it.

Thank you!

@elado
Copy link
Author

elado commented Dec 17, 2012

@steveklabnik Funny, I wrote the same thing and just didn't push it :) Thanks, I'll check it out soon.

@steveklabnik
Copy link
Member

Ha! Sorry :(

On Monday, December 17, 2012, Elad Ossadon wrote:

@steveklabnik https://github.com/steveklabnik Funny, I wrote the same
thing and just didn't push it :) Thanks, I'll check it out soon.


Reply to this email directly or view it on GitHubhttps://github.com//issues/390#issuecomment-11453569.

@elado
Copy link
Author

elado commented Dec 17, 2012

You shouldn't be! Thanks again.

@elado
Copy link
Author

elado commented Dec 17, 2012

Worth mentioning: heartcombo/devise#2182

@steveklabnik
Copy link
Member

I'll investigate and coordinate with @josevalim, thank you.

@josevalim
Copy link

The current RequestStore should not cause any issues with Devise, since it is cleaned at the beginning of the request and not inside ensure.

@elado
Copy link
Author

elado commented Dec 17, 2012

Since you don't clear the value after app.call it's not going to be a problem.

The common approach of middlewares and Thread.current i've seen so far was something like

def call(env)
  old, Thread.current[:key] = Thread.current[:key], {}
  @app.call(env)
ensure
  Thread.current[:key] = old
end

Not sure how much the reassigning of the old value is necessary, but without it it works because the value is still available after Warden's @app.call.

@steveklabnik
Copy link
Member

🤘

Yeah, I figured the least invasive thing was to only touch it at the start of the request.

steveklabnik added a commit to steveklabnik/authlogic that referenced this issue Mar 2, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants