GLQL/Work Items Advanced Search missing Elasticsearch routing parameter causing performance degradation

Summary

The GLQL/Work Items Advanced Search integration with Elasticsearch does not pass the root_ancestor_ids parameter in es_search_options, which prevents Elasticsearch from using shard routing. This causes queries to search across ALL shards instead of targeting specific shards for the namespace, resulting in performance degradation and increased cluster load.

Steps to reproduce

  1. Enable Advanced Search (Elasticsearch) for a GitLab instance
  2. Enable either glql_es_integration or work_items_list_es_integration feature flag
  3. Execute a GLQL query or Work Items List query for a specific project or group
  4. Observe the Elasticsearch query (via ES slow logs or monitoring)
  5. Notice the query does NOT include a routing parameter

Example: Query a group's work items via GLQL:

query GLQL_WorkItems {
  group(fullPath: "mygroup") {
    workItems(state: OPENED) {
      nodes { id title }
    }
  }
}

What is the current bug behavior?

Current behavior:

  • Elasticsearch queries from the Advanced Finder do NOT include the routing parameter
  • ES searches across ALL shards in the work_items index
  • Query performance degrades proportionally to the number of shards
  • Increased resource consumption on the Elasticsearch cluster

Evidence: In ee/lib/ee/search/advanced_finders/work_items_finder.rb (lines 105-109):

def es_search_options
  {
    index_name: INDEX_NAME
  }
end

The root_ancestor_ids parameter is missing, which is required for Elasticsearch routing.

What is the expected correct behavior?

Expected behavior:

  • Elasticsearch queries should include the routing parameter with the root namespace ID
  • ES should only search shards containing data for the specified namespace
  • Query performance should be optimal, especially on large GitLab instances
  • Resource consumption should be minimized

Expected ES query format:

{
  "routing": "group_123",
  "index": "gitlab-production-work-items",
  "body": { ... }
}

Relevant logs and/or screenshots

Working implementation for comparison:

The Vulnerability Elastic Finder correctly implements routing in ee/app/finders/security/vulnerability_elastic_base_finder.rb (lines 134-145):

def es_search_options
  {
    index_name: INDEX_NAME,
    # This key is used by es to find the correct shard.
    # See Elastic::Latest::Routing for how its used
    root_ancestor_ids: root_ancestor_ids
  }
end

def root_ancestor_ids
  [vulnerable.root_ancestor.id]
end

Routing flow:

  1. es_search_options includes root_ancestor_ids: [123]
  2. Search::Elastic::Relation passes options to Gitlab::Search::Client.execute_search
  3. Gitlab::Search::Client includes Elastic::Latest::Routing module
  4. routing_options(options) is called, which builds { routing: "group_123" }
  5. Routing parameter is added to the Elasticsearch query

Possible fixes

Solution:

Add root_ancestor_ids to es_search_options in ee/lib/ee/search/advanced_finders/work_items_finder.rb:

def es_search_options
  {
    index_name: INDEX_NAME,
    # This key is used by ES to find the correct shard
    # See Elastic::Latest::Routing for how it's used
    root_ancestor_ids: root_ancestor_ids
  }
end

def root_ancestor_ids
  [resource_parent.root_ancestor.id]
end

Why this works:

  • The resource_parent is already available (either a Project or Group)
  • Both Project and Group have a root_ancestor method
  • The root_ancestor.id is already calculated in base_params (line 154) for query filters
  • The routing infrastructure (Elastic::Latest::Routing) is already included in Gitlab::Search::Client

Performance Impact:

  • On GitLab.com with 1000+ top-level groups across 10 shards:
    • Without routing: Queries search 10 shards
    • With routing: Queries search ~1 shard (10x improvement)

Files to modify:

  • ee/lib/ee/search/advanced_finders/work_items_finder.rb (add root_ancestor_ids to es_search_options)

Testing needed:

  • Unit tests: Verify es_search_options includes root_ancestor_ids
  • Integration tests: Verify routing string is included in actual ES queries
  • Performance tests: Compare query times with/without routing

References:

  • Routing module: ee/lib/elastic/latest/routing.rb
  • Client integration: ee/lib/gitlab/search/client.rb (lines 40-48)
  • Working example: ee/app/finders/security/vulnerability_elastic_base_finder.rb (lines 134-145)