My Rails 3.1 app uses an engine and I want to know if access to this engine is threadsafe.
I have /lib/mymodule.rb in the engine and it looks something like this:
module MyModule
def self.my_method()
begin
data = WebResource.find(:all) # Where WebResource < ActiveResource::Base
rescue
data = nil
end
return data
end
end
Then in my views/controllers, I call this method like this:
MyModule::WebResource.headers[:some_id] = cookies[:some_id]
MyModule::my_method()
In my main app, I have the threadsafe! configuration option set. I know that with threadsafe! enabled, each Controller lives in it's own thread for each request.
However, is this Module threadsafe? I suspect that there is only one copy of this module for all requests, so it is not inherently thread safe, and requires manual synchronization using something like a Mutex. Specifically, I have code that sets the header for the HTTP request outside of the ActiveResource class WebResource. Could this cause a threading issue?
It will depend on what you do inside this method as to whether it is thread safe. If it touches no class variables, then it is thread safe.
If it stores or sets information at the class level and assumes that no other method is going to touch that information before it uses it again, then it is not thread safe.
Related
I have this method in a class:
class User < ApplicationRecord
...
def answers
#answers ||= HTTParty.get("http://www.example.com/api/users/#{self.id}/answers.json")
end
...
end
Since I'm using Puma as a web server I'm wondering if this code is thread safe? can someone confirm it and if possible explain why this is thread safe?
This in an instance method, not to be confused with a class method. The answers method is on an instance of User, as opposed to being on the User class itself. This method is caching the answers on the instance of a User, but as long as this User instance is being instantiated with each web request (such as a User.find()or User.find_by()), you’re fine because the instance is not living between threads. It’s common practice to look records up every web request in the controller, so you’re likely doing that.
If this method was on the User class directly (such as User.answers), then you’d need to evaluate whether it’s safe for that cached value to be maintained across threads and web requests.
To recap, the your only concern for thread safety is class methods, class variables (instance variables that use two at signs such as ##answers), and instance methods where the instance lives on past a single web request.
If you ever find yourself needing to use a class-level variable safely, you can use Thread.current, which is essentially a per-thread Hash (like {}) that you can store values in. For example Thread.current[:foo] = 1 would be an example. ActiveSupport uses this when setting Time.zone.
Alternatively you may find times where you need a single array that you need to safely share across threads, in which case you’d need to look into Mutex, which basically lets you have an array that you lock and unlock to give threads safe access to reading and writing in it. The Sidekiq gem uses a Mutex to manage workers, for example. You lock the Mutex, so that no one else can change it, then you write to it, and then unlock it. It’s important to note that if any other thread wants to write to the Mutex while it’s locked, it’ll have to wait for it to become unlocked (like, the thread just pauses while the other thread writes), so it’s important to lock as short as possible.
I saw some retry code written like this, it tries to call a service 3 times if some exception is raised, Im trying to understand in a non-MRI multi-threaded server, is this counter thread safe? is it necessary to lock the process using Mutex?
This is how its been called
MyClass.new.my_method
class MyClass
def my_method
counter = 3
begin
call_some_service_raise_some_exception
rescue SomeException => e
retry if counter.positive?
end
end
end
Assuming the variable counter is scoped to that method only, and that there is no funny shenanigans going on with Singleton or any other weird stuff, then yes, that method should be thread safe in its current form.
If, however, counter is an instance variable and you are using an accessor to set it, then that method is not thread safe. You may never encounter the race condition if you're using every MyClass once only, but all it takes is one well-meaning refactoring to reuse MyClass and suddenly you've got a race condition there.
Basically - if your method is self-contained, uses variables scoped to it only, and references no external shared data then it is thread safe by default.
As soon as you use something that could be accessed at the same time by another thread, you have a potential race condition in the making and you should start thinking about synchronising access to the shared resources.
Im using Sidekiq, Redis, Websocket-rails inside of Rails. With sidekiq having server-side Class. Now, when adding more functionality, i cant anymore use instance variables inside my Sidekiq Class's methods, i need to share information between methods. Sidekiq class also inherits from Websocket class, to be available websockets.
Right now, i have 2000/per second data change for objects. Using 95% instance variables, 5% pushing/getting from Redis, to make lower I/O.
Im considering, using class variables or making all 100% on Redis. Im not sure about or it wont overload my background jobs with that big data transfer count, which goes bigger with every new client. I'm using heroku free Dyno, dont want to buy better server yet. But using class variables would be less I/O, and probably unsafe, becouse it inherits from Websocket-rails? Im doing this project partly to show something to employers, that i can program, to get my 1-st IT job. I care how they react to those class variables. What employers would say about class variables? And which one to choose?
You can absolutely use instance variables in your Sidekiq worker. You cannot use class or class-instance variables.
class MyWorker
include Sidekiq::Worker
def perform(a, b)
#a = a # instance variable, no problem!
##a = a # class variable, big problem!
self.set_a(a)
end
def self.set_a(a)
#a = a # class instance variable, big problem!
end
end
Class variables have their places, if they represent a concept that is unchangeable and should live as a definition of something, like a configuration throughout the application, or some fundamental multiplier of a part of the application domain/business.
But, just because things are unchanging, that doesn't mean they have to be a class variable. You can have an instance of a class to be always setup with the same values, and then share the instance. Kind of like a singleton, but not necessarily being a singleton, just being a widely shared variable that is part of the input on the start of a given process.
So instead of using Sidekiq to store data for this unchanging thing, and rather than using class variables, you can achieve a cleaner design by doing something like
class ImportantThing
def initialize(name, other_property)
#name = name
#other_property = other_property
end
# Other methods you wish to define the behavior of the thing
end
then you can
the_important_thing = ImportantThing.new("foobar", 3.46)
do_important_process(the_important_thing)
then as far as the important process you are running is concerned, the important thing is anything that behaves like a important thing, and it doesn't matter if it is a globally setup or ever unchanging.
This makes for a easy to test architecture which is generally a sign of clear and decoupled design.
What's the difference between class method and instance method.
I need to use some functions in a helper "RemoteFocusHelper" (under app/helpers/)
Then include the helper "RemoteFocusHelper" in the Worker module
But when I tried to call 'check_environment' (defined in RemoteFocusHelper),
It raised ""no method error"".
Instead of using "include", I used the "extend" and works.
I wonder know if it is correct that we can only use a class method when in a class method.
Is it possible to call a instance method in a class method ?
By the way,how does the rake resque:work QUEUE='*' know where to search the RemoteFocusHelper I didn't give it the file path.Is the rake command will trace all files under the Rails app?
automation_worker.rb
class AutomationWorker
#queue = :automation
def self.perform(task=false)
include RemoteFocusHelper
if task
ap task
binding.pry
check_environment
else
ap "there is no task to do"
end
end
end
The difference is the context where you're executing. Pretty much every tutorial will have include or extend under the class:
class Foo
include Thingy
end
class Bar
extend Thingy
end
This will get executed at the time the class is defined: self is Foo (or Bar) (of type Class). extend will thus dump the module contents into self - which creates class methods.
When you do it inside a method definition, self is the instance object (of type Foo or Bar). Thus the place where the module gets dumped into changes. Now if you extend (the module contents), it dumps them into what is now self - resulting in instance methods.
EDIT: It is also worth noting that because extend works on any instance object, it is defined on Object. However, since only modules and classes are supposed to be able to include stuff, include is an instance method of Module class (and, by inheritance, Class as well). As a consequence of this, if you try putting include inside a definition of an instance method, it will fail hard, since most things (including your AutomationWorker) are not descended from Module, and thus do not have access to the include method.
Using: Rails 3.0.3
I have this feature of my website (count calculate . com) which consists of plenty different calculations. The user can set his own variable for decimals (significant numbers) through a form. First I set this variable as a configatron variable which turned out to be a bad idea (a change that some user did was passed on to all users).
So, I decided to make it a session variable, which makes sense since a user should select his own decimal value.
Now, in the controller I call a module function that in turn calls 100 different module functions, depending on id.
My problem:
I want to access this session variable in that module which turns out to be impossible. Passing it already from the controller (through the model) is a bit of a nightmare since it needs to be passed for each and every function.
So:
How do I access session variables from a module in the lib-catalog?
How could I access a controller method from a module? If so I could call that method and fetch the session variable.
Do you mean a ruby module? As in a mixin? If you're including it in your controller, it already has access to everything the controller has access to. Just call session, request, params etc and all will just work.
If you really need to get the session from some arbitrary part of the system, you can obtain the request with:
ActionDispatch::Request.new(ENV)
Then you can get the session from the request. (Note that request is actually a singleton in disguise... it won't build a new request if one is already built).
Here is one way to do it, mix your calculation module into the controller
# application_controller contains a method that wraps your session value
class ApplicationController < ActionController::Base
protect_from_forgery
def user_value
return 5 # really a session value
end
end
The module that contains your calculation methods needs access to the session, since you are mixing it into the controller you can call the user_value method with self
module CalculationModule
def super_cool_calculation
return self.user_value * 420
end
# more calculation methods
end
note the self.user_value call, that method is not defined in the module but you will need to define it in the class you mixin to
class FooController < ApplicationController
include CalculationModule
def show
#calculation = super_cool_calculation # call calculation module method
end
# etc...
if any of your model objects also define a user_value method you could then mixin the calculation module to those if needed, they would probably not be getting the value from session
Something else to consider, just store the user defined value in the database instead of the session, you will have easy access to the value from the model or controller
it sounds like your Model is including the module code. You really really do not want to use or access the session directly from there. It breaks the purpose for MVC, Rails is doing its best to prevent you from trying something like that.
when you say " I call a module function that in turn calls 100 different module functions, depending on id." I would like to see that chunk of code.
Your original idea is the correct one...the value needs to be passed over to the model...it just sounds like the code needs to be DRY'd up a little so that it isn't such a headache.
It is hard to give recommendations without seeing the code. I was stuck in a similar situation and used dynamic methods (overriding method_missing) to get around the problem.