diff options
author | John Hawthorn <john@hawthorn.email> | 2019-02-01 08:52:18 -0800 |
---|---|---|
committer | John Hawthorn <john@hawthorn.email> | 2019-02-01 12:11:19 -0800 |
commit | de13ed9ec740feaa5fdb48f338d36581d7ce2e3f (patch) | |
tree | 9947225e474839fa2cc88b58d7dd7fdc64bc6888 | |
parent | 630a343f739a1ba29cf93dc149fff6e3e0a00b05 (diff) | |
download | rails-de13ed9ec740feaa5fdb48f338d36581d7ce2e3f.tar.gz rails-de13ed9ec740feaa5fdb48f338d36581d7ce2e3f.tar.bz2 rails-de13ed9ec740feaa5fdb48f338d36581d7ce2e3f.zip |
Make Notifications::Fanout#listeners_for faster
Previously we stored all subscribers in an array, and every time a new
message name came in asked each subscriber if they responded to the
message.
This commit changes Fanout to hash subscribers with a String pattern by
their pattern. This way we can look them up directly and only do the
O(N) scan over the non-string (Regex or any) patterns.
-rw-r--r-- | activesupport/lib/active_support/notifications/fanout.rb | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 4e4ca70942..9d7fcef2c7 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -13,7 +13,8 @@ module ActiveSupport include Mutex_m def initialize - @subscribers = [] + @string_subscribers = Hash.new { |h, k| h[k] = [] } + @other_subscribers = [] @listeners_for = Concurrent::Map.new super end @@ -21,7 +22,11 @@ module ActiveSupport def subscribe(pattern = nil, block = Proc.new) subscriber = Subscribers.new pattern, block synchronize do - @subscribers << subscriber + if String === pattern + @string_subscribers[pattern] << subscriber + else + @other_subscribers << subscriber + end @listeners_for.clear end subscriber @@ -31,9 +36,14 @@ module ActiveSupport synchronize do case subscriber_or_name when String - @subscribers.reject! { |s| s.matches?(subscriber_or_name) } + @string_subscribers[subscriber_or_name].clear else - @subscribers.delete(subscriber_or_name) + pattern = subscriber_or_name.try(:pattern) + if String === pattern + @string_subscribers[pattern].delete(subscriber_or_name) + else + @other_subscribers.delete(subscriber_or_name) + end end @listeners_for.clear @@ -56,7 +66,8 @@ module ActiveSupport # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics) @listeners_for[name] || synchronize do # use synchronisation when accessing @subscribers - @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) } + @listeners_for[name] ||= + @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) } end end @@ -101,6 +112,8 @@ module ActiveSupport end class Evented #:nodoc: + attr_reader :pattern + def initialize(pattern, delegate) @pattern = pattern @delegate = delegate |