< Return to Video

RailsCond 2014 - Make an Event of It by Jason Clark

  • 0:18 - 0:23
    JASON CLARK: All righty. I think, by my clock,
  • 0:23 - 0:25
    it's about 1:40. A couple more people
  • 0:25 - 0:29
    are coming on in, but we'll get things started.
  • 0:29 - 0:33
    My name's Jason Clark. I'm an engineer at
    New
  • 0:33 - 0:36
    Relic. I work specifically on the New Relic
    RPM
  • 0:36 - 0:38
    gem, which some of you may have installed
    in
  • 0:38 - 0:42
    your applications. So that's actually gonna
    play in a
  • 0:42 - 0:43
    little bit in some of the examples that I'm
  • 0:43 - 0:46
    gonna use later on. But you don't need to
  • 0:46 - 0:47
    know too much about how that works.
  • 0:47 - 0:50
    The first, so, I, I've got to start off
  • 0:50 - 0:54
    with a, an admission to something that I've
    always
  • 0:54 - 0:56
    wanted to do when presenting in front of a
  • 0:56 - 0:59
    group of people, and I figured that RailsConf
    is
  • 0:59 - 1:02
    probably just about the best opportunity.
    So, if you'll
  • 1:02 - 1:09
    just bear with me for a second. Just hang
    on.
  • 1:09 - 1:15
    So at New Relic, we have a support hero
  • 1:15 - 1:19
    who is responsible for handling the escalations.
    So any
  • 1:19 - 1:22
    time that something gets too complicated for
    support, they
  • 1:22 - 1:24
    send it to the support hero. And my wife
  • 1:24 - 1:28
    heard that, and she went, if you're a hero,
  • 1:28 - 1:31
    you need a cape. And so she made me
  • 1:31 - 1:36
    this lovely hand-embroidered Ruby emblem cape.
    And I've always
  • 1:36 - 1:40
    wanted to give a presentation wearing this.
    So, if
  • 1:40 - 1:45
    that's OK. Little unprofessional, maybe, but,
    you know. I'm
  • 1:45 - 1:48
    just gonna go for it, so.
  • 1:48 - 1:52
    AUDIENCE: Look in the sky! It's a bird!
  • 1:52 - 1:55
    J.C.: All right. That feels good. Thank you.
    Thank
  • 1:55 - 1:57
    you for humoring me. All right.
  • 1:57 - 1:58
    So what we're here for, here to talk about
  • 1:58 - 2:01
    today is about something that I call evented
    patterns
  • 2:01 - 2:04
    in Ruby. So let's start off with a little
  • 2:04 - 2:06
    bit of a story. This is a little bit
  • 2:06 - 2:09
    of a fabrication, but it might be something
    that
  • 2:09 - 2:11
    some of you have experienced before.
  • 2:11 - 2:14
    So, imagine that you have created an application.
    Maybe
  • 2:14 - 2:17
    it's something like, something basic, for
    doing tracking of
  • 2:17 - 2:22
    your cycling statistics. And all of a sudden,
    your
  • 2:22 - 2:25
    application really takes off. It gets on the
    social
  • 2:25 - 2:28
    media. People find out about it and, you know,
  • 2:28 - 2:30
    you, you start getting this influx of users
    that
  • 2:30 - 2:33
    you've got to deal with. As your traffic scales,
  • 2:33 - 2:35
    you've got to do things like tuning your web
  • 2:35 - 2:39
    server, right. You, you pick your, your Unicorn
    or
  • 2:39 - 2:40
    your Puma and you get that set up. You
  • 2:40 - 2:45
    tune your system. And while your traffic is
    growing,
  • 2:45 - 2:47
    there's a similar sort of growth that goes
    on
  • 2:47 - 2:50
    in your code.
  • 2:50 - 2:53
    Your controllers start out life kind of like
    this.
  • 2:53 - 2:56
    They start out pretty simple, you know. We
    just
  • 2:56 - 2:59
    create our user when somebody signs up. But
    then,
  • 2:59 - 3:01
    as your application grows and you get more
    stake
  • 3:01 - 3:04
    holders and your company's, you know, doubling
    every two
  • 3:04 - 3:06
    months, all of a sudden, you've got other
    things
  • 3:06 - 3:08
    that you need to add in there, right. You,
  • 3:08 - 3:10
    maybe you want to send welcome emails, so
    you,
  • 3:10 - 3:13
    you end up, you know, getting your action
    mailer
  • 3:13 - 3:16
    going there, and cram that into, into your
    action
  • 3:16 - 3:17
    when someone signs up.
  • 3:17 - 3:19
    Oh, and then maybe it's really important that
    our
  • 3:19 - 3:22
    business people be able to do analytics. They
    want
  • 3:22 - 3:24
    to know about who's signing up and track data,
  • 3:24 - 3:25
    and there's some other place that we want
    to
  • 3:25 - 3:29
    push that data. Oh, and then the sales people,
  • 3:29 - 3:31
    they really want this to be integrated to
    their
  • 3:31 - 3:33
    sales system, and so they want stuff to get
  • 3:33 - 3:37
    pushed over to their CRM. And maybe, oh, we
  • 3:37 - 3:38
    want to do some sort of posting out to
  • 3:38 - 3:41
    the social networks. So we have a background
    job
  • 3:41 - 3:43
    that takes care of that. You know, this sort
  • 3:43 - 3:46
    of growth happens in some of those critical
    points
  • 3:46 - 3:51
    in your infrastructure. The spots where interesting
    things happen
  • 3:51 - 3:53
    in the domain of your application.
  • 3:53 - 3:57
    Now, that's kind of, can get to be a
  • 3:57 - 4:00
    mess, you know. This, over time, I mean this
  • 4:00 - 4:02
    is a short example and slightly fabricated,
    but I'm
  • 4:02 - 4:04
    sure you all have, you know, I'm sure all
  • 4:04 - 4:06
    of your controllers are, like, twelve lines
    or less
  • 4:06 - 4:10
    for an action, right? Nobody has these long
    methods
  • 4:10 - 4:12
    that go on and do tons and tons of
  • 4:12 - 4:13
    things.
  • 4:13 - 4:16
    You can end up with a massive snarl. You
  • 4:16 - 4:18
    can end up with controllers that are very
    difficult
  • 4:18 - 4:20
    to test, difficult to reason about, and they
    go
  • 4:20 - 4:24
    on and on handling all of these different
    things.
  • 4:24 - 4:26
    So what we're gonna talk about is a pattern
  • 4:26 - 4:29
    of using events to handle that and to take
  • 4:29 - 4:33
    those key parts of your infrastructure and
    break them
  • 4:33 - 4:35
    apart into smaller bits.
  • 4:35 - 4:38
    So, first off, we're gonna talk about what
    I
  • 4:38 - 4:40
    actually mean when I say events, the pattern
    that
  • 4:40 - 4:43
    we're talking about. We'll talk about how
    that can
  • 4:43 - 4:46
    be used in the coupling of your application,
    both
  • 4:46 - 4:49
    internally within your app and as it relates
    to
  • 4:49 - 4:51
    other libraries that you might be using.
  • 4:51 - 4:53
    We'll take a look at a couple of mechanisms
  • 4:53 - 4:55
    that you can use to institute this sort of
  • 4:55 - 4:59
    system. And then we'll talk about some of
    the
  • 4:59 - 5:01
    responsibilities and sort of how you should
    think about
  • 5:01 - 5:05
    this type of evented system within your app.
    And
  • 5:05 - 5:07
    keep things flexible and performant.
  • 5:07 - 5:12
    So, first up, the pattern. So, when I say
  • 5:12 - 5:14
    events, you know, there's lots of different
    people that
  • 5:14 - 5:17
    think lots of different things when I say
    that.
  • 5:17 - 5:19
    Like, some folks that this was gonna be about
  • 5:19 - 5:23
    event machine. It's not about event machine.
    So what,
  • 5:23 - 5:24
    what sort of things pop to mind when I
  • 5:24 - 5:26
    say evented programming?
  • 5:26 - 5:27
    AUDIENCE: Asynchronous.
  • 5:27 - 5:28
    J.C.: K.
  • 5:28 - 5:30
    AUDIENCE: ActiveSupport notifications.
  • 5:30 - 5:33
    J.C.: Visual Basic event handlers? Anybody?
  • 5:33 - 5:35
    AUDIENCE: Nice!
  • 5:35 - 5:37
    J.C.: I'm sorry to, no. I shouldn't do that.
  • 5:37 - 5:37
    No.
  • 5:37 - 5:41
    A lot of people probably think node. Think
    asynchronous
  • 5:41 - 5:44
    sorts of callbacks. But that's not necessarily
    the core
  • 5:44 - 5:46
    of the type of event that I'm gonna describe
  • 5:46 - 5:50
    here today. This, these events are not necessarily
    asynchronous.
  • 5:50 - 5:54
    They're not necessarily about IO or about
    distributing things.
  • 5:54 - 5:56
    They're just a basic pattern to decoupled
    pieces of
  • 5:56 - 5:58
    your application.
  • 5:58 - 6:00
    So it starts off, the main term that I'll
  • 6:00 - 6:03
    use. Somebody knows that something interesting
    happens in your
  • 6:03 - 6:06
    system, I'm going to refer to it as a
  • 6:06 - 6:08
    notifier. There's a set of subscribers that
    also exist.
  • 6:08 - 6:10
    And those are the pieces of your application
    that
  • 6:10 - 6:14
    care about that event that just happened.
    And then
  • 6:14 - 6:16
    there's some sort of eventing in the middle.
    Something
  • 6:16 - 6:20
    that dispatches those events from the notifier
    to all
  • 6:20 - 6:22
    of the subscribers.
  • 6:22 - 6:25
    Now, this is really pretty basic, and you
    might
  • 6:25 - 6:30
    wonder, like, why aren't these just method
    calls? Well,
  • 6:30 - 6:32
    it's because of that eventing system that
    we can
  • 6:32 - 6:35
    get the decoupling. It allows for the notifier
    and
  • 6:35 - 6:38
    the subscriber to not necessarily know directly
    about each
  • 6:38 - 6:41
    other. Those classes don't have to interact,
    directly, without
  • 6:41 - 6:45
    that intermediary between them.
  • 6:45 - 6:47
    What's the relationship to the, to callbacks,
    you might
  • 6:47 - 6:51
    say as well. There's, you know, at ActiveRecord,
    we
  • 6:51 - 6:53
    have before and after hooks and around hooks
    to
  • 6:53 - 6:56
    do all sorts of different things. Well, this
    is
  • 6:56 - 6:58
    similar, but it's a little different from
    what we're
  • 6:58 - 7:01
    gonna describe, because of that line, where
    we have
  • 7:01 - 7:05
    to derive from ActiveRecord::Base. That's
    a really tight coupling.
  • 7:05 - 7:08
    And what I'm gonna describe doesn't put any
    requirements
  • 7:08 - 7:11
    on your class, except that it interacts with
    the
  • 7:11 - 7:15
    event dispatcher as a collaborator.
  • 7:15 - 7:17
    So that's the basics of what I mean by
  • 7:17 - 7:20
    evented patterns. So let's take a look, now,
    at
  • 7:20 - 7:24
    how it can influence coupling. So the first
    example
  • 7:24 - 7:26
    that I'm gonna draw is about internal coupling.
    So
  • 7:26 - 7:28
    when I say that, I mean, things that are
  • 7:28 - 7:30
    within the scope of your application, the
    code that
  • 7:30 - 7:33
    you own and that you've written.
  • 7:33 - 7:36
    So, working for New Relic, the gem that I
  • 7:36 - 7:40
    work on, you install it in your application
    and
  • 7:40 - 7:44
    it spins up, starts monitoring performance
    information, and then
  • 7:44 - 7:46
    sends it back every minute to New Relic so
  • 7:46 - 7:48
    we can give you pretty graphs and alerting
    and
  • 7:48 - 7:51
    all sorts of insight into what's going on
    in
  • 7:51 - 7:52
    your app.
  • 7:52 - 7:56
    When this gem was originally created, we would
    load
  • 7:56 - 7:57
    things up when the app starts and we read
  • 7:57 - 8:00
    a little config file to say what types of
  • 8:00 - 8:01
    things we want to be doing. You know, there's
  • 8:01 - 8:03
    features that you can turn on and turn off
  • 8:03 - 8:07
    and configure. And we knew the story about
    what
  • 8:07 - 8:10
    this configuration was when the application
    had successfully gotten
  • 8:10 - 8:11
    run.
  • 8:11 - 8:15
    Well, later on, we added a feature. And that
  • 8:15 - 8:20
    feature was server-side configuration. So
    the application now connects
  • 8:20 - 8:23
    to our server and gets back some additional
    set
  • 8:23 - 8:27
    of configuration values. And what this means
    is that
  • 8:27 - 8:29
    the point in time at which we could assume
  • 8:29 - 8:33
    that the application was actually configured
    changed. If we
  • 8:33 - 8:36
    had put a bunch of logic directly into our
  • 8:36 - 8:38
    app start up, like right there in the initialize
  • 8:38 - 8:41
    where things start running, we now didn't
    have the
  • 8:41 - 8:44
    right answer for what the configuration was.
    We have
  • 8:44 - 8:47
    to wait until that connect happens in the
    background
  • 8:47 - 8:49
    before we can do that.
  • 8:49 - 8:50
    So let's take a look at what happened with
  • 8:50 - 8:54
    that connect method. So we make a call out
  • 8:54 - 8:57
    to the server and, you know, this is obviously
  • 8:57 - 8:59
    a little stripped down. There's error handling
    in real
  • 8:59 - 9:01
    life. But we get back some sort of configuration
  • 9:01 - 9:04
    from the server. It tells us what those settings
  • 9:04 - 9:06
    are supposed to be. We pass that along to
  • 9:06 - 9:10
    finish set up and then finish set up starts
  • 9:10 - 9:13
    looking like a little bit of a mess. There's
  • 9:13 - 9:15
    a whole bunch of different concerns here,
    right. We've
  • 9:15 - 9:20
    got something about naming rules. Something
    about cross-application tracing.
  • 9:20 - 9:23
    Javascript. Some sort of beacon, like. It's
    not important
  • 9:23 - 9:25
    what all of the inner details of all of
  • 9:25 - 9:27
    these pieces are. But none of them are really
  • 9:27 - 9:29
    related. The only thing about them is that
    they
  • 9:29 - 9:33
    care that the configuration is ready. They're
    wanting to
  • 9:33 - 9:36
    respond to that event that happened, that
    the config
  • 9:36 - 9:37
    is set.
  • 9:37 - 9:42
    Now, one of the outgrowths of the code being
  • 9:42 - 9:43
    laid out like this is that we would like
  • 9:43 - 9:46
    to be able to write some simple tests. Some
  • 9:46 - 9:49
    unit tests, like DHH tells us we shouldn't,
    that
  • 9:49 - 9:51
    look kind of like this. We run our given
  • 9:51 - 9:52
    set up against a chosen config and then we
  • 9:52 - 9:54
    can see the results of what came out of
  • 9:54 - 9:55
    that.
  • 9:55 - 9:57
    But, in practice, what we end up having to
  • 9:57 - 10:00
    do is we end up having to stub out
  • 10:00 - 10:02
    things. Like those different collaborators
    that were in there.
  • 10:02 - 10:05
    The cross application tracing. If we don't
    care about
  • 10:05 - 10:06
    it in this test, but we still need to
  • 10:06 - 10:10
    make sure that that code path can run without
  • 10:10 - 10:13
    tripping anything up or crashing because things
    aren't filled
  • 10:13 - 10:16
    out the way that it needs it to be.
  • 10:16 - 10:18
    Things can get even worse than this. Now that
  • 10:18 - 10:22
    beacon line down on the bottom, you know,
    we're
  • 10:22 - 10:24
    constructing a brand new object. Who, who
    knows what
  • 10:24 - 10:27
    that thing might be doing internally. You
    know? I
  • 10:27 - 10:30
    mean, nobody's ever written code like this,
    right, that
  • 10:30 - 10:33
    does something bad like that from a constructor
    for
  • 10:33 - 10:36
    an object. That doesn't happen. But, you know,
    we
  • 10:36 - 10:38
    end up having to do things like stubbing out
  • 10:38 - 10:42
    new. Make it so that objects don't do interactions
  • 10:42 - 10:44
    and things that we don't really want.
  • 10:44 - 10:47
    Well, what if we could take this whole method
  • 10:47 - 10:50
    and just get rid of it in the form
  • 10:50 - 10:53
    that it's in and instead we notify that an
  • 10:53 - 10:57
    event has happened. We tell our event system
    that
  • 10:57 - 11:00
    the system is now fully configured. We are
    ready
  • 11:00 - 11:04
    to go. Everything's logged. We can head on.
  • 11:04 - 11:07
    Now this has some really nice side-effects.
    One of
  • 11:07 - 11:11
    them is that beacon code. Now, in initialize,
    you
  • 11:11 - 11:13
    still see the code where we're making this
    HTTP
  • 11:13 - 11:17
    callout. But it's not wrapped in this subscribe
    block.
  • 11:17 - 11:19
    So it means that that call is only going
  • 11:19 - 11:22
    to happen when the event system lets us know
  • 11:22 - 11:26
    that this configuration event has occurred.
    That provides a
  • 11:26 - 11:29
    buffer. You can safely create a beacon now
    and
  • 11:29 - 11:33
    it's not going to immediately go hit the network.
  • 11:33 - 11:35
    The tests surround the finished set up method
    become
  • 11:35 - 11:38
    very slim and very straightforward as well.
    All the
  • 11:38 - 11:42
    finish set up is responsible for is firing
    that
  • 11:42 - 11:44
    event. And we check that that event gets dispatched
  • 11:44 - 11:47
    correctly and that's all that we need to unit
  • 11:47 - 11:50
    test at this level. And unit testing for the
  • 11:50 - 11:54
    individual effects that occur, moves over
    and gets done
  • 11:54 - 11:56
    in the place that responds to that event.
    The
  • 11:56 - 11:59
    thing that is gonna subscribe to the event
    now
  • 11:59 - 12:01
    takes the responsibility for testing that.
  • 12:01 - 12:04
    Now, this doesn't cover everything. There
    is a need
  • 12:04 - 12:06
    for an integration test. You do want something
    that
  • 12:06 - 12:09
    sees that we wire up to the right events.
  • 12:09 - 12:11
    But you probably have tests that are gonna
    exercise
  • 12:11 - 12:14
    those things anyways, if this isn't a critical
    path
  • 12:14 - 12:17
    of your application.
  • 12:17 - 12:19
    So events allow us, within our own code, to
  • 12:19 - 12:24
    have more focused tests around those individual
    pieces, more
  • 12:24 - 12:28
    independence between the classes. The configuration
    in the finished
  • 12:28 - 12:31
    set up no longer has to know anything about
  • 12:31 - 12:35
    the Javascript instrumentor, the cross-application
    tracer, the beacon. All
  • 12:35 - 12:38
    of that is pried apart.
  • 12:38 - 12:40
    And we found that this also starts to kind
  • 12:40 - 12:43
    of create a language within your application.
    A domain
  • 12:43 - 12:46
    language, if you will. Because these are the
    important
  • 12:46 - 12:49
    events that happen for us. We care, as the
  • 12:49 - 12:51
    New Relic gem, that we are configured. That
    is
  • 12:51 - 12:53
    a critical thing for us. And there's now a
  • 12:53 - 12:55
    name for that. It's not just a bunch of
  • 12:55 - 12:57
    stuff that's tacked on to the end of one
  • 12:57 - 12:59
    of our methods.
  • 12:59 - 13:03
    It's not all roses, though. There is an issue
  • 13:03 - 13:06
    if you have some sort of ordering that's required
  • 13:06 - 13:08
    in the things that respond to those events.
    And
  • 13:08 - 13:10
    actually we've had a couple of situations
    where we've
  • 13:10 - 13:13
    been refactoring code and trying to pull it
    into
  • 13:13 - 13:16
    event handlers and discovered that there were
    dependencies between
  • 13:16 - 13:20
    different subscribers that aren't really handled
    by a really
  • 13:20 - 13:23
    simple event system like this.
  • 13:23 - 13:25
    So, that's something that you have to watch
    out
  • 13:25 - 13:26
    for. If there's a lot of that sort of
  • 13:26 - 13:29
    ordering, you're gonna have to deal with that
    somewhere
  • 13:29 - 13:31
    in your code.
  • 13:31 - 13:34
    It can also make debugging and reasoning about
    things
  • 13:34 - 13:37
    a little harder. It does spread some of those
  • 13:37 - 13:40
    responsibilities out. Now you don't see one
    method that
  • 13:40 - 13:42
    lists all of the things that we do on
  • 13:42 - 13:44
    connect. You have to go search for things
    that
  • 13:44 - 13:47
    subscribe to those events. But in the right
    cases
  • 13:47 - 13:49
    I feel like it can buy you things with
  • 13:49 - 13:52
    the other benefits it brings along that outweigh
    that.
  • 13:52 - 13:56
    Lastly, it is a performance consideration.
    You know, I
  • 13:56 - 13:58
    would not use this sort of structure in a
  • 13:58 - 14:01
    tight loop that's gonna get called a million
    times
  • 14:01 - 14:03
    a second. You know. If it's something like
    we're
  • 14:03 - 14:08
    responding to a configuration event, like,
    that we are
  • 14:08 - 14:11
    now ready to go or somebody just signed up.
  • 14:11 - 14:12
    I mean, it'd be great if you had a
  • 14:12 - 14:15
    million people signing up a minute, but reality
    is
  • 14:15 - 14:19
    it's not happening as frequently as that.
    So, being
  • 14:19 - 14:21
    aware of how this will impact your performance
    is
  • 14:21 - 14:25
    really critical.
  • 14:25 - 14:27
    Internal coupling, like within the code that
    you write
  • 14:27 - 14:30
    yourself, though, is not the only place where
    you
  • 14:30 - 14:33
    can get bound to different things. So in,
    in
  • 14:33 - 14:35
    the New Relic gem, we have a lot of
  • 14:35 - 14:39
    different functionality that we instrument
    that work through middleware.
  • 14:39 - 14:42
    So we do Javascript insertion for doing end
    user
  • 14:42 - 14:45
    timings. So as your request is going back
    out,
  • 14:45 - 14:49
    we'll modify your html and put things in there.
  • 14:49 - 14:51
    We have error collection. So if there are
    unhandled
  • 14:51 - 14:55
    exceptions that occur during your call, at
    the rack
  • 14:55 - 14:57
    level, we will actually gather those up and
    see
  • 14:57 - 14:59
    that those happened and report them back.
  • 14:59 - 15:02
    And we do things with HTTP requests between
    the
  • 15:02 - 15:05
    applications if both ends of them are running
    New
  • 15:05 - 15:08
    Relic. And we need to modify headers, HTTP
    headers
  • 15:08 - 15:10
    that are on those requests. And all of these
  • 15:10 - 15:14
    separate things fit for us very well within
    middlewares.
  • 15:14 - 15:17
    But, that makes kind of an assumption that
    everything
  • 15:17 - 15:20
    that we want to apply that functionality to
    is
  • 15:20 - 15:22
    built on top of rack. And while that's a
  • 15:22 - 15:24
    pretty good assumption for a lot of things
    in
  • 15:24 - 15:26
    the Ruby space, we do support things that
    aren't
  • 15:26 - 15:29
    necessarily configured that way.
  • 15:29 - 15:31
    We also want to avoid people having to set
  • 15:31 - 15:34
    things up. There are certain cases where maybe
    you
  • 15:34 - 15:37
    have to manually add the things that we've
    created,
  • 15:37 - 15:39
    and if we have seven different middlewares
    that you
  • 15:39 - 15:42
    need to add for all this different functionality,
    that's
  • 15:42 - 15:46
    kind of problematic from a configuration view
    point.
  • 15:46 - 15:49
    So our solution to this was that we standardized
  • 15:49 - 15:54
    on having one middleware that we commonly
    will install.
  • 15:54 - 15:56
    And we use events to actually do the dispatch
  • 15:56 - 15:58
    from there. So you'll see this is just a
  • 15:58 - 16:01
    very basic middleware. When a request comes
    in and
  • 16:01 - 16:04
    hits call, we notify on the before and pass
  • 16:04 - 16:07
    along the environment that we got. We call
    through
  • 16:07 - 16:09
    to the app inside and notify after and then
  • 16:09 - 16:11
    return the results.
  • 16:11 - 16:14
    So this allows us to be able to plug
  • 16:14 - 16:16
    things in that maybe are not shaped like rack.
  • 16:16 - 16:19
    We could use some of that code, like in
  • 16:19 - 16:22
    the cross application tracing. If we had some
    web
  • 16:22 - 16:24
    server that was not rack-based or some configuration
    that
  • 16:24 - 16:27
    was not gonna run through middleware, we could
    still
  • 16:27 - 16:29
    have it generate a before call from the right
  • 16:29 - 16:33
    point and handle that with this same code,
    without
  • 16:33 - 16:37
    having to deal with trying to create middlewares
    or
  • 16:37 - 16:40
    insert something at a point that doesn't really
    fit.
  • 16:40 - 16:42
    Now, just for the middleware case, you know,
    this
  • 16:42 - 16:44
    probably could have been solved with some
    sort of
  • 16:44 - 16:47
    composition. But, for us, it was actually
    a really
  • 16:47 - 16:51
    powerful technique for keeping our code decoupled
    from the
  • 16:51 - 16:53
    library that we were building on top of. That
  • 16:53 - 16:56
    looser coupling depends a lot on what sort
    of
  • 16:56 - 16:59
    library and what your interactions with it
    are, you
  • 16:59 - 17:03
    know. You're maintaining, basically, kind
    of a compatibility layer.
  • 17:03 - 17:05
    And the more code that you have to put
  • 17:05 - 17:08
    into that layer, the more you should probably
    think
  • 17:08 - 17:10
    about whether you're approaching it correctly.
    Obviously you can't
  • 17:10 - 17:13
    do something like this with ActiveRecord,
    it's just not
  • 17:13 - 17:16
    gonna be plausible. But if you have a small
  • 17:16 - 17:19
    number of points where you interact with another
    third-party
  • 17:19 - 17:21
    library, applying events at those boundaries
    can be a
  • 17:21 - 17:24
    way that you can keep some separation between
    your
  • 17:24 - 17:28
    code and the library that you're dealing with.
  • 17:28 - 17:31
    So that's coupling. That's sort of the primary
    motivator
  • 17:31 - 17:34
    behind using an evented pattern is to decouple
    the
  • 17:34 - 17:37
    pieces of your application and allow for those
    to
  • 17:37 - 17:40
    be looser and not as away of what's on
  • 17:40 - 17:43
    the other end of either firing or responding
    to
  • 17:43 - 17:45
    an event.
  • 17:45 - 17:48
    So, let's talk about what it actually takes
    to
  • 17:48 - 17:52
    implement this. What does this look like in
    practice?
  • 17:52 - 17:54
    So I have created a small gem. This is
  • 17:54 - 17:56
    sort of an abstraction from what we do in
  • 17:56 - 17:59
    the New Relic gem. We don't use it directly
  • 17:59 - 18:02
    and I am not suggesting this for production
    use.
  • 18:02 - 18:04
    But it's a really good example of how simple
  • 18:04 - 18:08
    it is to implement something like this in
    Ruby.
  • 18:08 - 18:12
    So there's one primary class. The SimpleEvents::Notifier.
    When we
  • 18:12 - 18:15
    initialize that, it's gonna be the central
    thing that
  • 18:15 - 18:17
    keeps track of all the events. And everybody's
    gonna
  • 18:17 - 18:21
    talk to this notifier object. So we hold onto
  • 18:21 - 18:23
    a hash, which is where we're gonna store those
  • 18:23 - 18:28
    handlers from the people that subscribe to
    us.
  • 18:28 - 18:32
    Subscribing gets done. Passing in just a key
    for
  • 18:32 - 18:34
    what the event is. We typically use symbols
    within
  • 18:34 - 18:37
    our application. Some similar things that
    we'll see later
  • 18:37 - 18:40
    use strings. And then hands in a block, which
  • 18:40 - 18:42
    is the code for it to run at that
  • 18:42 - 18:45
    point that that event gets fired.
  • 18:45 - 18:46
    And then it's just a simple matter of keeping
  • 18:46 - 18:48
    track of that list of handlers. Each event
    may
  • 18:48 - 18:50
    have multiple handlers, so we make sure the
    array
  • 18:50 - 18:54
    is there and add that handler onto the list.
  • 18:54 - 18:56
    Now there's one other line here that we're
    dealing
  • 18:56 - 18:59
    with and that's checking for runaway subscriptions.
  • 18:59 - 19:00
    That has to do with the fact that for
  • 19:00 - 19:04
    our usage, there is a limited number of things
  • 19:04 - 19:07
    that should be subscribing to events. This
    is not
  • 19:07 - 19:10
    something where every single web request that
    comes through
  • 19:10 - 19:12
    subscribes and hooks itself in. And we'll
    talk a
  • 19:12 - 19:14
    little bit later on about some of the negative
  • 19:14 - 19:17
    side effects you can have if it's happening.
  • 19:17 - 19:19
    So, for us, if more than a hundred event
  • 19:19 - 19:22
    subscribers have subscribed to a given event,
    we actually
  • 19:22 - 19:24
    have a bug. And so we check for that
  • 19:24 - 19:28
    and make sure to warn that that's going on.
  • 19:28 - 19:30
    Your mileage may vary depending on the structure
    that
  • 19:30 - 19:33
    you choose to use.
  • 19:33 - 19:35
    Notifying is a little more complicated than
    subscribing, but
  • 19:35 - 19:37
    not too much more. And we'll step through
    it
  • 19:37 - 19:40
    here. So first off, we check whether the events
  • 19:40 - 19:45
    collection has any handlers for the particular
    key that
  • 19:45 - 19:46
    we just passed in. And this is one of
  • 19:46 - 19:50
    the really nice aspects of taking this simple
    approach.
  • 19:50 - 19:53
    There's no central registry. There's nothing
    to set up.
  • 19:53 - 19:56
    And event, I can create a handler for an
  • 19:56 - 19:58
    event that doesn't exist or I can start firing
  • 19:58 - 20:00
    and event that nobody handles, and it all
    just
  • 20:00 - 20:03
    gracefully falls out. It doesn't cause any
    problems if
  • 20:03 - 20:07
    there aren't handlers wired up for things.
  • 20:07 - 20:10
    If we do find that there are event handlers,
  • 20:10 - 20:13
    we simply iterate across those and call passing
    along
  • 20:13 - 20:17
    the arguments that we received on the event.
    For
  • 20:17 - 20:21
    our particular use in New Relic, we trap errors
  • 20:21 - 20:24
    and will log and swallow any exceptions that
    happen
  • 20:24 - 20:28
    as a result of our direct event handlers.
    Now,
  • 20:28 - 20:30
    this is kind of an artifact of the sort
  • 20:30 - 20:31
    of gem that we are. We're there in the
  • 20:31 - 20:34
    background and these events are things for
    the agent
  • 20:34 - 20:36
    and for us sending our data back.
  • 20:36 - 20:38
    And the worst possible thing that we could
    do
  • 20:38 - 20:40
    is crash an application. So we trap everything
    that
  • 20:40 - 20:42
    goes through. If you were doing this in an
  • 20:42 - 20:45
    application setting, you may want to log.
    You may
  • 20:45 - 20:47
    also want to raise those errors and fail quickly
  • 20:47 - 20:49
    in the case that one of your notifiers has
  • 20:49 - 20:52
    something go wrong. But your mileage, you
    know, you
  • 20:52 - 20:54
    can choose what the right policy is for how
  • 20:54 - 20:57
    you're using events and what setting you're
    putting those
  • 20:57 - 20:58
    in.
  • 20:58 - 21:02
    So that's it. Like, that's an entire eventing
    system
  • 21:02 - 21:04
    is about fifty lines of Ruby, which allows
    you
  • 21:04 - 21:08
    to decouple those different pieces of your
    application. But
  • 21:08 - 21:09
    the fact of the matter is that you might
  • 21:09 - 21:12
    not have to even write that system yourself
    if
  • 21:12 - 21:17
    you're using Rails. ActiveSupport::Notifications
    have been baked in from
  • 21:17 - 21:19
    I think about the 3.0 version of Rails. And
  • 21:19 - 21:21
    it's a system that's very much like what we've
  • 21:21 - 21:23
    described here.
  • 21:23 - 21:27
    I'll abbreviate in the slides a little because
    ActiveSupport::Notifications
  • 21:27 - 21:30
    gets really, really long in the code, so,
    we'll
  • 21:30 - 21:32
    scrunch that down a little bit. But all sorts
  • 21:32 - 21:35
    of things flow through this evented system
    any time
  • 21:35 - 21:38
    a Rails request is occurring. So we get things
  • 21:38 - 21:40
    about the dispatch at the top level. We get
  • 21:40 - 21:43
    things about the controller and what it's
    doing. We
  • 21:43 - 21:47
    get notifications about SQL calls. We get
    notifications about
  • 21:47 - 21:51
    view and template rendering. Like, tons and
    tons of
  • 21:51 - 21:55
    events are streaming through this ActiveSupport::Notification
    system all of
  • 21:55 - 21:56
    the time.
  • 21:56 - 21:58
    Some common things that you might be familiar
    with,
  • 21:58 - 22:00
    like the log, the debug log that you get
  • 22:00 - 22:04
    of your queries, that's running through ActiveSupport::Notifications
    that are
  • 22:04 - 22:09
    fired by ActiveRecord. So what's this look
    like?
  • 22:09 - 22:12
    It looks very similar to what we've seen before.
  • 22:12 - 22:14
    So subscribing to an event, you give it the
  • 22:14 - 22:16
    name, you give it a block, and then you
  • 22:16 - 22:18
    can see the form of what one of these
  • 22:18 - 22:21
    events was that came through. It'll hand it
    to
  • 22:21 - 22:24
    us. It's got a name. It's got an identifier.
  • 22:24 - 22:25
    And then it's got this hash that's sort of
  • 22:25 - 22:30
    a payload of data that comes along with it.
  • 22:30 - 22:34
    ActiveSupport also provides a simple wrapper
    for handling that
  • 22:34 - 22:37
    payload and handling those events when they're
    in a
  • 22:37 - 22:39
    very common form. So most of the things that
  • 22:39 - 22:42
    Rails sends out, they're gonna have a name.
    They're
  • 22:42 - 22:44
    gonna have a duration and a payload in a
  • 22:44 - 22:47
    standard format. So this class kind of wraps
    up
  • 22:47 - 22:51
    that access and makes it a little neater.
  • 22:51 - 22:54
    On the notifying side, the method that you
    call
  • 22:54 - 22:57
    is instrument. So if you want to fire an
  • 22:57 - 22:59
    event and have that be sent out to the
  • 22:59 - 23:02
    system, you just give it the string for the
  • 23:02 - 23:04
    name, give it whatever payload for the data,
    and
  • 23:04 - 23:07
    you're off and running.
  • 23:07 - 23:08
    There's also a nice form of it that it
  • 23:08 - 23:12
    has that will wrap your code in a block,
  • 23:12 - 23:14
    so you can do something in there, and what
  • 23:14 - 23:16
    that'll do is that'll tack a duration on.
    So
  • 23:16 - 23:18
    it'll time what's going on. And this is one
  • 23:18 - 23:21
    of the key things that ActiveSupport::Notifications
    are used for
  • 23:21 - 23:24
    within Rails is timing how long those different
    things
  • 23:24 - 23:26
    take. Those timings that you see in your debug
  • 23:26 - 23:29
    output are all coming from things being wrapped
    by
  • 23:29 - 23:33
    this sort of instrument call.
  • 23:33 - 23:34
    As with all things Rails, there's a lot more
  • 23:34 - 23:37
    richness as well under the surface. You can
    do
  • 23:37 - 23:42
    subscriptions by regular expressions. If events
    are nested, the
  • 23:42 - 23:46
    ActiveSupport::Notifications system keeps
    track of that and will take
  • 23:46 - 23:48
    care of letting you know so you could look
  • 23:48 - 23:50
    and see whether an event was occurring within
    another
  • 23:50 - 23:54
    event, and you can go crazy if that's happening
  • 23:54 - 23:55
    too much, but.
  • 23:55 - 23:59
    There are temporary subscriptions and unsubscribe,
    which is useful
  • 23:59 - 24:01
    in certain scenarios where, maybe you do want
    to
  • 24:01 - 24:05
    have something hook into the ActiveSupport::Notifications
    for the course
  • 24:05 - 24:07
    of a request. But you don't want to have
  • 24:07 - 24:10
    that handler hanging around and responding
    for the rest
  • 24:10 - 24:12
    of the lifetime of your application.
  • 24:12 - 24:14
    And there's some really great examples that
    you can
  • 24:14 - 24:17
    find in Rails itself. So the log subscriber
    class,
  • 24:17 - 24:19
    which is the thing that spits out a lot
  • 24:19 - 24:21
    of your development log, is a great example
    of
  • 24:21 - 24:26
    how you can hook into that underlying infrastructure.
  • 24:26 - 24:28
    So that's the mechanism. That's how you can
    build
  • 24:28 - 24:31
    events in or use a library that you're probably
  • 24:31 - 24:33
    already using to get a lot of these things
  • 24:33 - 24:35
    done.
  • 24:35 - 24:38
    Let's talk a little bit more in detail about
  • 24:38 - 24:40
    the different parties that are involved here,
    and what
  • 24:40 - 24:46
    they're responsible for and considerations
    there. So notifiers have
  • 24:46 - 24:49
    a lot of responsibility. They're, they're
    kind of the
  • 24:49 - 24:53
    key points in your application. You should
    be, in
  • 24:53 - 24:55
    this scheme, you would be adding events for
    the
  • 24:55 - 24:57
    things that matter the most.
  • 24:57 - 25:00
    And on that, I think that naming is a
  • 25:00 - 25:02
    really critical thing around this to get the
    benefit
  • 25:02 - 25:05
    that you're looking for. So in the example
    that
  • 25:05 - 25:08
    I gave up front, you'll, you probably didn't
    notice,
  • 25:08 - 25:12
    but I named the event configured, to say,
    hey,
  • 25:12 - 25:13
    we are in a state where we are fully
  • 25:13 - 25:14
    configured.
  • 25:14 - 25:17
    I didn't name it connect to server or finish
  • 25:17 - 25:20
    with the set up or, you know, binding it
  • 25:20 - 25:22
    to the mechanism of what it was. I couched
  • 25:22 - 25:25
    it in the terms of what mattered to my
  • 25:25 - 25:27
    application. I think that that's something
    that's really powerful
  • 25:27 - 25:30
    if you do this right. If you, it allows
  • 25:30 - 25:33
    for changes in the location, like in our case,
  • 25:33 - 25:35
    that change of when we were configured. It
    moved
  • 25:35 - 25:38
    from just plain application start up to a
    point
  • 25:38 - 25:40
    in time in the future when we may hear
  • 25:40 - 25:42
    back from a server. And who knows when that
  • 25:42 - 25:45
    configured point might change in the future?
  • 25:45 - 25:48
    We're not bound by the event name to some
  • 25:48 - 25:52
    particular method or point in time. We can
    change
  • 25:52 - 25:53
    that and still have it make sense within the
  • 25:53 - 25:57
    domain of what we've created.
  • 25:57 - 25:59
    It's also important to consider what your
    payload is.
  • 25:59 - 26:02
    What data you're sending along from the notifier.
    So
  • 26:02 - 26:06
    Rails follows the convention with ActiveSupport::Notifications
    that I would
  • 26:06 - 26:09
    definitely encourage, of sending along primarily
    a hash of
  • 26:09 - 26:12
    different values that you can pipe through.
    I learned
  • 26:12 - 26:14
    this the hard way the second time that I
  • 26:14 - 26:16
    needed to add a bit of data onto one
  • 26:16 - 26:20
    of my events. And the mechanism, as we saw,
  • 26:20 - 26:22
    just sort of percolates the arguments along
    that we've
  • 26:22 - 26:22
    given it.
  • 26:22 - 26:25
    Well, if you add another argument, all those
    other
  • 26:25 - 26:28
    handlers downstream need to get updated. So
    do it
  • 26:28 - 26:31
    from the start to make these things flexible.
    Pass
  • 26:31 - 26:33
    your payloads along as a hash that you can
  • 26:33 - 26:37
    then add additional data that old event handlers
    don't
  • 26:37 - 26:39
    need to care about and that new things can
  • 26:39 - 26:42
    pick up along the way.
  • 26:42 - 26:45
    On that topic of the payload as well, if
  • 26:45 - 26:47
    you're writing some sort of internal application,
    you know,
  • 26:47 - 26:50
    you can put whatever you want into these payloads.
  • 26:50 - 26:52
    But as a third party, you may want to
  • 26:52 - 26:55
    be cautious if you're having other people,
    other gems,
  • 26:55 - 26:57
    and other codes subscribe to these events
    to try
  • 26:57 - 27:02
    to keep your payloads as primitive as you
    can.
  • 27:02 - 27:03
    Maybe even all the way down to just base
  • 27:03 - 27:04
    types.
  • 27:04 - 27:06
    The advantage here is that you are, if you
  • 27:06 - 27:09
    put one of your internal classes as something
    that
  • 27:09 - 27:11
    you're packing around in this event, and then
    broadcast
  • 27:11 - 27:14
    it out to the world for everybody to use,
  • 27:14 - 27:16
    you now don't know who might be getting access
  • 27:16 - 27:20
    to those objects and using them. And depending
    on
  • 27:20 - 27:22
    what sort of usage you've got with your, your
  • 27:22 - 27:26
    app, it can be difficult to make changes to
  • 27:26 - 27:29
    things that maybe you consider to be internal
    details.
  • 27:29 - 27:32
    If you turn things into primitives, you know,
    it,
  • 27:32 - 27:34
    they're just strings and hashes and numbers,
    those are
  • 27:34 - 27:37
    pretty safe, right. You can maintain that
    mapping from
  • 27:37 - 27:40
    your internal objects to those primitives
    and not have
  • 27:40 - 27:43
    to worry about who might be changed when you
  • 27:43 - 27:47
    refactor.
  • 27:47 - 27:49
    So the other end of things is the subscribers.
  • 27:49 - 27:53
    I think it's really important to consider,
    carefully, who
  • 27:53 - 27:56
    the subscribers are for a given event. This
    pattern
  • 27:56 - 27:59
    doesn't fit everywhere. This is not something
    where every
  • 27:59 - 28:01
    single thing that happens in your application
    should be
  • 28:01 - 28:04
    an event. And if you're writing an event,
    and
  • 28:04 - 28:07
    there's only one thing that's gonna handle
    it, you
  • 28:07 - 28:09
    might be misapplying this pattern. You might
    not get
  • 28:09 - 28:12
    the benefit that you need, there.
  • 28:12 - 28:14
    A lot of times, you'll find that there is
  • 28:14 - 28:18
    some natural home, some place within your
    app that
  • 28:18 - 28:21
    is already there that is the right place to
  • 28:21 - 28:23
    be subscribing to that event and responding
    to what's
  • 28:23 - 28:26
    happening. But if you don't, it's an, there's
    some
  • 28:26 - 28:28
    interesting ideas around how you can create
    what that
  • 28:28 - 28:31
    location is. So there's this gem that I've
    run
  • 28:31 - 28:34
    into, it's called mutations. It's sort of
    building command
  • 28:34 - 28:37
    objects that represent different actions that
    happen in your
  • 28:37 - 28:38
    system.
  • 28:38 - 28:41
    Now, you know, you might recognize something
    very like
  • 28:41 - 28:43
    this from earlier in the morning, but it can
  • 28:43 - 28:48
    be an interesting idea if you have key pieces
  • 28:48 - 28:51
    of your system where a workflow or something
    that
  • 28:51 - 28:55
    occurs is really important. You can write
    something, a
  • 28:55 - 28:58
    command, that encapsulates what the event
    is, what that
  • 28:58 - 29:01
    action is that needs to be taken. And this
  • 29:01 - 29:03
    can be something that helps in those cases
    that
  • 29:03 - 29:06
    I described, where there may be implicit ordering.
    There
  • 29:06 - 29:09
    may be ordering between different components.
    You could create
  • 29:09 - 29:11
    a command that wraps those up in the right
  • 29:11 - 29:14
    ordering to be applied and then have that
    be
  • 29:14 - 29:18
    what responds to the events.
  • 29:18 - 29:20
    Probably one of the biggest difficulties that
    you can
  • 29:20 - 29:23
    run into with an evented pattern is, if there's
  • 29:23 - 29:26
    too much nesting, if this gets overused, and
    events
  • 29:26 - 29:29
    trigger events and trigger events on all sorts
    of
  • 29:29 - 29:32
    different things, it can become very difficult.
    And the
  • 29:32 - 29:35
    main advice that I have there is that this,
  • 29:35 - 29:37
    this technique works best, I think, at the
    critical
  • 29:37 - 29:40
    lynch points of your application. The things
    that you
  • 29:40 - 29:43
    really care about the most to be providing,
    that
  • 29:43 - 29:47
    sort of visibility and language around.
  • 29:47 - 29:49
    And another thing to be aware of with the
  • 29:49 - 29:51
    implementation that we've done here. I've
    had a number
  • 29:51 - 29:53
    of poeple come up afterwards and point out,
    they're
  • 29:53 - 29:56
    like, it's all still synchronous. It's all
    still just
  • 29:56 - 29:59
    running. I mean, this is just splitting up
    where
  • 29:59 - 30:02
    those things are. And that's totally true.
    This is
  • 30:02 - 30:05
    not as described something that will make
    your application
  • 30:05 - 30:09
    parallel or asynchronous in how that event
    handling happens.
  • 30:09 - 30:11
    But it might give you good points in time
  • 30:11 - 30:13
    when you could decide to push things off.
    Things
  • 30:13 - 30:15
    could go to background jobs or things could
    get
  • 30:15 - 30:17
    dispatched to all sorts of other places if
    you
  • 30:17 - 30:19
    wanted.
  • 30:19 - 30:21
    It's also important with subscribers to watch
    out for
  • 30:21 - 30:26
    leaks. There is, when you create a block,
    so
  • 30:26 - 30:28
    like, if this subscriber was created for every
    web
  • 30:28 - 30:32
    request, the fact that we have that block
    potentially
  • 30:32 - 30:35
    holds onto a reference to this object and
    keeps
  • 30:35 - 30:37
    it alive past a point in time when you
  • 30:37 - 30:38
    might have thought that it was gonna go away.
  • 30:38 - 30:41
    I've seen this not just in Ruby but in
  • 30:41 - 30:43
    a number of different languages with these
    sorts of
  • 30:43 - 30:47
    evented patterns with handlers that get subscribed,
    where they
  • 30:47 - 30:49
    can hold references to objects that you might
    not
  • 30:49 - 30:51
    be expecting them too. And so you gotta watch
  • 30:51 - 30:56
    out for that to keep things from leaking.
  • 30:56 - 30:58
    So those are some ideas around the responsibilities
    that
  • 30:58 - 31:02
    your subscribers and your notifiers should
    have. And I
  • 31:02 - 31:03
    hope that maybe this has opened your eyes
    a
  • 31:03 - 31:06
    little bit to some ideas about how you might
  • 31:06 - 31:08
    be able to partition your app differently.
    What are
  • 31:08 - 31:12
    those responsibilities and things that happen
    during your app
  • 31:12 - 31:14
    that would be worth having an event or worth
  • 31:14 - 31:16
    giving that name to.
  • 31:16 - 31:17
    And as you can see that it's a pretty
  • 31:17 - 31:20
    simple thing to implement that can give you
    a
  • 31:20 - 31:23
    lot of benefit down the road. Thank you.
Title:
RailsCond 2014 - Make an Event of It by Jason Clark
Description:

more » « less
Duration:
31:49

English subtitles

Revisions