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.
- skip-cache header that “allows” a true or false 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.
- Try to read the value from the cache:
- If the condition is true:
- 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!