Looking at your nodes
In common with the other AppDynamics application agents, the Node.js agent recognises the arrival of http traffic as starting a transaction and also auto-detects many common exit points (places where the code makes requests of downstream systems) out of the box, including outbound http calls, queries on various databases and accesses to the memcached and redis caches.
As caches go, however, redis is a slightly unusual beast in that in addition to storing name.value pairs of data in memory for fast access, it also provides message broker functionality, allowing application processes to interact via publish/subscribe messaging. I recently came across a customer who was using redis to pass requests (and return responses) between services implemented in Node. To illustrate this, I have a sample app showing this communication between 2 simple simple services.
When these 2 commands are run, the out of the box instrumentation yields the following flowmap:
Unfortunately, this does not look anything like the request/response interaction between 2 application tiers that we know is really going on under the hood. We only see the entry tier of the application, apparently accessing a Redis cache.
The service tier is missing entirely since only tiers executing transactions appear on the flowmap and we do not recognise the arrival of a message on the Redis bus as constituting a transaction entry point and the sending of a message over the Redis bus appears as a cache access since that is the only Redis use-case supported by the out of the box instrumentation.
Fortunately, the Node.js agent provides a java script API we can use to customise the agent’s behaviour.
Fear not, we can customise it!
In order to follow the transaction flow through this Node application, we are going to need to do a couple of things:
- Recognise calls to the redis message bus as exits
- tag-and-follow the pub/sub messages that flow through Redis, to facilitate end to end correlation of the transaction flow
- Recognise the arrival of messages arriving from the Redis bus as starting a transaction
Given that I wrote, and you are reading, this article it will not surprise you to know that the AppDynamics Node agent API provides for all three of these eventualities.
Recognising the calls to the Redis message bus
To do this, we will need to suppress the out of the box identification of the Redis exit, and then create our own custom exit point.
Suppressing the out of the box handling requires us to implement the beforeExitCall callback. If our callback does not return anything, the default handling of the exit is suppressed. We do this conditionally for Redis exits to avoid impacting other exits which we may want:
Note we also had to add a call to the startTransaction API in order that we could interact with the agent’s view of the http transaction, which the out of the box HTTP entry support started for us.
Now we have suppressed the out of the box exit handling for Redis, we need to add the code to establish our custom redis exit:
Where the original code simply includes a call to its publish function, the AppDynamics instrumented version surrounds this code with calls to startExitCall and endExitCall to delimit the call itself.
Tagging and following transactions
AppDynamics tags and follows transactions by adding a correlation string to the payload. Depending on the protocol, this can be added in the message header or the message payload itself.
Since Redis publish / subscribe simply delivers a payload with no bus-level headers we can use, we will transport the correlation string as a field in the JSON payload.
The correlation string, obtained via a call to createCorrelationInfo, is added to the JSON message payload in the ci field.
Note that since this example only contains one client tier and one service tier we can directly correlate the tiers in the call, which means the identifying properties we passed to the startExitCall call will be mapped by ApDynamics to a single destination tier.
If, as is entirely possible in a pub/sub messaging model, the message could arrive at multiple destinations the optional doNotResolve flag should be set on the createCorrelationInfo call, which tells AppDynamics to expect a 1:many interaction between the single client (producer) and multiple consumers and suppress this mapping.
Recognising Redis pub/sub message arrival
The final piece of the jigsaw is detecting the arrival of messages across Redis and causing this to start a continuing segment of the transaction (the sample does this twice, once for the arrival of the request at the service and again to handle correlation of the reply from the service before the http response is written)
Unsurprisingly, the startTransaction API is used to start the transaction. Since it is a continuing transaction segment, this needs to process the incoming correlation string, which is obtained through the parseCorrelationInfo API, which is simply passed the string from the incoming payload.
Of course, you also see the reappearance of the code to suppress the default Redis exit handling and the dispatch of the service reply message through the Redis bus wrapped as an exit.
Putting it all together
Putting all the above into practise in the application code yields the following flowmap, which looks like we would expect, given our understanding of the workload:
I hope I have shown you how flexible the Node agent is in terms of being able to accommodate scenarios that don’t work out of the box.
In conclusion… Rest assured; even if your application flowmap doesn’t appear out of the box “by magic”, never fear… AppDynamics has the flexibility and simplicity to get the job done easily!