Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Make an Event of It! (ConFoo 2014)

Make an Event of It! (ConFoo 2014)

The upsurge in asynchronous programming has brought event-driven patterns to the forefront. But even without a rewrite in Node.js, the concept of eventing can improve your application.

This talk demonstrates how events benefit the layers of your application. We'll see how events aid in the fight against fat controllers. We'll use eventing to simplify tests and manage dependencies. We'll even see events reshape our data for maximum flexibility.

92e7389893670a1920a4fd98aec0d246?s=128

Jason R Clark

February 27, 2014
Tweet

Transcript

  1. Jason Clark @jasonrclark Ruby Agent Engineer Make an Event of

    It! Evented Patterns in Ruby Wednesday, February 26, 14
  2. 2 Wednesday, February 26, 14

  3. 3 Wednesday, February 26, 14

  4. 3 Wednesday, February 26, 14

  5. 3 Wednesday, February 26, 14

  6. Events Aren't (Necessarily) 4 • Asynchronous • IO related •

    Distributed • Complicated Wednesday, February 26, 14
  7. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 5 Wednesday, February 26, 14
  8. The Pattern http://flic.kr/p/6K9jC4 Wednesday, February 26, 14

  9. 7 Wednesday, February 26, 14

  10. 7 Notifier Wednesday, February 26, 14

  11. 7 Notifier Subscriber Subscriber Subscriber Wednesday, February 26, 14

  12. 7 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  13. 7 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  14. 7 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  15. 8 Why Not Methods? Wednesday, February 26, 14

  16. 9 Wednesday, February 26, 14

  17. 9 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  18. 9 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  19. Why Not Callbacks? 10 Wednesday, February 26, 14

  20. 11 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Wednesday, February 26, 14
  21. 11 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Wednesday, February 26, 14
  22. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 12 Wednesday, February 26, 14
  23. 13 Internal Coupling http://flic.kr/p/KikA9 Wednesday, February 26, 14

  24. 14 newrelic_rpm Wednesday, February 26, 14

  25. 14 app start newrelic_rpm Wednesday, February 26, 14

  26. 14 app start connect() New Relic Server newrelic_rpm Wednesday, February

    26, 14
  27. {server config} 14 app start connect() New Relic Server newrelic_rpm

    Wednesday, February 26, 14
  28. {server config} 14 app start connect() New Relic Server ?

    newrelic_rpm Wednesday, February 26, 14
  29. 15 def connect() config = config_from_server() finish_setup(config) end newrelic_rpm Wednesday,

    February 26, 14
  30. 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 Wednesday, February 26, 14
  31. 17 def test_finish_setup_naming_rules config = { 'rules' => [...] }

    @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Wednesday, February 26, 14
  32. 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 Wednesday, February 26, 14
  33. 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 Wednesday, February 26, 14
  34. 20 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 Wednesday, February 26, 14
  35. 21 class Beacon def initialize # Your code is bad

    and you should feel bad Net::HTTP.get("http://somewhere.com") end end newrelic_rpm Wednesday, February 26, 14
  36. 22 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 Wednesday, February 26, 14
  37. 23 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 Wednesday, February 26, 14
  38. 24 def finish_setup(config) events.notify(:configured) end newrelic_rpm Wednesday, February 26, 14

  39. 25 class Beacon def initialize events.subscribe(:configured) do Net::HTTP.get("http://...") end end

    end newrelic_rpm Wednesday, February 26, 14
  40. 26 def test_finish_setup_notifies called = false @events.subscribe(:configured) do called =

    true end @agent.finish_setup({}) assert called end newrelic_rpm Wednesday, February 26, 14
  41. 27 def test_add_naming_rules config = { 'rules' => [...] }

    @agent.add_naming_rules(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Wednesday, February 26, 14
  42. More Focused Tests 28 Wednesday, February 26, 14

  43. More Independent Classes 29 Wednesday, February 26, 14

  44. Implicit Ordering 30 Wednesday, February 26, 14

  45. Debugging 31 Wednesday, February 26, 14

  46. Performance 32 Wednesday, February 26, 14

  47. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 33 Wednesday, February 26, 14
  48. http://flic.kr/p/euQpT External Coupling Wednesday, February 26, 14

  49. 35 newrelic_rpm Wednesday, February 26, 14

  50. 35 Javascript insertion newrelic_rpm Wednesday, February 26, 14

  51. 35 Javascript insertion Error collection newrelic_rpm Wednesday, February 26, 14

  52. 35 Javascript insertion Cross application tracing Error collection newrelic_rpm Wednesday,

    February 26, 14
  53. 36 No Setup Please! newrelic_rpm Wednesday, February 26, 14

  54. 37 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 Wednesday, February 26, 14
  55. 38 class CrossAppTracing def initialize events.subscribe(:before_call) do |e| process_request(e) end

    ... end end newrelic_rpm Wednesday, February 26, 14
  56. Ever Heard of Composition? 39 Wednesday, February 26, 14

  57. Looser Coupling 40 Wednesday, February 26, 14

  58. Compatibilty Layer 41 Wednesday, February 26, 14

  59. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 42 Wednesday, February 26, 14
  60. http://flic.kr/p/dYor95 Adding Eventing Wednesday, February 26, 14

  61. 44 Wednesday, February 26, 14

  62. 44 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  63. 44 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  64. 45 gem install simple_events http://github.com/jasonrclark/simple_events Wednesday, February 26, 14

  65. 46 class SimpleEvents::Notifier def initialize @events = {} ... end

    end simple_events Wednesday, February 26, 14
  66. 47 class SimpleEvents::Notifier ... def subscribe(event, &handler) @events[event] ||= []

    @events[event] << handler check_for_runaway_subscriptions(event) end ... end simple_events Wednesday, February 26, 14
  67. 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 Wednesday, February 26, 14
  68. 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 Wednesday, February 26, 14
  69. 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 Wednesday, February 26, 14
  70. 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 Wednesday, February 26, 14
  71. 52 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 Wednesday, February 26, 14
  72. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 53 Wednesday, February 26, 14
  73. ActiveSupport:: Notifications http://flic.kr/p/aWNwU2 Wednesday, February 26, 14

  74. 55 AS::Notifications = ActiveSupport::Notifications activesupport Wednesday, February 26, 14

  75. Rails Request 56 •action_dispatch.request •start_processing.action_controller •process_action.action_controller •sql.active_record •render_template.action_view • ...

    and more! Wednesday, February 26, 14
  76. 57 events = [] AS::Notifications.subscribe("event") do |*args| events << *args

    end activesupport Wednesday, February 26, 14
  77. 58 AS::Notifications.subscribe("event") do |*args| events << *args end ["render_template.action_view", "e2a1e92a3c613576c2b0"

    {:identifier=>"/file/path/here"}] activesupport Wednesday, February 26, 14
  78. 59 e = AS::Notifications::Event.new(*args) e.name # => "render_template..." e.duration #

    => 10 (in milliseconds) e.payload # => { :identifier => ... } activesupport Wednesday, February 26, 14
  79. 60 activesupport AS::Notifications.instrument("evt", :data => 1) Wednesday, February 26, 14

  80. 61 AS::Notifications.instrument("evt", :data => 1) do #... your timed code

    here end activesupport Wednesday, February 26, 14
  81. • Regexp on Notifications.subscribe • Event#parent_of? • Temporary subscription •

    Unsubscribe • LogSubscriber 62 activesupport Wednesday, February 26, 14
  82. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 63 Wednesday, February 26, 14
  83. http://flic.kr/p/a1dEec Notifiers Wednesday, February 26, 14

  84. 65 Wednesday, February 26, 14

  85. 65 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  86. 65 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  87. Naming 66 Wednesday, February 26, 14

  88. Flexible Payload 67 Wednesday, February 26, 14

  89. Primitives at Boundaries 68 Wednesday, February 26, 14

  90. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 69 Wednesday, February 26, 14
  91. 70 http://flic.kr/p/8w9PyF Subscribers Wednesday, February 26, 14

  92. 71 Wednesday, February 26, 14

  93. 71 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  94. 71 Notifier Subscriber Subscriber Subscriber Eventing System Wednesday, February 26,

    14
  95. != self.only 72 Wednesday, February 26, 14

  96. 73 Wednesday, February 26, 14

  97. 74 class UserSignup < Mutations::Command def execute user = User.create!(inputs)

    NewsletterSubscriptions.create(user.id) UserMailer.async(:deliver_welcome, user.id) user end end Wednesday, February 26, 14
  98. 75 class SomeSubscriber def initialize events.subscribe(:configured) do log_config end end

    def log_config ... end end Wednesday, February 26, 14
  99. Memory Leaks 76 Wednesday, February 26, 14

  100. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 77 Wednesday, February 26, 14
  101. http://flic.kr/p/ndraB Events as Data Wednesday, February 26, 14

  102. 79 Opened Account $100 Check 0001 -$15 Deposit $20 Check

    0002 -$21 Total $84 Bank Transactions Wednesday, February 26, 14
  103. 80 Customer Pricing Wednesday, February 26, 14

  104. 80 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 Wednesday, February 26, 14
  105. 80 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 Wednesday, February 26, 14
  106. Event Sourcing 81 Wednesday, February 26, 14

  107. 82 http://flic.kr/p/4LTNc9 Wednesday, February 26, 14

  108. 83 Metrics 10:00AM CPU/Utilization 0.85 Wednesday, February 26, 14

  109. 84 Metrics 10:01AM CPU/Utilization 0.24 Wednesday, February 26, 14

  110. 85 Metrics Wednesday, February 26, 14

  111. 86 Metrics Wednesday, February 26, 14

  112. 87 Metrics Wednesday, February 26, 14

  113. 88 Metric Data Wednesday, February 26, 14

  114. 88 Metric Data Wednesday, February 26, 14

  115. 88 Metric Data Wednesday, February 26, 14

  116. Other Examples 89 • Database replication • Log files •

    PubSub • Immutable data structures Wednesday, February 26, 14
  117. Benefits 90 No Updates Automatic Audits Summarize/Transform/ Cache Wednesday, February

    26, 14
  118. Drawbacks So Much Data! Hard to Query Efficiently More Complex

    Code 91 Wednesday, February 26, 14
  119. Jason Clark @jasonrclark Ruby Agent Engineer • The Pattern •

    Internal Coupling • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data ? Wednesday, February 26, 14