Slide 1

Slide 1 text

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

Slide 32

Slide 32 text

18 def test_finish_setup_naming_rules @agent.cross_app_tracing.stub(:set_key) @agent.js_instrumentor.stub(:log_config) config = { 'rules' => [...] } @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14

Slide 33

Slide 33 text

19 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 34

Slide 34 text

20 class Beacon def initialize # Your code is bad and you should feel bad Net::HTTP.get("http://...") end end newrelic_rpm Thursday, January 9, 14

Slide 35

Slide 35 text

21 def test_finish_setup_naming_rules @agent.cross_app_tracing.stub(:set_key) @agent.js_instrumentor.stub(:log_config) Beacon.stub(:new).return(stub("fake beacon")) config = { 'rules' => [...] } @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14

Slide 36

Slide 36 text

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

Slide 51

Slide 51 text

34 Javascript insertion Cross application tracing Error collection newrelic_rpm Thursday, January 9, 14

Slide 52

Slide 52 text

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

Slide 116

Slide 116 text

Upsides 89 ★No Updates ★Automatic Audits ★Summarize/Transform/ Cache Thursday, January 9, 14

Slide 117

Slide 117 text

Downsides -So Much Data! -Hard to Query Efficiently -More Complex Code 90 Thursday, January 9, 14

Slide 118

Slide 118 text

Jason Clark @jasonrclark Ruby Agent Engineer • The Pattern • Internal Coupling • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data ? Thursday, January 9, 14