Fix silent data loss in Advanced Search routing for >128 root_ancestor_ids
What does this MR do and why?
When searching with more than 128 root ancestor IDs, the routing logic in ee/lib/elastic/latest/routing.rb emits { routing: [] } which serializes to routing= (empty string). Elasticsearch interprets this as 'search zero shards', returning 0 hits with HTTP 200 — silent data loss that looks like a successful empty result.
Root cause: The root_ancestor_ids branch lacked the blank guard that the project_ids branch already has. When build_routing returns [] for >128 IDs, it was returned directly as { routing: [] } instead of falling back to an unrouted search.
Fix: Add return {} if routing.blank? to the root_ancestor_ids branch, matching the existing pattern in the project_ids branch. When root ancestor count exceeds 128, the search now degrades gracefully to an unrouted (all-shard) search returning all results.
References
Screenshots or screen recordings
N/A — backend-only change, no UI impact.
| Before | After |
|---|---|
routing_options({ root_ancestor_ids: (1..129).to_a }) returns { routing: [] } → ES searches zero shards → 0 hits |
routing_options({ root_ancestor_ids: (1..129).to_a }) returns {} → ES searches all shards → correct results |
How to set up and validate locally
- Open a Rails console
- Run:
proxy = Elastic::Latest::ApplicationClassProxy.new(Issue) # Before fix: returns { routing: [] } — broken # After fix: returns {} — correct fallback to all-shard search proxy.routing_options({ root_ancestor_ids: (1..129).to_a }) - Verify it returns
{}(empty hash, no routing key) - Verify ≤128 IDs still return the correct routing string:
proxy.routing_options({ root_ancestor_ids: [1, 2, 3] }) # => { routing: 'group_1,group_2,group_3' }
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.