Application-level rate limiting based on context metadata
Context metadata could be used as elegant means of implementing an application-level rate limiting utility, that could be used throughout the application to protect and isolate resource-heavy operations.
Examples of metadata we use, that would be useful in the context of rate limiting:
-
meta.root_namespace
: the root namespace that the current operation is taking place for (eggitlab-org
) -
meta.user
: the username of the current user -
meta.project
: the current project -
meta.client_id
: proposed: a client identifier for both anonymous and non-anonymous users
This rate limited would rely on Gitlab::ApplicationRateLimiter
(as previously discussed in https://gitlab.com/gitlab-org/gitlab/issues/37490#note_252278171)
How would it work?
To illustrate this, here is what the code might look like:
Gitlab::ApplicationRateLimiter.for_context(5.minutes, per_project: 10, per_client_id: 5, per_namespace: 50) do
# Do something expensive, like generate a project export
end
What would this code do?
In an 5 minute period, it would ensure that the operation within the yield block would only happen a maximum of 10 times for any single project, 50 times for an single namespace and 5 times per user - or in the case of anonymous users - IP address.
If the action is rate limited, the method will throw an exception rather than run the code block.
If this exception bubbles all the way out of the controller, a middleware will automatically convert it into an HTTP 429 status code.
Why not simply rely on HTTP rate limiting?
HTTP rate limiting is good for rate limiting some thing s- specific URLS or client IPs, but here we are rate limiting along extra dimensions that we don't want to leak up the HTTP abstraction. This approach gives much better protection including:
- Rate limiting protection against a user attempting to export all projects in a namespace
- Rate limiting against multiple anonymous IPs attempting to export the same project at the same time
- Rate limiting against a mixture of ways of invoking an expensive action (for example, via the web UI, rest API and GraphQL)
Related issues