API Management Best Practices, Tips, and Tricks: #3 How to implement a cache refresh policy

Here we are, ready for another edition of API Management Best Practices, Tips, and Tricks. Today, we will address another helpful Best practice, Tips, and Tricks that you must consider while implementing your API policies: How to implement a cache refresh policy.

#2 How to implement a cache refresh policy?

As Microsoft documentation references, APIs and operations in API Management (APIM) can be configured with response caching. Response caching can significantly reduce latency for API callers and backend load for API providers. APIM provides an out-of-the-box Internal cache, but this built-in cache is volatile and is shared by all units in the same region in the same API Management service. However, if you desire more robustness and additional capabilities, you can use an external Azure Cache for Redis, for example.

An excellent example of using cache capabilities is to store access tokens. Usually, API tokens have a “time-to-live” (TTL), which is the maximum time that the access token will be valid for use within the application. That means we don’t need to regenerate a token each time we call our API. Instead, we can cache that value in APIM, specifying the cache duration.

When working with cache inside APIM, there are at least two policies you need to know:

  • cache-store-value: The cache-store-value performs cache storage by key. The key can have an arbitrary string value and is typically provided using a policy expression.
  • cache-lookup-value: Use the cache-lookup-value policy to perform cache lookup by key and return a cached value. The key can have an arbitrary string value and is typically provided using a policy expression.

cache-store-value

Policy statement:

<cache-store-value key="cache key value" value="value to cache" duration="seconds" caching-type="prefer-external | external | internal" />

This is a practical sample of this policy:

<cache-store-value key="tokenValue" value="@((string)context.Variables["varToken"])" duration="600" />

cache-lookup-value

Policy statement:

<cache-lookup-value key="cache key value"
    default-value="value to use if cache lookup resulted in a miss"
    variable-name="name of a variable looked up value is assigned to"
    caching-type="prefer-external | external | internal" />

This is a practical sample of this policy:

<cache-lookup-value key="tokenValue" variable-name="varTokenValue" />

Cache is quite simple to implement and use inside APIM. However, and this is the reason for this blog post, in many situations, we have the need to refresh or force a refresh on our cache or in our cache value. Let’s say that while developing our operation policy, we made a mistake, like caching an incorrect value or setting the duration incorrectly. Now we need to refresh the value that is cached, but we don’t want to wait for the cache duration to expire – that can be 30 min – or modify the operation policy to add a cache removed statement and then modify that policy again to remove that “workaround”.

So, how can we easily handle these requirements or capabilities?

Well, the best way is to always address this requirement by design and implement a cache refresh mechanism.

Taking the token example, this can easily be implemented by:

  • Adding an optional header on your API methods, let’s say:
    • skip-cache header that “allows” a true or false value
      • If it is true, you need to force to refresh the value that is cached.
      • Otherwise – if the value is false – you use the cached value.
  • In the inbound policy of your operation, add the following statements:
    • Read the value of the skip-cache header. If it doesn’t exist, the default value is false.
    • Check if the skip-cache header value is false:
      • If the condition is true:
        • Try to read the value from the cache:
          • If it is present in the cache, return the cached value.
          • If it is not present, call the token renewal API.
    • Otherwise, perform a call to the token renewal API.
<inbound>
	<set-variable name="varSkipCache" value="@(context.Request.Headers.GetValueOrDefault("skip-cache", "false"))" />
	<choose>
		<when condition="@((string)context.Variables["varSkipCache"] == "false")">
			<cache-lookup-value key="tokenValue" variable-name="varTokenValue" />
			<choose>
				<when condition="@(context.Variables.ContainsKey("varTokenValue"))">
					<return-response>
						<set-status code="200" reason="OK" />
						<set-header name="Content-Type" exists-action="override">
							<value>application/json</value>
						</set-header>
						<set-header name="CachedResponse" exists-action="override">
							<value>true</value>
						</set-header>
						<set-body>@((string)context.Variables["varTokenValue"])</set-body>
					</return-response>
				</when>
			</choose>
		</when>
	</choose>
    ... implement the call to the token renewal API

Note: It is a good practice to add a response header stating the response is cached.

  • In the outbound policy of your operation, add the following statements:
    • Read the token renewal API response.
    • Store the value in the cache for next time.
    • And return the response back to the caller.
<outbound>
	<set-variable name="bodyResponse" value="@(context.Response.Body.As<string>())" />
	<set-variable name="varToken" value="@{
		... logic to read the token value from the response
	}" />
	<!-- Store in cache for next time -->
	<cache-store-value key="tokenValue" value="@((string)context.Variables["varToken"])" duration="600" />
	<return-response>
		<set-status code="200" reason="OK" />
		<set-header name="Content-Type" exists-action="override">
			<value>text/plain</value>
		</set-header>
		<set-header name="CachedResponse" exists-action="override">
			<value>false</value>
		</set-header>
		<set-body>@((string)context.Variables["varToken"])</set-body>
	</return-response>
</outbound>

I hope you enjoy this tip and stay tuned for the following Azure API Management Best practices, Tips, and Tricks.

If you liked the content or found it helpful and want to help me write more content, you can buy (or help buy) my son a Star Wars Lego! 

Author: Sandro Pereira

Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc. He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.

Leave a Reply

Your email address will not be published. Required fields are marked *

turbo360

Back to Top