Perf characteristics of Behaviors, Mutators and Handler Ordering

Sorry for the lousy title, but I was wondering what the perf characteristics are of Behaviors vs. Mutators vs. Handler Ordering.

I know that when using Behaviors and Mutators, they will run for every message in the endpoint which owns the Behaviors and/or Mutators. However, I’m unsure of how NSB actually implements handler ordering.

Does NSB still need to inspect every message to see if that message is one of the messages that are handled by the ordered handlers? Or does it always try to execute the ordered handlers for every message regardless if the ordered handlers are handling that specific message?

Hi Michael

In short there is no performance penalty.

When message handler order is registered the types get registered in the message handler registry. The registry then statically caches them and always returns them in the defined order.

Is that the information you were looking for?

Regards
daniel

@danielmarbach , that is exactly what I was looking for!

Thanks so much
Mike

@danielmarbach, sorry, actually one more question. It sounds like for any message (even if the message is not the type(s) being handled by the ordered handlers) the message handler registry will always return the ordered handlers in the defined order.

So the look-up against the statically cached handler registry still happens for every message, regardless if that message is handled by an ordered handler?

If so, how does this differ from how Behaviors/Mutators s work for each message? Is there more work done by NSB to build/execute the pipeline (for Behaviors) or Mutators (which I assume are “fixed” in the pipeline) than for ordered handlers in the handler registry?

Mike,

Once handlers are managed by the message handler registry there is no differentiation anymore between order handlers and regular handlers. The load handlers connector asks the message handler registry for all handlers that match the currently handled message type and loads those cached handlers. That means for every incoming physical message all handlers are resolved again

Behaviors are resolved once and brought into the right pipeline order and then a custom expression tree is baked into the runtime that executes all behaviors in the right order. So that means once a message is handled behaviors will never be resolved anymore.

Mutators on the contrary have two behaviors. For mutators that are registered over assembly scanning they get built every time a new message comes in (similar to handlers). For mutators registered over Registration API there is no resolve anymore. So they are from a performance perspective similar to behaviors

Does that help?

Now I’m curious why you are asking these questions…

Regards
Daniel

@danielmarbach, first and foremost, thanks for taking the time to spell it out to me. I appreciate that.

LOL! I pretty much assumed you’d ask at this point. I’m toying with the idea of putting ordered handlers on an endpoint that could experience higher traffic than other endpoints in our system (I realize that’s vague, but just bear with me). Before I go and code this solution and make assumptions I shouldn’t, I wanted to know from a runtime perspective, what the overhead of using these three different approaches are, so if I do choose to any of the approaches, I know up front the trade-offs I’m making in order to build the functionality I want to build.

My interpretation is that Behaviors (or API registered Mutators), from the incoming physical message perspective, have less of a load/resolution “cost” than ordered handlers? Sorry if I’m still not “getting it”, but I figured while you’re still replying to me, I’d try to get as much information as possible :wink:

FYI, the functionality I’m looking to provide is “run once software”, in the context of making hot-fixes to a production system, with those changes only living in production until the next deployment.

Thanks!

With behaviors the resolution cost is paid once and then the firehose is opened. The IoC is only involved once. So instances injected into behaviors effectively become singletons per endpoint regardless of how they are configured in the container.

Registered mutators is the same (but they are not resolved via IoC because you as the user provide them to us) although we always have to loop through the list of mutators.

With handlers the IoC is always involved and thus the throughput is affected by the performance characteristics of your IoC container of choice

Let me know if you have more questions

Daniel