Need to implement Response Caching with request mapping with response

I have following request:

Request with 2 query params: AppID and ObjectID

Request Payload : 

{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}

Now I want to implement response caching in a way that if the same request comes then APIGEE responds with the corresponding request. The same request is defined as combination of exact same payload and query params.

I want to create a mapping in the APIGEE for request and response and maintain that in APIGEE. But I dont think I can create such a mapping with the help of ResponseCache Policy as I cant keep the request payload as key for it. Please suggest a better approach.

 

Solved Solved
0 3 226
1 ACCEPTED SOLUTION

You could consider hashing the payload? 

With that said, you may also want to think about TPS and payload size of your use case as this could become an intensive operation

View solution in original post

3 REPLIES 3

You could consider hashing the payload? 

With that said, you may also want to think about TPS and payload size of your use case as this could become an intensive operation

You are correct that the ResponseCache policy in APIGEE cannot use the request payload as the key for caching. However, you can use a combination of query parameters and a custom cache key to achieve the desired behavior.

Here is an example of how you can implement response caching with APIGEE for your use case:

  1. Define a custom cache key: You can define a custom cache key that is a combination of the query parameters and the request payload. For example, you can concatenate the AppID and ObjectID query parameters with the request payload and use that as the cache key.

  2. Configure the ResponseCache policy: In the APIGEE proxy, you can configure the ResponseCache policy to use the custom cache key that you defined in step 1. You can also set the caching duration, cache control headers, and other caching parameters in the policy.

  3. Implement a cache lookup: When a new request comes in, you can first check if there is a cached response for that request. To do this, you can use a CacheLookup policy in the APIGEE proxy. If there is a cached response, you can return it directly without processing the request further.

  4. Cache the response: If there is no cached response for the request, you can process the request and generate a response. Before returning the response to the client, you can cache the response using the ResponseCache policy with the custom cache key that you defined in step 1.

Here is a sample APIGEE proxy configuration for implementing response caching with a custom cache key:

<ProxyEndpoint name="default">
<HTTPProxyConnection>
<BasePath>/example</BasePath>
</HTTPProxyConnection>
<PreFlow>
<CacheLookup>
<CacheKey>{{request.queryparam.AppID}}-{{request.queryparam.ObjectID}}-{{request.content}}</CacheKey>
<Scope>Application</Scope>
<LookupTimeout>10</LookupTimeout>
<Dynamic>true</Dynamic>
</CacheLookup>
</PreFlow>
<PostFlow>
<ResponseCache>
<CacheKey>{{request.queryparam.AppID}}-{{request.queryparam.ObjectID}}-{{request.content}}</CacheKey>
<Scope>Application</Scope>
<ExpirySettings>
<TimeoutInSec>3600</TimeoutInSec>
<UseDateExpiry>false</UseDateExpiry>
</ExpirySettings>
</ResponseCache>
</PostFlow>
</ProxyEndpoint>

In the above configuration, the cache key is defined as a combination of the AppID and ObjectID query parameters and the request payload. The CacheLookup policy checks if there is a cached response for the request, and the ResponseCache policy caches the response if there is no cached response. You can modify the caching parameters and the cache key format as per your requirements.

I hope this helps you implement response caching with APIGEE for your use case.

This one is tricky. You may want to review the expected request payloads and see if there are any other possible elements you could key off of. Also, be mindful that there are limits to the cache key size. Currently, the cache key can be no larger than 2KB (2048 characters).

With all of those caveats, you have at least two possibilities. Neither are great.

Option 1. Base64 encode the request payload, then concatenate that to the query params. To do this, you first need an AssignMessage policy to convert the string to base64 using a message template.

<AssignMessage async="false" continueOnError="false" enabled="true" name="convertBodytoBase64">
    <DisplayName>convertBodytoBase64</DisplayName>
    <Properties/>
    <AssignVariable>
        <Name>base64_body</Name>
        <Value>null</Value>
        <Template>{encodeBase64(request.content)}</Template>
    </AssignVariable>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

Then in your Response Cache policy, you can set the `<CacheKey>` like so:

<CacheKey>{request.queryparam.AppID}-{request.queryparam.ObjectID}-{base64_body}</CacheKey>

 

Option 2: Create a JSON dictionary using an array in a Javascript policy. You'll need to know the expected payloads in advance, but if there is a short list this is viable. Keep in mind, this would also allow you to bypass the 2KB cache key limit.

First, create your object map Javascript resource file `json_map.js`, like so:

// sample JSON objects
var obj1 = {
  "glossary": {
    "title": "example glossary",
    "GlossDiv": {
      "title": "S",
      "GlossList": {
        "GlossEntry": {
          "ID": "SGML",
          "SortAs": "SGML",
          "GlossTerm": "Standard Generalized Markup Language",
          "Acronym": "SGML",
          "Abbrev": "ISO 8879:1986",
          "GlossDef": {
            "para": "A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso": [
              "GML",
              "XML"
            ]
          },
          "GlossSee": "markup"
        }
      }
    }
  }
};

var obj2 = {
  "glossary": {
    "title": "example glossary",
    "GlossDiv": {
      "title": "T",
      "GlossList": {
        "GlossEntry": {
          "ID": "SGML",
          "SortAs": "SGML",
          "GlossTerm": "Standard Generalized Markup Language",
          "Acronym": "SGML",
          "Abbrev": "ISO 8879:1986",
          "GlossDef": {
            "para": "A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso": [
              "GML",
              "XML"
            ]
          },
          "GlossSee": "markup"
        }
      }
    }
  }
};

// create an empty array
var req_obj_dictionary = [];

// add objects to the array
req_obj_dictionary.push(obj1);
req_obj_dictionary.push(obj2);

 Then, create another Javascript resource file `find_json.js`, like so:

var req_obj = JSON.parse(context.getVariable("request.content"));

var match_obj = context.setVariable("json_map_index",findObjectIndex(req_obj_dictionary,req_obj));

function findObjectIndex(arr, obj) {
  for (var i = 0; i < arr.length; i++) {
    if (JSON.stringify(arr[i]) === JSON.stringify(obj)) {
      return i.toString();
    }
  }
  return "-1";
}

And finally, create a Javascript policy, like so:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="json-obj-map">
    <DisplayName>json-obj-map</DisplayName>
    <Properties/>
    <IncludeURL>jsc://json_map.js</IncludeURL>
    <ResourceURL>jsc://find_json.js</ResourceURL>
</Javascript>

 Now, you can reference the JSON objects as an index of your array when storing them as a cache key in the Response Cache policy:

{request.queryparam.AppID}-{request.queryparam.ObjectID}-{json_map_index}

Again, I want to reiterate that both options are not great. You could run into some performance bottle necks, particularly as the proxy scales to 1000s of transactions per second. I strongly encourage you perform rigorous testing so make sure the performance and behavior is aligned with your SLOs.