How can I store the results of the ServiceCallout policy in cache? and later, retrieve it from cache?

Not applicable

Hi,

I am trying to mashup the data from a number of ServiceCallout policies, but only the proxy itself is getting returned from cache. What is the best way to return the result from a service callout from cache rather than making a new request each time to the backend?

When I try to do this, it doesn't seem to hit the cache for the service callouts which really slows down the overall response time.

Thanks

Solved Solved
2 5 6,375
1 ACCEPTED SOLUTION

I suspect you are using the "response cache". That works only for the target response. I think you want to use the cache primitives - LookupCache and PopulateCache - to store the result of the service callout.

For example, your flow might be like this:

      <Request>
        <!-- check the cache for the data previously retrieved -->
        <Step><Name>CacheLookup-MyData</Name></Step>
        <Step>
          <Name>SC-RetrieveMyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>Extract-MyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>CacheInsert-MyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>AV-MyData</Name>
          <Condition>cached.my.data != null</Condition>
        </Step>
         ....


What's going on there? The first step is a LookupCache . It looks in the cache for a particular key, and stores the resulting data into the context variable "cached.my.data". If the cache entry does not exist, then that context variable will be null after executing the first policy. If the cache entry exists, that context variable will be non-null.

The next 4 policies execute conditionally. The SC-RetrieveMyData is a ServiceCallout that calls some external thing. The Extract-MyData is an ExtractVariables policy that pulls something from the ServiceCallout response into a variable. (This assumes you do not want the entire response.content to be cached, but rather, some subset or portion of it) You can call this variable anything, let's call it "my.data". The CacheInsert-MyData is a PopulateCache policy that inserts the extracted value ("my.data") into the cache, so that it is available for the next request.

Finally, the AV-MyData runs only if the original LookupCache succeeded. Basically this just copies the retrieved value from the "cached.my.data" variable into the "my.data" variable .

When the cache is warm, the first and final policies in the above snip will execute. When the cache is cold, the first 4 policies will execute. Either way, "my.data" holds the correct value at the end of this sequence. Subsequent policies can reference "my.data" which has been filled either by the AV-MyData, in the case of a cache hit, or by the Extract-MyData policy, in the case of a cache miss.

Does this make sense? This is a handy re-usable pattern for caching data retrieved from ServiceCallout policies.

For some further detail, here's an example of what the CacheLookup-MyData might look like:

<LookupCache name='CacheLookup-MyData'>
  <CacheResource>cache1</CacheResource>
  <AssignTo>cached.my.data</AssignTo> <!-- name of flow variable -->
  <Scope>Application</Scope>
  <CacheKey>
    <Prefix>something</Prefix>
    <KeyFragment ref="client_id"/> <!-- as an example -->
  </CacheKey>
</LookupCache>

The CacheInsert is something like this:

<PopulateCache name='CacheInsert-MyData'>
  <CacheResource>cache1</CacheResource> <!-- choose a proper name -->
  <Source>my.data</Source>
  <Scope>Application</Scope>
  <CacheKey> <!-- use same cache key as the LookupCache -->
    <Prefix>something</Prefix>
    <KeyFragment ref="client_id"/>
  </CacheKey>
  <ExpirySettings>
    <TimeoutInSec>7200</TimeoutInSec>
  </ExpirySettings>
</PopulateCache>

And the AV-MyData is like this:

<AssignMessage name='AV-MyData'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <AssignVariable>
    <Name>my.data</Name>
    <Ref>cached.my.data</Ref>
  </AssignVariable>
</AssignMessage>

I will leave the ServiceCallout and ExtractVariables policy for you to implement. The key is that the Extract should operate on the response message that has been set by ServiceCallout, and should set "my.data".

As I said, you can omit the ExtractVariables if you wish to cache the entire response.content from the servicecallout.

The cache key can be anything. The example code here shows client_id, but you could use anything that identifies the data to be cached. a query param, or a header, or whatever is appropriate.

View solution in original post

5 REPLIES 5

