2023-03-30

Using soft limits to prevent abuse with good user experience

When you develop a service you will probably run into a situation where you want to put in some arbitrary large limit on something to prevent bad behaving clients from using too much of your resources. This can be a max length of the URI, max length of each request header, max size of a request and so on. What we tend to do is often to return some kind of error when this happens and expect the client to fix their request if it is legitimate. But what if it is a legitimate request - just something you didn't expect to be valid? Or what if there is a bug in the client, but the user have no way of fixing the client but rely heavily on your service? Do you really want to completely break your users in these cases?

I have come across situations like this several times in my career and often the problem is solved short term by increasing the arbitrary limits. This however only works when clients are few and don't expect quick fixes and you can deploy a fix quickly. I have however found another way to deal with this problem.

Instead of completely blocking unexpectedly large request I have found it effective to process them differently than "normal" requests. The solution is to allow each client a small emergency quota. Any requests conforming to the limits expected do not need emergency quota. But when a request comes in that in some what exceeds the expected limits (typically on resources) I can check if that client have enough emergency quota to process the request. If not; I return an error informing the client they do not have quota to process the request since it exceeds some specified limit.

This way any users that only have an occasional extreme request will never notice what happened. But users that exceed limits a lot will only get a fraction of the requests processed but they will also (hopefully) get a clear error from their client. And whoever maintains the client can clearly see what the problem is.

But the biggest benefit is that I can protect my own service from extreme requests causing a denial of service since the quota check ensures I only process a small fraction of the dangerous requests. It is true I take a small risk this way that always needs to be weighted against the user experience. So this is not a pattern I use all the time, but rather a useful pattern in certain situations.

No comments:

Post a Comment