Rate Limiting REST server

Has anyone been able to determine a way to rate limit REST calls with an XData Server?


Rhett Price

What exactly do you want to limit? The number of requests per second? Per minute? Per endpoint?

Usually to avoid a high workload (i.e., nr of requests per an amount of time on any endpoint, to avoid DoS attacks), an edge proxy is the most useful solution. You can add Traefik for example in from of an XData server and just set a rate limit there.

To control limits per endpoint, or something more business-oriented (like maximum requests per user) then you should do that at business level, i.e., increasing a value in the database related to thad user and that requested resource.

Hmmm.... I have been curious about this as well. Let's say we want something kind of simple. For a given XData server, I'd like to set a rate limit of 10 requests per IP address per minute, 100 per hour, and 1,000 per day, aggregated across all the service endpoints on that particular server. A "ratelimitcheck" function might look something like this (pseudocode).

function ratelimitcheck
// create key for current minute + IP address
// get value from RateMinute TDictionary using that key 
// if value + 1 > 10 then return HTTP code 429
// Also set "retry-after" value to number of seconds left in current minute
// Otherwise update TDictionary so key now has value + 1

Do the same for RateHour and RateDay TDictionaries. These three dictionaries (one for minute, hour, and day) would of course need to be defined outside of the service endpoints as they would be called for each request made to the XData server.

The question then is where to stick this function call? Is there an XData event that can be used to intercept all of the incoming requests and immediately return the 429 code, bypassing the call to the service endpoint entirely? Or is this something that should be called separately from each service endpoint that we want to rate limit in this fashion?

If we only wanted this for unauthenticated endpoints, for example, that might make more sense. On the other hand, we'd like this check to be done as quickly and efficiently as possible, so as early in the connection process as we can manage, before doing things like sorting through parameters or decompressing incoming data streams and that sort of thing.

I haven't checked to see how to go about making calls to TDictionary threadsafe but as we'd not need to delete anything, this is likely not going to cause too many problems. The dictionaries will likely need to be cleared periodically, depending on traffic levels, naturally.

Also, the idea here is that the rate limits apply to a particular minute, hour, or day, so the rate limit could be exceeded if the request started near the end of, say, an hour and carried over into the next hour. Likewise, if multiple XData servers were handling requests from the same IP addresses, this wouldn't work quite as well, but maybe that doesn't matter so much so long as no one individual XData server is being swamped with too many requests.

The idea is more to make this as efficient as possible to calculate, without having to run a database query to track all the individual requests and determine whether a new request exceeded a limit in precisely the past minute, hour, or day, given that this would be running for every single request, the overwhelming majority of which won't exceed the limits.

The best place I think is to create a middleware. The middleware can do all those checks, and if the limit is reached, it will just return 429 without calling Next(Content), which is the usual process that forwards the request to the next middleware and eventually to the service operation.

1 Like

I'll give it a try!

1 Like