Multithreading in JRuby on Rails App

Hello,

I’ve done a decent amount of multi-threading in .NET, but have never
done
any in Java or Ruby. I couldn’t find anything about the topic on the
JRuby
wiki, so I just wanted to get some feedback. Basically, I want a
singleton
thread that continuously processes user submissions until it finds no
more
to process, then it exits. The
UserSubmissionProcessor.start_processor_if_necessary method should start
the
singleton thread if it is not already running. My code is below. Are
there
any non-obvious gotchas I should be aware of? Also, I can’t figure out
how
to pass exceptions to rails exception-handling mechanism (so that it
gets
logged in my log file, and emailed to me using the exception notifier
plugin).

Thanks!

  • Myron

class UserSubmission < ActiveRecord::Base

def after_create
UserSubmissionProcessor.start_processor_if_necessary
end

def process
self.update_attribute(:processing_began, Time.now)
# do processing here…
self.update_attribute(:processing_completed, Time.now)
end

end

class UserSubmissionProcessor
@@semaphore = Mutex.new
@@singleton_thread = nil unless defined? @@singleton_thread

def self.start_processor_if_necessary
@@semaphore.synchronize do
if @@singleton_thread.nil?
@@singleton_thread = Thread.new
{UserSubmissionProcessor.process}
end
end
end

private

def self.process
sleep 1
#TODO: how to I propagate exceptions to rails’ exception-handling
mechanism?
while true
user_submission = UserSubmission.find(:first, :conditions =>
{:processing_completed => nil}, :order => ‘created_at’)
break unless user_submission
user_submission.process
end

@@semaphore.synchronize do
  @@singleton_thread = nil
  Thread.exit
end

end
end

On Wed, Jul 30, 2008 at 10:43 PM, Myron M.
[email protected] wrote:

Hello,

I’ve done a decent amount of multi-threading in .NET, but have never done
any in Java or Ruby. I couldn’t find anything about the topic on the JRuby
wiki, so I just wanted to get some feedback. Basically, I want a singleton
thread that continuously processes user submissions until it finds no more
to process, then it exits. The
UserSubmissionProcessor.start_processor_if_necessary method should start the
singleton thread if it is not already running. My code is below. Are there
any non-obvious gotchas I should be aware of?

Does the code seem to work?

The only one that comes to mind here is that your background thread
has the potential to corrupt your (probably) single database
connection, if it were to execute at the same time as a foreground
thread. Setting ActiveRecord::Base.allow_concurrency = true should
help with that, but I’ve heard from some that it’s still broken.

The other thing is that you might as well just have the while true
loop never finish, and just have it sleep in between loops rather than
tearing down and creating the thread all the time.

Also, I can’t figure out how
to pass exceptions to rails exception-handling mechanism (so that it gets
logged in my log file, and emailed to me using the exception notifier
plugin).

Not sure how exception_notifier works, or what triggers notifications.
You might want to just scan the plugin source, see how it captures
exceptions, and tie into that code directly, or one level up from that
if convenient.

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Thanks for the quick response!

Does the code seem to work?

So far it does. But I haven’t gone live yet, and since race conditions
don’t occur in a deterministic fashion, it doesn’t mean there isn’t a
bug
there :).

The only one that comes to mind here is that your background thread
has the potential to corrupt your (probably) single database
connection, if it were to execute at the same time as a foreground
thread. Setting ActiveRecord::Base.allow_concurrency = true should
help with that, but I’ve heard from some that it’s still broken.

I was unaware of this, so thanks. I found this blog post[1] discussing
this, and apparently I should call
ActiveRecord::Base.verify_active_connections! when my thread ends. I’ve
added both of these. Can you elaborate on 'I’ve heard from some that
it’s
still broken"?

The other thing is that you might as well just have the while true
loop never finish, and just have it sleep in between loops rather than
tearing down and creating the thread all the time.

I’ve considered this, but I don’t expect my app to get a lot of traffic
(at
least initially)–it wouldn’t surprise me at all if I the thread goes
hours
between user submissions. It seems kinda like a waste to have the
thread
sleep that long. Then again, if my traffic is that low, I guess I don’t
need to worry about it.

Not sure how exception_notifier works, or what triggers notifications.
You might want to just scan the plugin source, see how it captures
exceptions, and tie into that code directly, or one level up from that
if convenient.

I poked around in the plugin a while last night and couldn’t figure it
out
(sometimes Ruby and Rails is too magical and dynamic for its own
good…).
I’ll spend some more time on this later, but in the meantime, if anyone
knows, feel free to enlighten us :).

  • Myron

[1] Threading in Rails – Bibliographic Wilderness

Does the code seem to work?

So far it does. But I haven’t gone live yet, and since race conditions
don’t occur in a deterministic fashion, it doesn’t mean there isn’t a
bug there :).

You probably also want to make sure you’re just running one jruby
instance… warbler creates several by default, and it looks like you’d
end up with (potentially) as many processing threads as you have
runtimes. If you’re going live with just one jmongrel, this obviously
won’t be an issue.

Not sure how exception_notifier works, or what triggers notifications.
You might want to just scan the plugin source, see how it captures
exceptions, and tie into that code directly, or one level up from that
if convenient.

I poked around in the plugin a while last night and couldn’t figure it
out (sometimes Ruby and Rails is too magical and dynamic for its own
good…). I’ll spend some more time on this later, but in the
meantime, if anyone knows, feel free to enlighten us :).

Rails itself catches most exceptions in actionpack’s rescue.rb
(perform_with_rescue). If all you want to do is log the exception, I’d
start by looking at rescue.rb, particularly log_error. Actionpack 2.0.1
defines it this way:

   def log_error(exception) #:doc:
     ActiveSupport::Deprecation.silence do
       if ActionView::TemplateError === exception
         logger.fatal(exception.to_s)
       else
         logger.fatal(
           "\n\n#{exception.class} (#{exception.message}):\n    " +
           clean_backtrace(exception).join("\n    ") +
           "\n\n"
         )
       end
     end
   end

“logger” should be available on any ActiveRecord::Base or
ActionController::Base subclass. If logger doesn’t work,
RAILS_DEFAULT_LOGGER might be a better choice.


Matt


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

On Wed, Jul 30, 2008 at 11:27 PM, Myron M.
[email protected] wrote:

I was unaware of this, so thanks. I found this blog post[1] discussing
this, and apparently I should call
ActiveRecord::Base.verify_active_connections! when my thread ends. I’ve
added both of these. Can you elaborate on 'I’ve heard from some that it’s
still broken"?

http://groups.google.com/group/merb/browse_thread/thread/50468729356c5a50

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email