aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Hawthorn <john@hawthorn.email>2019-02-01 08:52:18 -0800
committerJohn Hawthorn <john@hawthorn.email>2019-02-01 12:11:19 -0800
commitde13ed9ec740feaa5fdb48f338d36581d7ce2e3f (patch)
tree9947225e474839fa2cc88b58d7dd7fdc64bc6888
parent630a343f739a1ba29cf93dc149fff6e3e0a00b05 (diff)
downloadrails-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.rb23
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