MuleSoft Caching Strategy for Performance Improvement
MuleSoft Caching Strategy for Performance Improvement
Learn about the three ways MuleSoft provides to cache responses, which speeds up performance by preventing requests from overwhelming back end systems.
Join the DZone community and get the full member experience.Join For Free
xMatters delivers integration-driven collaboration that relays data between systems, while engaging the right people to proactively resolve issues. Read the Monitoring in a Connected Enterprise whitepaper and learn about 3 tools for resolving incidents quickly.
This article will talk about how to implement caching in MuleSoft. If we expose an API to external users, especially an API that allows users to get information, then I would highly recommend you implement caching to prevent requests from hammering the backend systems.
A cache is like an intermediary endpoint that catches your application response based on a particular request. When a request is cached, then further requests for the same data would be retrieved from a cache instead of the actual endpoint (as illustrated in Figure 1.0).
MuleSoft caches responses 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 goes through the cache it would result in a “cache miss.” When this happens, the request would be forwarded to the later application logic to retrieve the necessary response data to fulfill the request (in steps 2, 3, and 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, the cache returns the cached response back to User 2, eliminating the network and processing latency (from having to go through steps 1 to 6).
2.0 Types of Cache Store Provided by MuleSoft
MuleSoft provides three ways to cache your application response:
In memory caching: The easiest form of caching, zero to minimal setup required, not recommended for production.
Managed Store caching: This is an out of the box MuleSoft persisted caching, which means that after you restart the runtime 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 suggests, 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.
The Groovy script just generates a random number. The logger in the front and back of the flow will show evidence of data being retrieved from the cache when it is not writing into the console. The application also shows a graphical Cache Scope, which envelopes all of the message processors in the flow, which implies caching of the end payload result that would be returned from any of its message processors.
You may download the full source code from this link.
In order to configure an application for caching, you need to set up two things in your Mule application:
<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>
... <ee:cache cachingStrategy-ref="Caching_Strategy" filterExpression="#[Boolean.parseBoolean(message.inboundProperties.'http.query.params'.fromCache)]" doc:name="Cache"> ... </ee:cache> ..
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 onto disc when the application is turned off.
There are three settings that I want you to note:
maxEntries - which denotes the number of requests and responses 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 it; this field is actually for the dormant “Cache Officer” to wake up and sweep off (clean) the expired cache. Here, I have instructed the “Cache Officer” to clean up the cache (delete expired items) every 600 seconds.
Here the developer needs 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 requests 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 behavior.
4.0 Types of Data for Caching
This is probably the most important section for you to read. The 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 streams type of payload (usually generate from HTTP responses or dataweave outputs).
Non-Consumable- String (java.lang.String).
When I dug further into the MuleSoft code, this is what I found: the cache scope implements a Consumable Message Filter.
This invokes an
isConsumable() method from the org.mule.DefaultMessage class.
As I dug deeper into the class, I found that it actually calls another private method,
isConsumedFromAdditional(), which validates the payload Class type against an array list of ppre-configuredtypes.
This is where I finally traced it 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.
Only non-consumable payloads can be cached; an 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.
Opinions expressed by DZone contributors are their own.