Skip to content

Commit

Permalink
Add async store implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Kunkle committed Aug 13, 2011
1 parent 524d92d commit 8858f65
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/system_metrics.rb
Expand Up @@ -4,6 +4,7 @@ module SystemMetrics
autoload :Middleware, 'system_metrics/middleware'
autoload :NestedEvent, 'system_metrics/nested_event'
autoload :Store, 'system_metrics/store'
autoload :AsyncStore, 'system_metrics/async_store'
autoload :Version, 'system_metrics/version'

def self.collection_on
Expand Down
57 changes: 57 additions & 0 deletions lib/system_metrics/async_store.rb
@@ -0,0 +1,57 @@
require 'thread'

module SystemMetrics
class AsyncStore

# An instrumenter that does not send notifications. This is used in the
# AsyncStore so saving events does not send any notifications, not even
# for logging.
class VoidInstrumenter < ::ActiveSupport::Notifications::Instrumenter
def instrument(name, payload={})
yield(payload) if block_given?
end
end

def initialize
@queue = Queue.new
@thread = Thread.new do
set_void_instrumenter
consume
end
end

def save(events)
@queue << events
end

protected
def set_void_instrumenter
Thread.current[:"instrumentation_#{notifier.object_id}"] = VoidInstrumenter.new(notifier)
end

def notifier
ActiveSupport::Notifications.notifier
end

def consume
while events = @queue.pop
root_event = SystemMetrics::NestedEvent.arrange(events, :presort => false)
root_model = create_metric(root_event)
root_model.update_attributes(:request_id => root_model.id)
save_tree(root_event.children, root_model.id, root_model.id)
end
end

def save_tree(events, request_id, parent_id)
events.each do |event|
model = create_metric(event, :request_id => request_id, :parent_id => parent_id)
save_tree(event.children, request_id, model.id)
end
end

def create_metric(event, merge_params={})
SystemMetrics::Metric.create(event.to_hash.merge(merge_params))
end

end
end
2 changes: 1 addition & 1 deletion lib/system_metrics/config.rb
Expand Up @@ -3,7 +3,7 @@ class Config
attr_accessor :store, :instruments, :notification_exclude_patterns, :path_exclude_patterns

def initialize
self.store = SystemMetrics::Store.new
self.store = SystemMetrics::AsyncStore.new
self.notification_exclude_patterns = []
self.path_exclude_patterns = [/system\/metrics/, /system_metrics/]
self.instruments = [
Expand Down
50 changes: 50 additions & 0 deletions spec/system_metrics/async_store_spec.rb
@@ -0,0 +1,50 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'system_metrics/metric'

describe SystemMetrics::AsyncStore do
include NotificationsSupport

describe '#save' do
it 'should save an array of events hierarchically' do
parent = event(:start => Time.now - 10.seconds, :end => Time.now)
child = event(:start => Time.now - 9.seconds, :end => Time.now - 1.seconds)
grandchild = event(:start => Time.now - 8.seconds, :end => Time.now - 2.seconds)

store = SystemMetrics::AsyncStore.new

lambda {
store.save([grandchild, child, parent])
sleep(0.1)
}.should change(SystemMetrics::Metric, :count).by(3)

metrics = SystemMetrics::Metric.all
verify_equal(parent, metrics[0])
verify_equal(child, metrics[0].children[0])
verify_equal(grandchild, metrics[0].children[0].children[0])
end

it 'should not attempt to save anything if passed an empty array of events' do
store = SystemMetrics::AsyncStore.new
lambda { store.save([]); sleep(0.1) }.should_not change(SystemMetrics::Metric, :count)
end

it 'should not attempt to save anything if passed a nil' do
store = SystemMetrics::AsyncStore.new
lambda { store.save(nil); sleep(0.1) }.should_not change(SystemMetrics::Metric, :count)
end
end

private

def verify_equal(event, metric)
event.name.should == metric.name
event.action.should == metric.action
event.category.should == metric.category
event.transaction_id.should == metric.transaction_id
event.payload.should == metric.payload
event.started_at.should be_within(1).of(metric.started_at)
event.duration.should be_within(1).of(metric.duration)
event.exclusive_duration.should be_within(1).of(metric.exclusive_duration)
end

end
2 changes: 1 addition & 1 deletion spec/system_metrics/store_spec.rb
Expand Up @@ -11,7 +11,7 @@
grandchild = event(:start => Time.now - 8.seconds, :end => Time.now - 2.seconds)

store = SystemMetrics::Store.new

lambda {
store.save([grandchild, child, parent])
}.should change(SystemMetrics::Metric, :count).by(3)
Expand Down

0 comments on commit 8858f65

Please sign in to comment.