Add short lived cache for Search::GroupsFinder and Search::ProjectsFinder

Problem

The Search::GroupsFinder and Search::ProjectsFinder classes are currently called multiple times during search operations, particularly in advanced search scenarios. While they use Gitlab::SafeRequestStore for request-scoped caching (as seen in ee/lib/search/elastic/concerns/authorization_utils.rb), this cache only persists for a single request. Subsequent requests from the same user must recompute the authorized groups and projects, which can be expensive operations.

Proposal

Implement a short-lived Redis cache (5-10 minutes TTL) for these finders to reduce repeated authorization queries across multiple requests from the same user.

Cache specifications:

  • Duration: 5-10 minutes TTL
  • Cache key format:
    • Groups: search_user:{user_id}:min_access_level:{level}:groups
    • Projects: search_user:{user_id}:projects
  • Storage: Redis via Rails.cache (uses Gitlab::Redis::Cache)
  • Scope: Per-user basis
  • Invalidation: Time-based expiration (TTL)

Implementation considerations

Parameters to include in cache key:

For Search::GroupsFinder:

  • min_access_level - MUST be included in cache key as it affects results
    • Used in groups_for_user method with different access levels
    • Current SafeRequestStore key already includes this: "user:#{user&.id}-min_access_level:#{min_access_level}-groups_for_search"
    • Defaults to GUEST if not provided
  • features - Can be ignored for caching purposes
    • The features param is mutually exclusive with min_access_level and internally converts to a min_access_level
    • Since it's converted to min_access_level, we only need to cache based on the resolved access level

For Search::ProjectsFinder:

  • No parameters - The _params argument is currently unused (placeholder for future)
  • Cache key only needs user_id

Redis is appropriate here because:

  1. Rails.cache already uses Gitlab::Redis::Cache with an 8-hour default TTL
  2. The cache instance has key eviction policies configured
  3. This is truly cache-like data that can be regenerated if absent
  4. The 5-10 minute TTL is much shorter than the default, reducing staleness concerns

Caching strategy:

  • Layer the Redis cache on top of the existing Gitlab::SafeRequestStore caching
  • Keep the request-scoped cache for within-request performance
  • Add Redis cache to reduce cross-request overhead
  • For GroupsFinder: Cache separately for each min_access_level value
  • For ProjectsFinder: Single cache per user (no parameter variations)

Cache invalidation considerations:

  • Short TTL (5-10 minutes) provides a reasonable balance between performance and data freshness
  • User permissions don't change frequently enough to require immediate invalidation
  • Acceptable for search results to reflect permissions up to 10 minutes old

Feature flag consideration:

  • This should be implemented behind a derisk feature flag (e.g., search_finders_redis_cache)
  • Allows gradual rollout and monitoring of Redis memory impact
  • Enables quick rollback if caching causes unexpected authorization issues or memory pressure
  • Can be enabled per-group or percentage-based rollout to validate behavior
  • ee/app/finders/search/groups_finder.rb - Accepts min_access_level and features params
  • ee/app/finders/search/projects_finder.rb - Currently no params used
  • ee/lib/search/elastic/concerns/authorization_utils.rb - Current SafeRequestStore usage with min_access_level
Edited by 🤖 GitLab Bot 🤖