Why Are My Thread Variables Intermittent in Rails

Why are my thread variables intermittent in Rails?

Manipulating the Thread local variables is a really bad idea and is going to lead to nothing but sadness, heartache, and pain. There's no guarantee that different parts of the request processing will be handled by the same thread, and because of this, your variables might end up getting lost.

The Rails convention is to create instance variables in the context of ApplicationController. In simple terms, all you really do is this:

class ApplicationController < ActionController::Base
before_filter :set_current_subdomain

attr_reader :current_subdomain
helper_method :current_subdomain

protected
def set_current_subdomain
@current_subdomain = request.subdomain

@account = Account.find_by_subdomain(@current_subdomain)
end
end

Any @... type variables you create will be attached to the instance of the ApplicationController associated with the current request. It's important to note that each request will be issued a brand-new instance of the appropriate controller class.

You're free to create whatever instance variables you want provided they don't somehow conflict with those used by Rails itself but in general terms this doesn't happen very often and conflicts typically occur on method names instead.

Class-level instance variables will persist between requests in environments where the "cache classes" flag is enabled. In the development environment your controller class is re-loaded each time a request is made to ensure it reflects the current state of your source files.

Safety of Thread.current[] usage in rails

There is not an specific reason to stay away from thread-local variables, the main issues are:

  • it's harder to test them, as you will have to remember to set the thread-local variables when you're testing out code that uses it
  • classes that use thread locals will need knowledge that these objects are not available to them but inside a thread-local variable and this kind of indirection usually breaks the law of demeter
  • not cleaning up thread-locals might be an issue if your framework reuses threads (the thread-local variable would be already initiated and code that relies on ||= calls to initialize variables might fail

So, while it's not completely out of question to use, the best approach is not to use them, but from time to time you hit a wall where a thread local is going to be the simplest possible solution without changing quite a lot of code and you will have to compromise, have a less than perfect object oriented model with the thread local or changing quite a lot of code to do the same.

So, it's mostly a matter of thinking which is going to be the best solution for your case and if you're really going down the thread-local path, I'd surely advise you to do it with blocks that remember to clean up after they are done, like the following:

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

This ensures the thread local variable is cleaned up before being used if this thread is recycled.

Why isn't there method that assign value to a variable in programming language

When writing code, but especially when using a dynamic language like Ruby, think in terms of data structures and not in terms of variables.

What you're looking for is actually this:

students = [ "taro", "ziro", "tarao" ].map do |name|
Student.new(name)
end

Where that creates an array of students. These can be referenced as students[0] and so on.

If you wanted them indexed by name:

students = [ "taro", "ziro", "tarao" ].map do |name|
[ name, Student.new(name) ]
end.to_h

Where then you can call students['taro'] and get that student. You'll often find that the Ruby solution that's the most minimal is often the most Ruby-like.

Using regular data structures like this helps when you're passing this data from one location in your code to another. Consider how easy this is:

best_student(students)

Versus this approach:

best_student(taro, ziro, tarao, ...)

Where each time you add a new student you need to go and change all of your code. That's counter-productive.

The version that does roughly what you want is really ugly by comparison, and intentionally so as you're not supposed to do this casually, it shouldn't be easy:

[ "taro", "ziro", "tarao" ].map do |name|
instance_variable_set(:"@#{name}", Student.new(name))
end

Where that creates instance variables like @taro but is also a giant mess compared to the simple version.

Intermittent error in PostGIS query via Rails (string interpolation failure?)

There is no need to convert the geography to a string, and then to read it back as a geography.

You can try directly

Node.where("ST_DWITHIN(geog, ?, 25)", path.geog)

That being said, you may indeed have some invalid geometries

Elastic Beanstalk intermittently activates rack 1.5.2, but my Gemfile requires rack 1.6.0

After a lot of trial and error, here's what worked for me.

Remove puma and rack from Gemfile. Run bundle install. Here's what my Gemfile looks like.

# Gemfile
source 'https://rubygems.org'

gem 'ahoy_matey'
gem 'aws-sdk'
gem 'bcrypt'
gem 'cancancan'
gem 'coffee-rails'
gem 'font-awesome-rails'
gem 'foundation-rails'
gem 'gibbon'
gem 'jbuilder'
gem 'jquery-infinite-pages'
gem 'jquery-rails'
gem 'kaminari'
gem 'mandrill_mailer'
gem 'modernizr-rails'
gem 'nokogiri'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'owlcarousel-rails'
gem 'paper_trail'
gem 'pg'
gem 'rails'
gem 'rails_admin'
gem 'sanitize'
gem 'sass-rails'
gem 'sentry-raven'
gem 'stripe'
gem 'twitter-typeahead-rails'
gem 'uglifier'
gem 'whenever'

group :test, :development do
gem 'dotenv-rails'
end

group :development do
gem 'spring'
end

group :doc do
gem 'sdoc'
end

In .ebextensions/ folder in your repo, create a script to install rack 1.6.0 as a local gem.

# .ebextensions/00-install-local-gems.config:
commands:

# add rack 1.6.0 to $GEM_ROOT so puma can activate it instead of rack 1.5.2
# use actual path not $GEM_ROOT because env vars are not available here
# make sure puma and rack are not in app's Gemfile or there will be blood

00_install_rack_160:
command: gem install -i /opt/rubies/ruby-2.1.4/lib/ruby/gems/2.1.0 rack -v 1.6.0

Commit Gemfile, Gemfile.lock and .ebextensions/00-install-local-gems.config into your repo. Push the code to Elastic Beanstalk.

You now should terminate all your existing instances. Elastic Beanstalk will re-create them with this updated configuration.

I can confirm the above works with 64bit Amazon Linux 2014.09 v1.2.0 and v1.0.9, both running Ruby 2.1 (Puma).

Why not use shared ActiveRecord connections for Rspec + Selenium?

This solution was written by Jose Valim - well respected in the Rails community and a member of the Rails core team. I doubt he would recommend using it if there were issues with it. I personally haven't had any issues.

Just be aware that if you use Spork this needs to be in the each_run block to work.

FWIW - I have had intermittent capybara test issues with the above patch on Postgres. The Mike Perham solution that @hsgubert has below appears to have solved those issues. I am now use that solution.

Why is Ruby throwing a Segmentation fault on only my system, and only in this Rails application?

Ok, I've fixed this on my machine. I'm not sure exactly why, but downgrading the ruby-debug gem to version 0.10.0 solves the problem for me.

The specific fix is running the following commands:

sudo gem uninstall ruby-debug

sudo gem install ruby-debug --version 0.10.0


Related Topics



Leave a reply



Submit