Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveSupport::Notifications should be async?

I have an extremely simple setup that tests the rails3 ActiveSupport::Notifications. By reading the documentation, the ActiveSupport::Notifications.subscribe bit should perform it's operations asynchronously. Apparently this is not the case.

Example:

ActiveSupport::Notifications.subscribe "some.channel" do |name, start, finish, id, payload|
  # do expensive task
  sleep(10)
end

ActiveSupport::Notifications.instrument "some.channel" #=> will return 10 seconds later

I was under the impression that ActiveSupport::Notifications.instrument "some.channel" would return right away and let the expensive task do expensive stuff. Otherwise I could just call the expensive task directly, without using a subscriber.

The documentation is also stating that there could be multiple subscribers. In that case I would get blocked until all other subscribers executed their code.

Is this right? If it is, can someone please explain what does this line from http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html mean?

The block will be called asynchronously whenever someone instruments “render”:

like image 395
Oktav Avatar asked May 20 '13 14:05

Oktav


1 Answers

You're right. ActiveSupport::Notifications.instrument does not call the subscribers asynchronously / concurrently or anything of the sort.

If you follow the code, you'll find that #instrument calls an instrumenter, and instrumenter calls a notifier. The notifier is an instance of ActiveSupport::Notifications::Fanout, which sends notifications to all listeners via the #publish method:

def publish(name, *args)
  listeners_for(name).each { |s| s.publish(name, *args) }
end

The #publish method of the listener/subscriber is not asynchronous either:

def publish(message, *args)
  @delegate.call(message, *args)
end

All this does is call the block provided by the subscriber. Synchronously.

So why does the documentation claim that The block will be called asynchronously whenever someone instruments “render” and Notifications ships with a queue implementation that consumes and publish events to log subscribers in a thread?

It looks like the original implementation was asynchronous, and they forgot to update the documentation. If you look at the change history for fanout.rb, you can see some commits from 2010 where the implementation was changed to a synchronous queue. A threaded implementation was probably too complicated and error-prone. You can even see some vestiges left over:

# This is a sync queue, so there is no waiting.
def wait
end

This looks like a good candidate for a commit to docrails.

In any case, even if the implementation were asynchronous, you would probably want any long-running code to go into a queue (such as resque) and get processed by a background worker. This is so that your webapp worker processes aren't tied up processing long-running tasks, rather than serving requests.

UPDATE: Found a commit from 11 months ago that removes the incorrect information about notifications being asychronous. However, the incorrect information that the queue is running in a thread is still there.

UPDATE 2: I committed to docrails to fix the information about the queue running in a thread.

UPDATE 3: My commit has been merged into the official documentation.

like image 90
davogones Avatar answered Sep 17 '22 19:09

davogones