I suspect you are using the "response cache". That works only for the target response. I think you want to use the cache primitives - LookupCache and PopulateCache - to store the result of the service callout.

For example, your flow might be like this:

      <Request>
        <!-- check the cache for the data previously retrieved -->
        <Step><Name>CacheLookup-MyData</Name></Step>
        <Step>
          <Name>SC-RetrieveMyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>Extract-MyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>CacheInsert-MyData</Name>
          <Condition>cached.my.data = null</Condition>
        </Step>
        <Step>
          <Name>AV-MyData</Name>
          <Condition>cached.my.data != null</Condition>
        </Step>
         ....


What's going on there? The first step is a LookupCache . It looks in the cache for a particular key, and stores the resulting data into the context variable "cached.my.data". If the cache entry does not exist, then that context variable will be null after executing the first policy. If the cache entry exists, that context variable will be non-null.

The next 4 policies execute conditionally. The SC-RetrieveMyData is a ServiceCallout that calls some external thing. The Extract-MyData is an ExtractVariables policy that pulls something from the ServiceCallout response into a variable. (This assumes you do not want the entire response.content to be cached, but rather, some subset or portion of it) You can call this variable anything, let's call it "my.data". The CacheInsert-MyData is a PopulateCache policy that inserts the extracted value ("my.data") into the cache, so that it is available for the next request.

Finally, the AV-MyData runs only if the original LookupCache succeeded. Basically this just copies the retrieved value from the "cached.my.data" variable into the "my.data" variable .

When the cache is warm, the first and final policies in the above snip will execute. When the cache is cold, the first 4 policies will execute. Either way, "my.data" holds the correct value at the end of this sequence. Subsequent policies can reference "my.data" which has been filled either by the AV-MyData, in the case of a cache hit, or by the Extract-MyData policy, in the case of a cache miss.

Does this make sense? This is a handy re-usable pattern for caching data retrieved from ServiceCallout policies.

For some further detail, here's an example of what the CacheLookup-MyData might look like:

<LookupCache name='CacheLookup-MyData'>
  <CacheResource>cache1</CacheResource>
  <AssignTo>cached.my.data</AssignTo> <!-- name of flow variable -->
  <Scope>Application</Scope>
  <CacheKey>
    <Prefix>something</Prefix>
    <KeyFragment ref="client_id"/> <!-- as an example -->
  </CacheKey>
</LookupCache>

The CacheInsert is something like this:

<PopulateCache name='CacheInsert-MyData'>
  <CacheResource>cache1</CacheResource> <!-- choose a proper name -->
  <Source>my.data</Source>
  <Scope>Application</Scope>
  <CacheKey> <!-- use same cache key as the LookupCache -->
    <Prefix>something</Prefix>
    <KeyFragment ref="client_id"/>
  </CacheKey>
  <ExpirySettings>
    <TimeoutInSec>7200</TimeoutInSec>
  </ExpirySettings>
</PopulateCache>

And the AV-MyData is like this:

<AssignMessage name='AV-MyData'>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <AssignVariable>
    <Name>my.data</Name>
    <Ref>cached.my.data</Ref>
  </AssignVariable>
</AssignMessage>

I will leave the ServiceCallout and ExtractVariables policy for you to implement. The key is that the Extract should operate on the response message that has been set by ServiceCallout, and should set "my.data".

As I said, you can omit the ExtractVariables if you wish to cache the entire response.content from the servicecallout.

The cache key can be anything. The example code here shows client_id, but you could use anything that identifies the data to be cached. a query param, or a header, or whatever is appropriate.

Dino, thanks for the comprehensive answer, after some more head scratching I was finally able to get my head around the entire concept. I think the documentation should be updated with a good example like yours! Philip

I had implemented the same thing and was coming here to post the answer, but you explained it better than I ever could, Dino.

Glad to be able to help!

Thanks, @Dino. And great question, @Philip Sinclair. I've added a link to this from the Service Callout policy doc, in the About section.