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 ### Related code - `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`
issue