Rate-limiting specific resource / verb combinations

kkleva
Participant V

Hello,

As an API product owner I require the means to set a different rate limit for specific resource nouns and verbs based on the API product associated with a developer app so I may manage traffic in a more effective way.

For example:

GET /v1/giftcards - Allows app developer to get list of available giftcards - limit 100 request per min

POST /v1/giftcards/order - Allows app developer to order a giftcard - limit 10 request per min

As an added perk I must be able to set these values based on the API product the APP is registered in.

To date I've been able to do the second part of this requirement using JSON data within custom attributes of the API product then using those traffic management values within a JavaScript policy.

For example in API Product A, I would have custom attribute values:

GiftCard-Quota = {"value":100, "unit":"min"}

This approach has worked to date however I now seek the flexibility to do something like

GiftCard-Quota = [
{"value":100, "unit":"min", "request.verb":"GET", "path": "/v1/giftcards"},
{"value":10, "unit":"min", "request.verb":"POST", "path": "/v1/giftcards/order"}
.....
]

Naturally, this approach really starts to get message with APIs with lots of resources / verb so I'm really hesitant to continue down that path.

Are there any apigeeks out there who may have encountered a similar requirement in their own journey?

cc.

@Sean Case @Diego Zuluaga @derekdorr @knandigam @Carlos Eberhardt @epackwood

Solved Solved
2 5 491
1 ACCEPTED SOLUTION

@Kristopher Kleva - perhaps we can do this with a naming convention trick.

Irrespective of where the spike limits are stored, they will be assigned to variables in Apigee, i.e. if they are in product attributes, they will be assigned to variables by VerifyApiKey or OAuthV2 VerifyAccessToken. If they are coming from KVM, you will need to read them yourself and assign to variables, etc.

Once these limits are in variables, you will need one JS policy to set the spike value for this request using a predefined naming convention. Let's assume your convention is:

{VERB}{NORMALISED-PATH}

By normalised, I mean 'convert / to -'

Then you define the following custom variables in product:

GET-v1-giftcards
POST-v1-giftcards-order

Then in preflow request, you can have a JS policy that reads the value of the relevant variable based on request and set another variable that will store the spike limit:

var verb = context.getVariable('request.verb');
var path = context.getVariable('proxy.pathsuffix');
var normalisedPath = normalisePath(path); //you can do anything you want here.

// variable name would be 'apiproduct.GET-v1-giftcards'
var spikeLimitVariableName = 'apiproduct.' + verb + normalisedPath;
var spikeLimit = context.getVariable(spikeLimitVariableName);

//set the spike limit to a centralised variable
context.setVariable('spikeLimitForThisRequest', spikeLimit);

Then you can have a single spike arrest policy:

<SpikeArrest name="SpikeArrest">
  <Rate ref="spikeLimitForThisRequest">100ps</Rate>
</SpikeArrest>

If there is a new resource without any spike configuration in product attributes, you will get 100ps - beware I haven't tested this....

When you want to change the spike limit or when you want to introduce a new resource, you will just need to add the configuration in correct naming conventions to product attributes.

View solution in original post

5 REPLIES 5

I have been asked about this before - so looking at your example

"request.verb":"POST","path":"/v1/giftcards/order"

this is basically a conditional flow, correct? In that case, can we just apply individual quota policy for each conditional flow?

and use named custom variables for each conditional flow in the product -

flow1.value = 100

flow1.unit = min

...

I am also curious to know what would be a best way to do this?

@Kristopher Kleva - perhaps we can do this with a naming convention trick.

Irrespective of where the spike limits are stored, they will be assigned to variables in Apigee, i.e. if they are in product attributes, they will be assigned to variables by VerifyApiKey or OAuthV2 VerifyAccessToken. If they are coming from KVM, you will need to read them yourself and assign to variables, etc.

Once these limits are in variables, you will need one JS policy to set the spike value for this request using a predefined naming convention. Let's assume your convention is:

{VERB}{NORMALISED-PATH}

By normalised, I mean 'convert / to -'

Then you define the following custom variables in product:

GET-v1-giftcards
POST-v1-giftcards-order

Then in preflow request, you can have a JS policy that reads the value of the relevant variable based on request and set another variable that will store the spike limit:

var verb = context.getVariable('request.verb');
var path = context.getVariable('proxy.pathsuffix');
var normalisedPath = normalisePath(path); //you can do anything you want here.

// variable name would be 'apiproduct.GET-v1-giftcards'
var spikeLimitVariableName = 'apiproduct.' + verb + normalisedPath;
var spikeLimit = context.getVariable(spikeLimitVariableName);

//set the spike limit to a centralised variable
context.setVariable('spikeLimitForThisRequest', spikeLimit);

Then you can have a single spike arrest policy:

<SpikeArrest name="SpikeArrest">
  <Rate ref="spikeLimitForThisRequest">100ps</Rate>
</SpikeArrest>

If there is a new resource without any spike configuration in product attributes, you will get 100ps - beware I haven't tested this....

When you want to change the spike limit or when you want to introduce a new resource, you will just need to add the configuration in correct naming conventions to product attributes.

This works but I think where it get hard when the path includes a variable. So for example,

{"value":100, "unit":"min", "request.verb":"GET", "path": "/v1/giftcards/{id}"}

So in the case of GET /v1/giftcards/123456 we would want treat that with a diff rate limit as GET /v1/giftcards

@Kristopher Kleva - in that case you will need to find a request variable that is unique per flow - choose one from http://apigee.com/docs/api-services/reference/variables-reference.

For example how about "current.flow.name"? This will give you the name of the flow you defined, as in:

<Flow name="GET giftcards">
...
</Flow>

More info about this particular variable here: http://apigee.com/docs/api-services/reference/variables-reference#variablesforapiproxyflows.

I tried setting the spike arrest rate by referring a variable value for different resource. Although, the rate is set correctly but "ratelimit.OpLevel_SpikeArrest_Throttle.allowed.count" is not changing and remains same. Suppose, for first call the rate limit is set to 240pm. Then , for second call of same API but different resource though the rate limit is 60pm but it still the value of "ratelimit.OpLevel_SpikeArrest_Throttle.allowed.count" is 240.

Could you please help on this. Please let me know if you also observed the same behaviour.