On Apr 18, 2013, at 7:15 AM, Michal Fojtik wrote:

> Hi,
> 
> I recently discovered an interesting bug, that occurs when you do a lot of 
> parallel requests to Deltacloud API.
> 
> Let say you start Deltacloud API with the 'mock' driver as default driver. 
> Then you do 3 parallel requests to retrieve RHEV-M images, realms and 
> hardware_profiles. In that case I get this error:
> 
> <snip>
> E, [2013-04-18T12:44:13.629135 #11892] ERROR -- 500: [LoadError] 
> uninitialized constant Deltacloud::Drivers::Rhevm
> 
> /home/mfojtik/code/core/server/lib/deltacloud/helpers/driver_helper.rb:57:in 
> `rescue in driver'
> /home/mfojtik/code/core/server/lib/deltacloud/helpers/driver_helper.rb:54:in 
> `driver'
> 
> 127.0.0.1 - - [18/Apr/2013 12:44:13] "GET /api HTTP/1.1" rhevm 
> http://provider.url 500 127410 0.1480
> </snip>
> 
> Note, that when I do the curl request manually with same params (provider, 
> accept, I get 200 back).
> 
> I think this might be a threading issue, but I'm not sure how to fix it.
> I started looking at 'driver' method and what we do here is that we require 
> the 'driver' source file and then return the initialized driver.
> 
> We do this for every request (except we don't 'require' the driver source if 
> the driver class exists in current namespace).
> 
> My impression is that the 'require' method is not thread-safe. It can be 
> demonstrated on this code:
> 
> begin
>  driver_class
> rescue NameError => e
>  require_relative(driver_source_name) ? retry :
>     raise(LoadError.new(e.message))
> end
> 
> In this case, we try to return the 'driver_class' and if the constant does 
> not exists, we require the driver source file and call 'retry' that will then 
> try to return it again. I think, if you have multiple parallel requests, the 
> 'require_relative' (which is just alias for 'require') behave incorrectly, 
> because multiple threads are requiring the same file in parallel.
> 
> My fix for this, that so far works for me is to change the line after 'rescue 
> NameError => e' to:
> 
> Thread.exclusive { require_relative(driver_source_name) } ? retry : 
> raise(LoadError.new(e.message))
> 
> With this I don't run into any problems with parallel requests (so far).
> However, I'm not 'ruby threads' expert, so any advise from somebody more 
> experienced is appreciated.
> 
> Also I think one task, we need to do in Deltacloud/CIMI in close future would 
> be to identify spots in our code base that could be potentially not thread 
> safe.
> 
> Having somebody to write some reasonable benchmarking tool (like 'ab' with 
> different urls/drivers/etc) would help to identifying this spots.
> 
>  -- Michal
> 
> -- 
> 
> Michal Fojtik <mfoj...@redhat.com>
> Deltacloud API, CloudForms


Hey Michal,

As I mentioned in IRC last week the research I did seems to indicate the 
solution you present here
is the right way to go.

This describes the problem you found:
http://betterlogic.com/roger/2008/10/rubys-require-is-not-thread-safe/

This describes the Ruby threading and seems to support your proposed solution 
of using Thread.exclusive.
http://cs.calvin.edu/curriculum/cs/214/adams/labs/11/ruby/


So it seems your solution is the correct way to go.

Joe

Reply via email to