Jason Clark
@jasonrclark
Ruby Agent Engineer
Make an Event of It!
Evented Patterns in Ruby
Thursday, January 9, 14
Slide 2
Slide 2 text
Where Are We Going?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Eventing
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
2
Thursday, January 9, 14
Slide 3
Slide 3 text
The Pattern
http://flic.kr/p/6K9jC4
Thursday, January 9, 14
Slide 4
Slide 4 text
4
Thursday, January 9, 14
Slide 5
Slide 5 text
5
Thursday, January 9, 14
Slide 6
Slide 6 text
5
Thursday, January 9, 14
Slide 7
Slide 7 text
5
Thursday, January 9, 14
Slide 8
Slide 8 text
Events Aren't
(Necessarily)
6
• Asynchronous
• IO related
• Distributed
• Complicated
Thursday, January 9, 14
Slide 9
Slide 9 text
7
Thursday, January 9, 14
Slide 10
Slide 10 text
7
Notifier
Thursday, January 9, 14
Slide 11
Slide 11 text
7
Notifier
Subscriber
Subscriber
Subscriber
Thursday, January 9, 14
Slide 12
Slide 12 text
7
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 13
Slide 13 text
7
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 14
Slide 14 text
7
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 15
Slide 15 text
8
Why Not Methods?
Thursday, January 9, 14
Slide 16
Slide 16 text
9
Thursday, January 9, 14
Slide 17
Slide 17 text
9
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 18
Slide 18 text
9
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 19
Slide 19 text
Why Not Callbacks?
10
Thursday, January 9, 14
Slide 20
Slide 20 text
11
class Subscription < ActiveRecord::Base
before_create :record_signup
private
def record_signup
self.signed_up_on = Date.today
end
end
Thursday, January 9, 14
Slide 21
Slide 21 text
11
class Subscription < ActiveRecord::Base
before_create :record_signup
private
def record_signup
self.signed_up_on = Date.today
end
end
Thursday, January 9, 14
Slide 22
Slide 22 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Eventing
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
12
Thursday, January 9, 14
Slide 23
Slide 23 text
13
Internal Coupling
http://flic.kr/p/KikA9
Thursday, January 9, 14
Slide 24
Slide 24 text
14
newrelic_rpm
Thursday, January 9, 14
Slide 25
Slide 25 text
14
app start
newrelic_rpm
Thursday, January 9, 14
Slide 26
Slide 26 text
14
app start connect
New Relic
Server
newrelic_rpm
Thursday, January 9, 14
Slide 27
Slide 27 text
{server config}
14
app start connect
New Relic
Server
newrelic_rpm
Thursday, January 9, 14
Slide 28
Slide 28 text
{server config}
14
app start connect
New Relic
Server
?
newrelic_rpm
Thursday, January 9, 14
Slide 29
Slide 29 text
15
def connect()
config = config_from_server()
finish_setup(config)
end
newrelic_rpm
Thursday, January 9, 14
Slide 30
Slide 30 text
16
def finish_setup(config)
add_naming_rule(config['rules'])
@cross_app_tracing.set_key(config)
@js_instrumentor.log_config(config)
@beacon = Beacon.new
end
newrelic_rpm
Thursday, January 9, 14
Slide 31
Slide 31 text
17
def test_finish_setup_naming_rules
config = { 'rules' => [...] }
@agent.finish_setup(config)
assert_equal 2, @agent.transaction_rules.size
end
newrelic_rpm
Thursday, January 9, 14
22
def finish_setup(config)
add_naming_rule(config['rules'])
@cross_app_tracing.set_key(config)
@js_instrumentor.log_config(config)
@beacon = Beacon.new
end
newrelic_rpm
Thursday, January 9, 14
Slide 37
Slide 37 text
23
def finish_setup(config)
events.notify(:configured)
end
newrelic_rpm
Thursday, January 9, 14
Slide 38
Slide 38 text
24
class Beacon
def initialize
events.subscribe(:configured) do
Net::HTTP.get("http://...")
end
end
end
newrelic_rpm
Thursday, January 9, 14
Slide 39
Slide 39 text
25
def test_finish_setup_notifies
called = false
@events.subscribe(:configured) do
called = true
end
@agent.finish_setup({})
assert called
end
newrelic_rpm
Thursday, January 9, 14
Slide 40
Slide 40 text
26
def test_add_naming_rules
config = { 'rules' => [...] }
@agent.add_naming_rules(config)
assert_equal 2, @agent.transaction_rules.size
end
newrelic_rpm
Thursday, January 9, 14
Slide 41
Slide 41 text
More Focused
Tests
27
Thursday, January 9, 14
Slide 42
Slide 42 text
More Independent
Classes
28
Thursday, January 9, 14
Slide 43
Slide 43 text
Implicit Ordering
29
Thursday, January 9, 14
Slide 44
Slide 44 text
Debugging
30
Thursday, January 9, 14
Slide 45
Slide 45 text
Performance
31
Thursday, January 9, 14
Slide 46
Slide 46 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Eventing
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
32
Thursday, January 9, 14
Slide 47
Slide 47 text
http://flic.kr/p/euQpT
External Coupling
Thursday, January 9, 14
Slide 48
Slide 48 text
34
newrelic_rpm
Thursday, January 9, 14
Slide 49
Slide 49 text
34
Javascript insertion
newrelic_rpm
Thursday, January 9, 14
Slide 50
Slide 50 text
34
Javascript insertion
Error collection
newrelic_rpm
Thursday, January 9, 14
35
No Setup Please!
newrelic_rpm
Thursday, January 9, 14
Slide 53
Slide 53 text
36
module NewRelic::Rack
class AgentHooks
def call(env)
notify(:before_call, env)
result = @app.call(env)
notify(:after_call, env, result)
result
end
end
end
newrelic_rpm
Thursday, January 9, 14
Slide 54
Slide 54 text
37
class CrossAppTracing
def initialize
events.subscribe(:before_call) do |e|
process_request(e)
end
...
end
end
newrelic_rpm
Thursday, January 9, 14
Slide 55
Slide 55 text
Ever Heard of
Composition?
38
Thursday, January 9, 14
Slide 56
Slide 56 text
Looser Coupling
39
Thursday, January 9, 14
Slide 57
Slide 57 text
Compatibilty
Layer
40
Thursday, January 9, 14
Slide 58
Slide 58 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Eventing
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
41
Thursday, January 9, 14
Slide 59
Slide 59 text
http://flic.kr/p/dYor95
Adding Eventing
Thursday, January 9, 14
Slide 60
Slide 60 text
43
Thursday, January 9, 14
Slide 61
Slide 61 text
43
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 62
Slide 62 text
43
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 63
Slide 63 text
44
gem install
simple_events
http://github.com/jasonrclark/simple_events
Thursday, January 9, 14
Slide 64
Slide 64 text
45
class SimpleEvents::Notifier
def initialize
@events = {}
...
end
end
simple_events
Thursday, January 9, 14
Slide 65
Slide 65 text
46
class SimpleEvents::Notifier
...
def subscribe(event, &handler)
@events[event] ||= []
@events[event] << handler
check_for_runaway_subscriptions(event)
end
...
end
simple_events
Thursday, January 9, 14
Slide 66
Slide 66 text
47
class SimpleEvents::Notifier
...
def notify(event, *args)
return unless @events.has_key?(event)
@events[event].each do |handler|
begin
handler.call(*args)
rescue => err
logger.debug("Fail #{@event}", err)
end
end
end
end
simple_events
Thursday, January 9, 14
Slide 67
Slide 67 text
48
class SimpleEvents::Notifier
...
def notify(event, *args)
return unless @events.has_key?(event)
@events[event].each do |handler|
begin
handler.call(*args)
rescue => err
logger.debug("Fail #{@event}", err)
end
end
end
end
simple_events
Thursday, January 9, 14
Slide 68
Slide 68 text
49
class SimpleEvents::Notifier
...
def notify(event, *args)
return unless @events.has_key?(event)
@events[event].each do |handler|
begin
handler.call(*args)
rescue => err
logger.debug("Fail #{@event}", err)
end
end
end
end
simple_events
Thursday, January 9, 14
Slide 69
Slide 69 text
50
class SimpleEvents::Notifier
...
def notify(event, *args)
return unless @events.has_key?(event)
@events[event].each do |handler|
begin
handler.call(*args)
rescue => err
logger.debug("Fail #{@event}", err)
end
end
end
end
simple_events
Thursday, January 9, 14
Slide 70
Slide 70 text
51
class SimpleEvents::Notifier
...
def notify(event, *args)
return unless @events.has_key?(event)
@events[event].each do |handler|
begin
handler.call(*args)
rescue => err
logger.debug("Fail #{@event}", err)
end
end
end
end
simple_events
Thursday, January 9, 14
Slide 71
Slide 71 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Eventing
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
52
Thursday, January 9, 14
Slide 72
Slide 72 text
ActiveSupport::
Notifications
http://flic.kr/p/aWNwU2
Thursday, January 9, 14
Slide 73
Slide 73 text
54
AS::Notifications
=
ActiveSupport::Notifications
activesupport
Thursday, January 9, 14
Slide 74
Slide 74 text
Rails Request
55
•action_dispatch.request
•start_processing.action_controller
•process_action.action_controller
•sql.active_record
•render_template.action_view
• ... and more!
Thursday, January 9, 14
Slide 75
Slide 75 text
56
events = []
AS::Notifications.subscribe("event") do |*args|
events << *args
end
activesupport
Thursday, January 9, 14
Slide 76
Slide 76 text
57
AS::Notifications.subscribe("event") do |*args|
events << *args
end
["render_template.action_view",
"e2a1e92a3c613576c2b0"
{:identifier=>"/file/path/here"}]
activesupport
Thursday, January 9, 14
Slide 77
Slide 77 text
58
e = AS::Notifications::Event.new(*args)
e.name # => "render_template..."
e.duration # => 10 (in milliseconds)
e.payload # => { :identifier => ... }
activesupport
Thursday, January 9, 14
Slide 78
Slide 78 text
59
activesupport
AS::Notifications.instrument("evt", :data => 1)
Thursday, January 9, 14
Slide 79
Slide 79 text
60
AS::Notifications.instrument("evt", :data => 1) do
#... your timed code here
end
activesupport
Thursday, January 9, 14
Slide 80
Slide 80 text
• Regexp on Notifications.subscribe
• Event#parent_of?
• Temporary subscription
• Unsubscribe
• LogSubscriber
61
activesupport
Thursday, January 9, 14
Slide 81
Slide 81 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Events
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
62
Thursday, January 9, 14
Slide 82
Slide 82 text
http://flic.kr/p/a1dEec
Notifiers
Thursday, January 9, 14
Slide 83
Slide 83 text
64
Thursday, January 9, 14
Slide 84
Slide 84 text
64
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 85
Slide 85 text
64
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 86
Slide 86 text
Naming
65
Thursday, January 9, 14
Slide 87
Slide 87 text
Flexible Payload
66
Thursday, January 9, 14
Slide 88
Slide 88 text
Primitives at
Boundaries
67
Thursday, January 9, 14
Slide 89
Slide 89 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Events
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
68
Thursday, January 9, 14
Slide 90
Slide 90 text
69
http://flic.kr/p/8w9PyF
Subscribers
Thursday, January 9, 14
Slide 91
Slide 91 text
70
Thursday, January 9, 14
Slide 92
Slide 92 text
70
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 93
Slide 93 text
70
Notifier
Subscriber
Subscriber
Subscriber
Eventing
System
Thursday, January 9, 14
Slide 94
Slide 94 text
!= self.only
71
Thursday, January 9, 14
Slide 95
Slide 95 text
72
Thursday, January 9, 14
Slide 96
Slide 96 text
73
class UserSignup < Mutations::Command
def execute
user = User.create!(inputs)
NewsletterSubscriptions.create(user.id)
UserMailer.async(:deliver_welcome, user.id)
user
end
end
Thursday, January 9, 14
Slide 97
Slide 97 text
74
class SomeSubscriber
def initialize
events.subscribe(:configured) do
log_config
end
end
def log_config
...
end
end
Thursday, January 9, 14
Slide 98
Slide 98 text
Memory Leaks
75
Thursday, January 9, 14
Slide 99
Slide 99 text
Where Have We Been?
• The Pattern
• Internal Coupling
• External Coupling
• Adding Events
• ActiveSupport::Notifications
• Notifiers
• Subscribers
• Events as Data
76
Thursday, January 9, 14
Slide 100
Slide 100 text
http://flic.kr/p/ndraB
Events as Data
Thursday, January 9, 14
Slide 101
Slide 101 text
78
Opened Account $100
Check 0001 -$15
Deposit $20
Check 0002 -$21
Total $84
Bank Transactions
Thursday, January 9, 14
Slide 102
Slide 102 text
79
Customer Pricing
Thursday, January 9, 14
Slide 103
Slide 103 text
79
customer_id date plan
1 1/1/2013 basic
1 1/2/2013 premium
1 2/1/2013 basic
1 2/2/2013 premium
Customer Pricing
Thursday, January 9, 14
Slide 104
Slide 104 text
79
customer_id date plan
1 1/1/2013 basic
1 1/2/2013 premium
1 2/1/2013 basic
1 2/2/2013 premium
Customer Pricing
Thursday, January 9, 14
Slide 105
Slide 105 text
Event Sourcing
80
Thursday, January 9, 14
Slide 106
Slide 106 text
81 http://flic.kr/p/4LTNc9
Thursday, January 9, 14
Slide 107
Slide 107 text
82
Metrics
10:00AM
CPU/Utilization
0.85
Thursday, January 9, 14
Slide 108
Slide 108 text
83
Metrics
10:01AM
CPU/Utilization
0.24
Thursday, January 9, 14
Slide 109
Slide 109 text
84
Metrics
Thursday, January 9, 14
Slide 110
Slide 110 text
85
Metrics
Thursday, January 9, 14
Slide 111
Slide 111 text
86
Metrics
Thursday, January 9, 14
Slide 112
Slide 112 text
87
Metric Data
Thursday, January 9, 14
Slide 113
Slide 113 text
87
Metric Data
Thursday, January 9, 14
Slide 114
Slide 114 text
87
Metric Data
Thursday, January 9, 14
Slide 115
Slide 115 text
Other Examples
88
• Database replication
• Log files
• PubSub
• Immutable data structures
Thursday, January 9, 14