Monday, August 7, 2017

MuleSoft Caching Strategy for Performance Improvement

1.0 Overview

This article will talk about how to implement caching in mulesoft. If we expose API to external users especially API that allows users to get information, then I would highly recommend you implement caching, this is to prevent request from hammering the backend systems.

Figure 1.0

A cache is like an intermediary endpoint that catches your application response based on a particular request, when a request is cached then further request for the same data would be retrieved from a cache instead of the actual end point (as illustrated in Figure 1.0).

MuleSoft cache response by using the request as a key (as depicted in Figure 1.0), User 1 triggered a new request (“Request ABC”) for the first time, when the request go through the cache it would result in a “cache miss”, when this happens this the request would be forwarded to the later application logic to retrieve the necessary response data to full fill the request ( in step 2, 3, 4,).

In Step 5, is where this new request would be cached, and the cache is actually a map with the request (“Request ABC”) as the key and the response (“Response ABC”) as the value. When User 2 submits the same request via step 7, it would then result in a “Cache Hit” when this happens cache returns the cache response back to “User 2”, hence eliminating the network and processing latency (from having to go through step 1 to 6).

2.0 Types of Cache Store Provided By MuleSoft

MuleSoft provides 3 ways to cache your application response
  • In memory caching - easiest form of caching, zero to minimal setup required, not recommended for production. The cache
  • Managed Store caching - this is an out of the box mulesoft persisted caching, which means after you restart the run time on which your application is running the cache is still persisted.
  • Object Store Caching - You have to write your own object store or use mule’s default object store.
  • File Store Caching - As the name suggest, it stores your cached data in an object store.

3.0 Mule Demo Application

Figure 3.0 depicts the mule application that I have built to demonstrate the caching functionality.
Figure 3.0

The groovy script just generates a random number. The logger in the front and back of the flow will show evidence of data being retrieve from cache when it is not writing into the console. The application also shows a graphical Cache Scope, which envelopes all of the message processor in the flow, which implies caching of the the end payload result that would be retuned from any of it’s message processors.

You may download the full source code from the following link.

In order to configure an application for caching you need to set up 2 things in your mule application and they are the
  • Global object-store-caching-strategy & the
   <ee:object-store-caching-strategy name="Caching_Strategy" doc:name="Caching Strategy">
       <managed-store storeName="demoCache" persistent="true" maxEntries="10000" entryTTL="60000000" expirationInterval="600000"/>
   </ee:object-store-caching-strategy
  • Cache scope
...
<ee:cache cachingStrategy-ref="Caching_Strategy" filterExpression="#[Boolean.parseBoolean(message.inboundProperties.'http.query.params'.fromCache)]" doc:name="Cache">
...
       </ee:cache>
..
3.1 Setting up the Global object-store-caching-strategy

The demo application that I have created, in essence is production ready, I have used the managed stored for persistence. Which means the cached payload would be persisted on to disc when the application is turned off.

There are 3 settings that I want you to note and they are
  • maxEntries - which denotes the number of request and response you want the cache to store
  • entryTTL - which denotes the time to live of a cached request and response item, here I have set it to be 60k seconds
  • expirationInterval - this is where people find it hard wrap their heads around, this field is actually for the dormant “Cache Officer” to wake up and sweep off (clean) expired cache, here I have instructed the “Cache Officer” to clean up the cache (delete expired items) every 600 seconds.

3.2 Setting up the Cache Scope

Here the developer need to envelope all the message processors that would be required for caching (hence its name cache scope). In this demo application I have allowed the user to post in a boolean value to the “filterExpression” cache scope so as to allow users to turn on and turn off caching when necessary. When “filterExpression” is set to true the mule application would service user request via a cache response, if there is a “Cache Hit”, but in the event of a “Cache Miss”, the operation would proceed as usual.

When the “filterExpression” is set to false, the user request would still be passed on to the following operations in the cache scope regardless of “Cache Hit” or “Cache Miss” results. The “filterExpression” is akin to a manual override field that allows us to override caching behaviour.

4.0 Types of Data for Caching

This is probably the most important section for you to read. MuleSoft Caching scope can only cache “Non-Consumable” response payloads, it cannot cache “Consumable” response payloads. This is a very important fact to note, the MuleSoft documentation says it, but it does not give further elaboration on the concept of “Consumable” and “Non-Consumable” payloads.

  • Consumable - Byte array or steams type of payload (usually generate from HTTP responses or dataweave outputs)

  • Non-Consumable - Sting (java.lang.String)

When I dig further into MuleSoft codes this is what I found, the cache scope implements a Consumable Message Filter.

Which invokes an isConsumable() method from the org.mule.DefaultMessage class.
As I dig deeper into the class, it actually calls another private method isConsumedFromAdditional(). Which validates the payload Class type against an array list of preconfigured types.
This is where I finally traced to down to the list of preconfigured ArrayList with payload class types that would be identified as consumable.
This is how the cache filter identifies consumable and non-consumable payload types.

5.0 Conclusion

Only non-consumable payload can be cached, and instance of a non-consumable payload at the time of this writing is java.lang.String payloads. In-Memory caching (which is the default caching method if nothing is configured) should not be applied to production, because it will use up all your production memory resources.

5 comments: