diff options
Diffstat (limited to 'activesupport')
-rw-r--r-- | activesupport/CHANGELOG.md | 38 | ||||
-rw-r--r-- | activesupport/lib/active_support/callbacks.rb | 6 | ||||
-rw-r--r-- | activesupport/lib/active_support/message_encryptor.rb | 4 | ||||
-rw-r--r-- | activesupport/lib/active_support/notifications/fanout.rb | 42 | ||||
-rw-r--r-- | activesupport/lib/active_support/notifications/instrumenter.rb | 55 | ||||
-rw-r--r-- | activesupport/lib/active_support/subscriber.rb | 9 | ||||
-rw-r--r-- | activesupport/lib/active_support/testing/parallelization.rb | 16 | ||||
-rw-r--r-- | activesupport/lib/active_support/values/time_zone.rb | 4 | ||||
-rw-r--r-- | activesupport/test/cache/stores/redis_cache_store_test.rb | 4 | ||||
-rw-r--r-- | activesupport/test/log_subscriber_test.rb | 11 | ||||
-rw-r--r-- | activesupport/test/notifications_test.rb | 36 |
11 files changed, 196 insertions, 29 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 24ad72c45d..8808b38aac 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,41 @@ +* Add "event object" support to the notification system. + Before this change, end users were forced to create hand made artisanal + event objects on their own, like this: + + ActiveSupport::Notifications.subscribe('wait') do |*args| + @event = ActiveSupport::Notifications::Event.new(*args) + end + + ActiveSupport::Notifications.instrument('wait') do + sleep 1 + end + + @event.duration # => 1000.138 + + After this change, if the block passed to `subscribe` only takes one + parameter, the framework will yield an event object to the block. Now + end users are no longer required to make their own: + + ActiveSupport::Notifications.subscribe('wait') do |event| + @event = event + end + + ActiveSupport::Notifications.instrument('wait') do + sleep 1 + end + + p @event.allocations # => 7 + p @event.cpu_time # => 0.256 + p @event.idle_time # => 1003.2399 + + Now you can enjoy event objects without making them yourself. Neat! + + *Aaron "t.lo" Patterson* + +* Add cpu_time, idle_time, and allocations to Event + + *Eileen M. Uchitelle*, *Aaron Patterson* + * RedisCacheStore: support key expiry in increment/decrement. Pass `:expires_in` to `#increment` and `#decrement` to set a Redis EXPIRE on the key. diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index a1b841ec3d..c266b432c0 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -497,9 +497,7 @@ module ActiveSupport arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) } end - def nested - @nested - end + attr_reader :nested def final? !@call_template @@ -578,7 +576,7 @@ module ActiveSupport end protected - def chain; @chain; end + attr_reader :chain private diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 8b73270894..404404cad1 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -210,9 +210,7 @@ module ActiveSupport OpenSSL::Cipher.new(@cipher) end - def verifier - @verifier - end + attr_reader :verifier def aead_mode? @aead_mode ||= new_cipher.authenticated? diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 25aab175b4..4e4ca70942 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -70,12 +70,29 @@ module ActiveSupport module Subscribers # :nodoc: def self.new(pattern, listener) + subscriber_class = Timed + if listener.respond_to?(:start) && listener.respond_to?(:finish) - subscriber = Evented.new pattern, listener + subscriber_class = Evented else - subscriber = Timed.new pattern, listener + # Doing all this to detect a block like `proc { |x| }` vs + # `proc { |*x| }` or `proc { |**x| }` + if listener.respond_to?(:parameters) + params = listener.parameters + if params.length == 1 && params.first.first == :opt + subscriber_class = EventObject + end + end end + wrap_all pattern, subscriber_class.new(pattern, listener) + end + + def self.event_object_subscriber(pattern, block) + wrap_all pattern, EventObject.new(pattern, block) + end + + def self.wrap_all(pattern, subscriber) unless pattern AllMessages.new(subscriber) else @@ -130,6 +147,27 @@ module ActiveSupport end end + class EventObject < Evented + def start(name, id, payload) + stack = Thread.current[:_event_stack] ||= [] + event = build_event name, id, payload + event.start! + stack.push event + end + + def finish(name, id, payload) + stack = Thread.current[:_event_stack] + event = stack.pop + event.finish! + @delegate.call event + end + + private + def build_event(name, id, payload) + ActiveSupport::Notifications::Event.new name, nil, nil, id, payload + end + end + class AllMessages # :nodoc: def initialize(delegate) @delegate = delegate diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index e99f5ee688..455b7a44a6 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -63,6 +63,42 @@ module ActiveSupport @end = ending @children = [] @duration = nil + @cpu_time_start = nil + @cpu_time_finish = nil + @allocation_count_start = 0 + @allocation_count_finish = 0 + end + + # Record information at the time this event starts + def start! + @time = now + @cpu_time_start = now_cpu + @allocation_count_start = now_allocations + end + + # Record information at the time this event finishes + def finish! + @cpu_time_finish = now_cpu + @end = now + @allocation_count_finish = now_allocations + end + + # Returns the CPU time (in milliseconds) passed since the call to + # +start!+ and the call to +finish!+ + def cpu_time + (@cpu_time_finish - @cpu_time_start) * 1000 + end + + # Returns the idle time time (in milliseconds) passed since the call to + # +start!+ and the call to +finish!+ + def idle_time + duration - cpu_time + end + + # Returns the number of allocations made since the call to +start!+ and + # the call to +finish!+ + def allocations + @allocation_count_finish - @allocation_count_start end # Returns the difference in milliseconds between when the execution of the @@ -88,6 +124,25 @@ module ActiveSupport def parent_of?(event) @children.include? event end + + private + def now + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + + def now_cpu + Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID) + end + + if defined?(JRUBY_VERSION) + def now_allocations + 0 + end + else + def now_allocations + GC.stat :total_allocated_objects + end + end end end end diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb index 8ad39f7a05..5a4c3d74af 100644 --- a/activesupport/lib/active_support/subscriber.rb +++ b/activesupport/lib/active_support/subscriber.rb @@ -79,7 +79,8 @@ module ActiveSupport end def start(name, id, payload) - e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload) + e = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload) + e.start! parent = event_stack.last parent << e if parent @@ -87,9 +88,8 @@ module ActiveSupport end def finish(name, id, payload) - finished = Time.now - event = event_stack.pop - event.end = finished + event = event_stack.pop + event.finish! event.payload.merge!(payload) method = name.split(".".freeze).first @@ -97,7 +97,6 @@ module ActiveSupport end private - def event_stack SubscriberQueueRegistry.instance.get_queue(@queue_key) end diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb index 59c8486f41..1caac1feb3 100644 --- a/activesupport/lib/active_support/testing/parallelization.rb +++ b/activesupport/lib/active_support/testing/parallelization.rb @@ -26,25 +26,21 @@ module ActiveSupport def pop; @queue.pop; end end - @after_fork_hooks = [] + @@after_fork_hooks = [] def self.after_fork_hook(&blk) - @after_fork_hooks << blk + @@after_fork_hooks << blk end - def self.after_fork_hooks - @after_fork_hooks - end + cattr_reader :after_fork_hooks - @run_cleanup_hooks = [] + @@run_cleanup_hooks = [] def self.run_cleanup_hook(&blk) - @run_cleanup_hooks << blk + @@run_cleanup_hooks << blk end - def self.run_cleanup_hooks - @run_cleanup_hooks - end + cattr_reader :run_cleanup_hooks def initialize(queue_size) @queue_size = queue_size diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 792c88415c..fd07a3a6a2 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -265,7 +265,7 @@ module ActiveSupport private def load_country_zones(code) country = TZInfo::Country.get(code) - country.zone_identifiers.map do |tz_id| + country.zone_identifiers.flat_map do |tz_id| if MAPPING.value?(tz_id) MAPPING.inject([]) do |memo, (key, value)| memo << self[key] if value == tz_id @@ -274,7 +274,7 @@ module ActiveSupport else create(tz_id, nil, TZInfo::Timezone.new(tz_id)) end - end.flatten(1).sort! + end.sort! end def zones_map diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb index 3b873de383..305a2c184d 100644 --- a/activesupport/test/cache/stores/redis_cache_store_test.rb +++ b/activesupport/test/cache/stores/redis_cache_store_test.rb @@ -103,9 +103,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests private def build(**kwargs) - ActiveSupport::Cache::RedisCacheStore.new(driver: DRIVER, **kwargs).tap do |cache| - cache.redis - end + ActiveSupport::Cache::RedisCacheStore.new(driver: DRIVER, **kwargs).tap(&:redis) end end diff --git a/activesupport/test/log_subscriber_test.rb b/activesupport/test/log_subscriber_test.rb index 2af9b1de30..4f413b9627 100644 --- a/activesupport/test/log_subscriber_test.rb +++ b/activesupport/test/log_subscriber_test.rb @@ -75,6 +75,17 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event end + def test_event_attributes + ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber + instrument "some_event.my_log_subscriber" + wait + event = @log_subscriber.event + assert_operator event.duration, :>, 0 + assert_operator event.cpu_time, :>, 0 + assert_operator event.idle_time, :>, 0 + assert_operator event.allocations, :>, 0 + end + def test_does_not_send_the_event_if_it_doesnt_match_the_class ActiveSupport::LogSubscriber.attach_to :my_log_subscriber, @log_subscriber instrument "unknown_event.my_log_subscriber" diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index d035f993f7..62817a839a 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -26,6 +26,42 @@ module Notifications end end + class SubscribeEventObjects < TestCase + def test_subscribe_events + events = [] + @notifier.subscribe do |event| + events << event + end + + ActiveSupport::Notifications.instrument("foo") + event = events.first + assert event, "should have an event" + assert_operator event.allocations, :>, 0 + assert_operator event.cpu_time, :>, 0 + assert_operator event.idle_time, :>, 0 + assert_operator event.duration, :>, 0 + end + + def test_subscribe_via_top_level_api + old_notifier = ActiveSupport::Notifications.notifier + ActiveSupport::Notifications.notifier = ActiveSupport::Notifications::Fanout.new + + event = nil + ActiveSupport::Notifications.subscribe("foo") do |e| + event = e + end + + ActiveSupport::Notifications.instrument("foo") do + 100.times { Object.new } # allocate at least 100 objects + end + + assert event + assert_operator event.allocations, :>=, 100 + ensure + ActiveSupport::Notifications.notifier = old_notifier + end + end + class SubscribedTest < TestCase def test_subscribed name = "foo" |