Extending Gems
Patterns and Anti-Patterns of
Making Your Gem Pluggable
Jason Clark
@jasonrclark
Ruby Agent Engineer
Monday, November 11, 13
Slide 2
Slide 2 text
2
Storytime!
Monday, November 11, 13
Slide 3
Slide 3 text
3
newrelic_rpm
module NewRelic::Agent::BrowserMonitoring
def browser_monitoring_queue_time
queue_time = current_transaction.queue_time
millis = queue_time.to_f * 1000.0
clamp_to_positive(millis.round)
end
def footer_js_string(config)
"... #{browser_monitoring_queue_time}"
end
end
Monday, November 11, 13
Slide 4
Slide 4 text
4
module NewRelic::Agent::BrowserMonitoring
def current_timings
TransactionTimings.new
end
def footer_js_string(config)
"... #{current_timings.queue_time_in_millis}"
end
end
newrelic_rpm
Monday, November 11, 13
Slide 5
Slide 5 text
5
Meanwhile...
Elsewhere At New Relic
http://flic.kr/p/9xEUug
Monday, November 11, 13
Slide 6
Slide 6 text
6
module NewRelic::Agent::BrowserMonitoring
# MONKEY PATCHED!
def footer_js_string(config)
"... #{browser_monitoring_queue_time}"
end
end
rpm_site
Monday, November 11, 13
Slide 7
Slide 7 text
7
http://pixabay.com/en/elephant-foot-ten-foot-elephant-52536/
Monday, November 11, 13
Slide 8
Slide 8 text
8
Monday, November 11, 13
Slide 9
Slide 9 text
9
Reskiq
It forks then it threads!
Monday, November 11, 13
Slide 10
Slide 10 text
10
gem install
my_awesome_code
Monday, November 11, 13
Slide 11
Slide 11 text
Where We're Going
11
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 12
Slide 12 text
Pass It On
12
Monday, November 11, 13
Slide 13
Slide 13 text
Loggers
13
Monday, November 11, 13
Slide 14
Slide 14 text
activerecord
14
ActiveRecord::Base.logger =
::Logger.new("my_special.log")
Monday, November 11, 13
Slide 15
Slide 15 text
15 http://flic.kr/p/7GDRue
Dependency Injection
Monday, November 11, 13
Slide 16
Slide 16 text
16
http://flic.kr/p/gzxjKu
Duck Typing
Monday, November 11, 13
excon
19
class SimpleInstrumentor
class << self
attr_accessor :events
def instrument(name, params = {}, &blk)
puts "#{name} just happened."
yield if block_given?
end
end
end
Monday, November 11, 13
Slide 20
Slide 20 text
20
http://pixabay.com/en/elephant-foot-ten-foot-elephant-52536/
Monday, November 11, 13
Slide 21
Slide 21 text
Backends
21
Monday, November 11, 13
Slide 22
Slide 22 text
Delayed::Job
22
# without delayed_job
@user.activate!(@device)
# with delayed_job
@user.delay.activate!(@device)
Monday, November 11, 13
Slide 23
Slide 23 text
Delayed::Job
23
Monday, November 11, 13
Slide 24
Slide 24 text
Delayed::Job
24
::Delayed.const_set(:Job, backend)
Monday, November 11, 13
Slide 25
Slide 25 text
25
newrelic_rpm
def failed_jobs
Delayed::Job.count(
:conditions => 'failed_at is not NULL')
end
Monday, November 11, 13
Slide 26
Slide 26 text
Gem-Specific
26
Monday, November 11, 13
Slide 27
Slide 27 text
Resque
27
class SendMessageJob
def self.perform(id, text)
user = User.find(id)
user.send_message(text)
end
end
Resque.enqueue(SendMessageJob, 42, "Yo")
Monday, November 11, 13
Slide 28
Slide 28 text
28
Resque
https://github.com/resque/resque/blob/master/docs/HOOKS.md
class SendMessageJob
def self.before_perform_log_it(*args)
logger.debug("Messaging #{args}")
end
...
end
Monday, November 11, 13
Slide 29
Slide 29 text
Where We've Been
29
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 30
Slide 30 text
Events
30
Monday, November 11, 13
Slide 31
Slide 31 text
What's Important?
31
Monday, November 11, 13
Slide 32
Slide 32 text
32
Monday, November 11, 13
Slide 33
Slide 33 text
Surfacing Events
33
Monday, November 11, 13
Slide 34
Slide 34 text
34
newrelic_rpm
events = NewRelic::Agent.instance.events
events.subscribe(:start_transaction) do
set_transaction_custom_parameters
end
Monday, November 11, 13
Slide 35
Slide 35 text
35
Resque
Resque.after_fork do |job|
ActiveRecord::Base.establish_connection
end
Monday, November 11, 13
Slide 36
Slide 36 text
36
http://pixabay.com/en/elephant-foot-ten-foot-elephant-52536/
Monday, November 11, 13
Slide 37
Slide 37 text
Ordering?
37
Monday, November 11, 13
Slide 38
Slide 38 text
ActiveSupport::Notifications
38
activesupport
Monday, November 11, 13
Slide 39
Slide 39 text
Where We've Been
39
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 40
Slide 40 text
Middleware
40
Monday, November 11, 13
Slide 41
Slide 41 text
41
Rack::Cache
ActionDispatch::Cookies
YourApplication
Web Request
Monday, November 11, 13
Slide 42
Slide 42 text
42
# [status, headers, response]
def call(env)
# ... before
result = @app.call(env)
# ... after
result
end
Monday, November 11, 13
Slide 43
Slide 43 text
43
# [status, headers, response]
def call(env)
# ... before
result = @app.call(env)
# ... after
result
end
Monday, November 11, 13
88
unicorn
worker_processes 5
preload_app true
timeout 30
after_fork do |server, worker|
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end
Monday, November 11, 13
Slide 92
Slide 92 text
89
on_worker_boot do
ActiveSupport.on_load(:active_record) do
ActiveRecord::Base.establish_connection
end
end
Monday, November 11, 13
Slide 93
Slide 93 text
Great for Apps?
What about Gems?
90
Monday, November 11, 13
Slide 94
Slide 94 text
91
newrelic_rpm
if defined?(::Puma) &&
::Puma.respond_to?(:cli_config)
config = ::Puma.cli_config
config.options[:worker_boot] << Proc.new do
NewRelic::Agent.after_fork(...)
end
end
Monday, November 11, 13
Slide 95
Slide 95 text
92
newrelic_rpm
if defined?(::Puma) &&
::Puma.respond_to?(:cli_config)
config = ::Puma.cli_config
config.options[:worker_boot] << Proc.new do
NewRelic::Agent.after_fork(...)
end
end
Monday, November 11, 13
Slide 96
Slide 96 text
Where We've Been
93
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 97
Slide 97 text
Docs
94
Monday, November 11, 13
Slide 98
Slide 98 text
README
95
Monday, November 11, 13
Slide 99
Slide 99 text
sinatra
96
Monday, November 11, 13
Slide 100
Slide 100 text
97
Monday, November 11, 13
Slide 101
Slide 101 text
98
Monday, November 11, 13
Slide 102
Slide 102 text
99
Monday, November 11, 13
Slide 103
Slide 103 text
Expected
Extensions?
100
Monday, November 11, 13
Slide 104
Slide 104 text
101
Monday, November 11, 13
Slide 105
Slide 105 text
Don't Bury It
102
Monday, November 11, 13
Slide 106
Slide 106 text
103
Monday, November 11, 13
Slide 107
Slide 107 text
104
Monday, November 11, 13
Slide 108
Slide 108 text
Versioning
105
Monday, November 11, 13
Slide 109
Slide 109 text
106
Monday, November 11, 13
Slide 110
Slide 110 text
Where We've Been
107
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 111
Slide 111 text
Where We've Been
108
Monday, November 11, 13
Slide 112
Slide 112 text
Where We've Been
108
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Monday, November 11, 13
Slide 113
Slide 113 text
Where We've Been
108
• Pass It On
• Events
• Middleware
• Lifecycle
• Names and Paths
• Config
• Docs
Jason Clark
@jasonrclark
Ruby Agent Engineer
Monday, November 11, 13