Maven Virtual Registries: cache the permissions evaluation across requests

🔥 Problem

While checking the current performance of the maven virtual registry, we noticed that we have pretty high cpu_s values for requests that are a cache hit:

Screenshot_2024-11-14_at_11.34.33

How can get an average cpu_s close to 200ms for an API endpoint that has to:

  1. check a feature flag.
  2. fetch the User, Registry, Group, Upstream, CachedResponse.
  3. verify the user permissions.
  4. send the send_url workhorse instruction to send the file from object storage.

?

All of these tasks should not be CPU intensive. 🤔

Upon digging execution times locally, we discovered that the permissions evaluation can actually take a non trivial amount of time. Our assumption here is because of the amount of conditions that have to be evaluated between the Registry and Group policy.

🚒 Solution

Instead of trying to improve the permissions policies, we can use a different path.

A Maven client that will use the virtual registry has very high chances to send multiple API requests. All these requests will (obviously) use the exact same credentials. So, the permissions evaluation (3.) will exactly the same for all requests.

Could we leverage that fact?

Basically, on the very first request, we evaluate the permissions and cache the result in a persistent location. Subsequent requests will use the cached result instead of running the permissions evaluation again.

In technical terms, instead of:

can?(current_user, :read_virtual_registry, registry)

use:

Rails.cache.fetch("#{current_user.cache_key}/read_virtual_registry/#{registry.cache_key}", expires_in: 5.minutes) do
  can?(current_user, :read_virtual_registry, registry)
end

Our local trials on this change showed that the mvn execution time went from 01:02 min to 42.007 s (~30% improvement). The improvement on gitlab.com may vary.

Edited by David Fernandez