time_bandits 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/Gemfile +6 -0
- data/README.rdoc +22 -13
- data/Rakefile +14 -0
- data/lib/time_bandits.rb +3 -1
- data/lib/time_bandits/monkey_patches/action_controller.rb +17 -4
- data/lib/time_bandits/monkey_patches/memcache-client.rb +10 -35
- data/lib/time_bandits/monkey_patches/memcached.rb +13 -54
- data/lib/time_bandits/rack/logger.rb +1 -1
- data/lib/time_bandits/rack/logger40.rb +1 -1
- data/lib/time_bandits/railtie.rb +12 -7
- data/lib/time_bandits/time_consumers/base_consumer.rb +57 -0
- data/lib/time_bandits/time_consumers/dalli.rb +51 -0
- data/lib/time_bandits/time_consumers/database.rb +10 -30
- data/lib/time_bandits/time_consumers/garbage_collection.rb +7 -11
- data/lib/time_bandits/time_consumers/mem_cache.rb +12 -15
- data/lib/time_bandits/time_consumers/memcached.rb +19 -14
- data/lib/time_bandits/version.rb +1 -1
- data/test/test_helper.rb +18 -0
- data/test/unit/active_support_notifications_test.rb +64 -0
- data/test/unit/base_test.rb +63 -0
- data/test/unit/memcached_test.rb +60 -0
- data/time_bandits.gemspec +2 -0
- metadata +41 -7
- data/init.rb +0 -12
- data/lib/time_bandits/monkey_patches/action_controller_rails2.rb +0 -162
- data/lib/time_bandits/monkey_patches/active_record_rails2.rb +0 -86
- data/lib/time_bandits/time_consumers/database_rails2.rb +0 -61
data/init.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# this file gets only loaded for rails2
|
2
|
-
require 'time_bandits'
|
3
|
-
require 'time_bandits/monkey_patches/active_record_rails2'
|
4
|
-
require 'time_bandits/monkey_patches/action_controller_rails2'
|
5
|
-
|
6
|
-
ActionController::Base.send :include, ActionController::TimeBanditry
|
7
|
-
|
8
|
-
TimeBandits::TimeConsumers::GarbageCollection.heap_dumps_enabled = %w(production development).include?(RAILS_ENV)
|
9
|
-
|
10
|
-
# TimeBandits.add TimeBandits::TimeConsumers::Memcached if defined?(Memcached)
|
11
|
-
# TimeBandits.add TimeBandits::TimeConsumers::GarbageCollection.instance if GC.respond_to? :enable_stats
|
12
|
-
# TimeBandits.add TimeBandits::TimeConsumers::JMX.instance if defined? JRUBY_VERSION
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# =========================================================================================================
|
2
|
-
# IMPORTANT: the plugin changes the ActionController#process method chain
|
3
|
-
#
|
4
|
-
# the original rails stack looks like this:
|
5
|
-
#
|
6
|
-
# ActionController::SessionManagement#process_with_session_management_support
|
7
|
-
# ActionController::Filters#process_with_filters
|
8
|
-
# ActionController::Base#process
|
9
|
-
# ActionController::Caching::SqlCache#perform_action_with_caching
|
10
|
-
# ActionController::Rescue#perform_action_with_rescue
|
11
|
-
# *** ActionController::Benchmarking#perform_action_with_benchmark ***
|
12
|
-
# ActionController::Filters#perform_action_with_filters (==> this runs the filters)
|
13
|
-
# ActionController::Base#perform_action (==> run the action and eventually call render)
|
14
|
-
# ActionController::Benchmarking#render_with_benchmark (==> this is where the rendering time gets computed)
|
15
|
-
# ActionController::Base::render
|
16
|
-
#
|
17
|
-
# with the plugin installed, the stack looks like this:
|
18
|
-
#
|
19
|
-
# ActionController::SessionManagement#process_with_session_management_support
|
20
|
-
# ActionController::Filters#process_with_filters
|
21
|
-
# ActionController::Base#process
|
22
|
-
# *** ActionController::Benchmarking#perform_action_with_time_bandits *** <=========== !!!!
|
23
|
-
# ActionController::Caching::SqlCache#perform_action_with_caching
|
24
|
-
# ActionController::Rescue#perform_action_with_rescue
|
25
|
-
# ActionController::Filters#perform_action_with_filters (==> this runs the filters)
|
26
|
-
# ActionController::Base#perform_action (==> run the action and eventually call render)
|
27
|
-
# ActionController::Benchmarking#render_with_benchmark (==> this is where the rendering time gets computed)
|
28
|
-
# ActionController::Base::render
|
29
|
-
# =========================================================================================================
|
30
|
-
|
31
|
-
module ActionController #:nodoc:
|
32
|
-
|
33
|
-
class Base
|
34
|
-
# this ugly hack is used to get the started_at and ip information into time bandits metrics
|
35
|
-
def request_origin
|
36
|
-
# this *needs* to be cached!
|
37
|
-
# otherwise you'd get different results if calling it more than once
|
38
|
-
@request_origin ||=
|
39
|
-
begin
|
40
|
-
remote_ip = request.remote_ip
|
41
|
-
t = Time.now
|
42
|
-
started_at = "#{t.to_s(:db)}.#{t.usec}"
|
43
|
-
request.env["time_bandits.metrics"] = {:ip => remote_ip, :started_at => started_at}
|
44
|
-
"#{remote_ip} at #{started_at}"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module TimeBanditry #:nodoc:
|
50
|
-
def self.included(base)
|
51
|
-
base.class_eval do
|
52
|
-
alias_method_chain :perform_action, :time_bandits
|
53
|
-
alias_method_chain :rescue_action, :time_bandits
|
54
|
-
|
55
|
-
# if timebandits are used, the default benchmarking is
|
56
|
-
# disabled. As alias_method_chain is unfriendly to extensions,
|
57
|
-
# this is done by skipping perform_action_with_benchmarks by
|
58
|
-
# calling perform_action_without_benchmarks at the appropriate
|
59
|
-
# place.
|
60
|
-
def perform_action_without_rescue
|
61
|
-
perform_action_without_benchmark
|
62
|
-
end
|
63
|
-
|
64
|
-
alias_method :render, :render_with_benchmark
|
65
|
-
end
|
66
|
-
|
67
|
-
TimeBandits.add TimeBandits::TimeConsumers::Database.instance
|
68
|
-
end
|
69
|
-
|
70
|
-
def render_with_benchmark(options = nil, extra_options = {}, &block)
|
71
|
-
if logger
|
72
|
-
before_rendering = TimeBandits.consumed
|
73
|
-
|
74
|
-
render_output = nil
|
75
|
-
@view_runtime = Benchmark::realtime { render_output = render_without_benchmark(options, extra_options, &block) }
|
76
|
-
|
77
|
-
other_time_consumed_during_rendering = TimeBandits.consumed - before_rendering
|
78
|
-
@view_runtime -= other_time_consumed_during_rendering
|
79
|
-
|
80
|
-
render_output
|
81
|
-
else
|
82
|
-
render_without_benchmark(options, extra_options, &block)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def perform_action_with_time_bandits
|
87
|
-
if logger
|
88
|
-
TimeBandits.reset
|
89
|
-
|
90
|
-
seconds = [ Benchmark::measure{ perform_action_without_time_bandits }.real, 0.0001 ].max
|
91
|
-
|
92
|
-
# need to call this to compute DB time/calls
|
93
|
-
TimeBandits.consumed
|
94
|
-
|
95
|
-
log_message = "Completed in #{sprintf("%.3f", seconds * 1000)}ms"
|
96
|
-
|
97
|
-
log_message << " ("
|
98
|
-
log_message << view_runtime
|
99
|
-
TimeBandits.time_bandits.each do |bandit|
|
100
|
-
log_message << ", #{bandit.runtime}"
|
101
|
-
end
|
102
|
-
log_message << ")"
|
103
|
-
|
104
|
-
log_message << " | #{response.status}"
|
105
|
-
log_message << " [#{complete_request_uri rescue "unknown"}]"
|
106
|
-
|
107
|
-
logger.info(log_message)
|
108
|
-
response.headers["X-Runtime"] = "#{sprintf("%.0f", seconds * 1000)}ms"
|
109
|
-
merge_metrics(seconds)
|
110
|
-
else
|
111
|
-
perform_action_without_time_bandits
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def rescue_action_with_time_bandits(exception)
|
116
|
-
# HACK!
|
117
|
-
if logger && !caller.any?{|c| c =~ /perform_action_without_time_bandits/ }
|
118
|
-
TimeBandits.reset
|
119
|
-
|
120
|
-
seconds = [ Benchmark::measure{ rescue_action_without_time_bandits(exception) }.real, 0.0001 ].max
|
121
|
-
|
122
|
-
# need to call this to compute DB time/calls
|
123
|
-
TimeBandits.consumed
|
124
|
-
|
125
|
-
log_message = "Completed in #{sprintf("%.3f", seconds * 1000)}ms"
|
126
|
-
|
127
|
-
log_message << " ("
|
128
|
-
log_message << view_runtime
|
129
|
-
TimeBandits.time_bandits.each do |bandit|
|
130
|
-
log_message << ", #{bandit.runtime}"
|
131
|
-
end
|
132
|
-
log_message << ")"
|
133
|
-
|
134
|
-
log_message << " | #{response.status}"
|
135
|
-
log_message << " [#{complete_request_uri rescue "unknown"}]"
|
136
|
-
|
137
|
-
logger.info(log_message)
|
138
|
-
response.headers["X-Runtime"] = "#{sprintf("%.0f", seconds * 1000)}ms"
|
139
|
-
merge_metrics(seconds)
|
140
|
-
else
|
141
|
-
rescue_action_without_time_bandits(exception)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
def merge_metrics(total_time_seconds)
|
148
|
-
basic_request_metrics = {
|
149
|
-
:total_time => total_time_seconds * 1000,
|
150
|
-
:view_time => (@view_runtime||0) * 1000,
|
151
|
-
:code => response.status.to_i,
|
152
|
-
:action => "#{self.class.name}\##{action_name}",
|
153
|
-
}
|
154
|
-
request.env["time_bandits.metrics"].merge!(TimeBandits.metrics).merge!(basic_request_metrics)
|
155
|
-
end
|
156
|
-
|
157
|
-
def view_runtime
|
158
|
-
"View: %.3f" % ((@view_runtime||0) * 1000)
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|
162
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
# this file monkey patches class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
2
|
-
# and the module module ActiveRecord::ConnectionAdapters::QueryCache
|
3
|
-
# to count the number of sql statements being executed.
|
4
|
-
# it needs to be adapted to each new rails version
|
5
|
-
|
6
|
-
raise "AR abstract adapter monkey patch for custom benchmarking is not compatible with your rails version" unless
|
7
|
-
%w(2.3.2 2.3.3 2.3.4 2.3.8 2.3.9 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14).include?(Rails::VERSION::STRING)
|
8
|
-
|
9
|
-
module ActiveRecord
|
10
|
-
module ConnectionAdapters
|
11
|
-
class ConnectionPool
|
12
|
-
attr_reader :connections
|
13
|
-
end
|
14
|
-
|
15
|
-
class AbstractAdapter
|
16
|
-
attr_accessor :call_count, :query_cache_hits
|
17
|
-
|
18
|
-
def initialize(connection, logger = nil) #:nodoc:
|
19
|
-
@connection, @logger = connection, logger
|
20
|
-
@runtime = 0
|
21
|
-
@call_count = 0
|
22
|
-
@last_verification = 0
|
23
|
-
@query_cache_enabled = false
|
24
|
-
@query_cache_hits = 0
|
25
|
-
end
|
26
|
-
|
27
|
-
def reset_call_count
|
28
|
-
calls = @call_count
|
29
|
-
@call_count = 0
|
30
|
-
calls
|
31
|
-
end
|
32
|
-
|
33
|
-
def reset_query_cache_hits
|
34
|
-
hits = @query_cache_hits
|
35
|
-
@query_cache_hits = 0
|
36
|
-
hits
|
37
|
-
end
|
38
|
-
|
39
|
-
protected
|
40
|
-
def log(sql, name)
|
41
|
-
if block_given?
|
42
|
-
result = nil
|
43
|
-
seconds = Benchmark.realtime { result = yield }
|
44
|
-
@runtime += seconds
|
45
|
-
@call_count += 1
|
46
|
-
log_info(sql, name, seconds * 1000)
|
47
|
-
result
|
48
|
-
else
|
49
|
-
log_info(sql, name, 0)
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
rescue Exception => e
|
53
|
-
# Log message and raise exception.
|
54
|
-
# Set last_verification to 0, so that connection gets verified
|
55
|
-
# upon reentering the request loop
|
56
|
-
@last_verification = 0
|
57
|
-
message = "#{e.class.name}: #{e.message}: #{sql}"
|
58
|
-
log_info(message, name, 0)
|
59
|
-
raise ActiveRecord::StatementInvalid, message
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
module QueryCache
|
64
|
-
private
|
65
|
-
def cache_sql(sql)
|
66
|
-
result =
|
67
|
-
if @query_cache.has_key?(sql)
|
68
|
-
@query_cache_hits += 1
|
69
|
-
log_info(sql, "CACHE", 0.0)
|
70
|
-
@query_cache[sql]
|
71
|
-
else
|
72
|
-
@query_cache[sql] = yield
|
73
|
-
end
|
74
|
-
|
75
|
-
if Array === result
|
76
|
-
result.collect { |row| row.dup }
|
77
|
-
else
|
78
|
-
result.duplicable? ? result.dup : result
|
79
|
-
end
|
80
|
-
rescue TypeError
|
81
|
-
result
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# Database time consumer for Rails2. You need to add it via
|
2
|
-
#
|
3
|
-
# TimeBandits.add TimeBandits::TimeConsumers::Database.instance
|
4
|
-
#
|
5
|
-
|
6
|
-
module TimeBandits
|
7
|
-
module TimeConsumers
|
8
|
-
# provide a time consumer interface to ActiveRecord for perform_action_with_benchmark and render_with_benchmark
|
9
|
-
class Database
|
10
|
-
def initialize
|
11
|
-
@consumed = 0.0
|
12
|
-
@call_count = 0
|
13
|
-
@query_cache_hits = 0
|
14
|
-
end
|
15
|
-
private :initialize
|
16
|
-
|
17
|
-
def self.instance
|
18
|
-
@instance ||= new
|
19
|
-
end
|
20
|
-
|
21
|
-
def reset
|
22
|
-
reset_stats
|
23
|
-
@call_count = 0
|
24
|
-
@consumed = 0.0
|
25
|
-
@query_cache_hits = 0
|
26
|
-
end
|
27
|
-
|
28
|
-
def consumed
|
29
|
-
hits, calls, time = reset_stats
|
30
|
-
@query_cache_hits += hits
|
31
|
-
@call_count += calls
|
32
|
-
@consumed += time
|
33
|
-
end
|
34
|
-
|
35
|
-
def runtime
|
36
|
-
sprintf "DB: %.3f(%d,%d)", @consumed * 1000, @call_count, @query_cache_hits
|
37
|
-
end
|
38
|
-
|
39
|
-
def metrics
|
40
|
-
{
|
41
|
-
:db_calls => @call_count,
|
42
|
-
:db_sql_query_cache_hits => @query_cache_hits,
|
43
|
-
:db_time => @consumed * 1000
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
def all_connections
|
49
|
-
ActiveRecord::Base.connection_handler.connection_pools.values.map{|pool| pool.connections}.flatten
|
50
|
-
end
|
51
|
-
|
52
|
-
def reset_stats
|
53
|
-
connections = all_connections
|
54
|
-
hits = connections.map{|c| c.reset_query_cache_hits}.sum
|
55
|
-
calls = connections.map{|c| c.reset_call_count}.sum
|
56
|
-
time = connections.map{|c| c.reset_runtime}.sum
|
57
|
-
[hits, calls, time]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|