Add N+1 query shared example for MCP tool specs
What does this MR do and why?
Adds an 'an MCP tool that avoids N+1 queries' shared example for MCP tool service specs, and adopts it in list_projects, search_labels and list_merge_requests. A tool that returns a list can start running queries for each record without any spec noticing. The example runs the tool, adds records, and requires the query count to grow by exactly threshold: (default 0).
All three tools already run policy queries for each record on master, and the count differs by edition. Each CE spec runs the example only in FOSS, and a new spec under ee/spec covers EE. Each example has a threshold equal to the measured increase and a # TODO: link to the tracking issue. Because the match is exact, the fix MR fails until it lowers or removes the threshold. Once both editions reach zero, the EE example goes away and the CE gate is removed. The GraphQL integration dev doc and the gitlab-mcp-tool-builder skill now tell tool authors to use the example.
Design notes and measurements
- Each call runs with a fresh request store, Rails cache and BatchLoader executor, as
GraphqlHelpers#run_with_clean_statedoes.doc/development/database/query_recorder.mdrecommends the:request_storeand:use_sql_query_cachetags. In a service spec those share one request store across the warm-up, control and measured calls, and the memoized state from the warm-up hid queries in the control. The example raises when the spec is tagged:request_store. - A warm-up call runs before the control so one-time caches don't inflate the control count.
match_query_count(control).with_threshold(n)passes only when the measured call runs exactly control +nqueries, counting cached queries.exceed_query_limitandissue_same_number_of_queries_asalso pass after a fix, which would leave a stale threshold.- Both calls must return
isError: false, andexpect(result).not_to eq(control_result)fails the example if the added records never reach the response. - CE specs run in both editions, so the CE example sits in
context 'when CE edition', unless: Gitlab.ee?. as-if-foss jobs don't loadee/spec.
| Tool | EE increase (2 records) | FOSS increase | Cause | Issue |
|---|---|---|---|---|
list_projects |
+4 | 0 | needs_new_sso_session loads each root ancestor and its SAML provider |
#502245 |
list_merge_requests (group) |
+8 | +2 | Policy conditions while authorizing each merge request's project. In CE, project_features. |
#635534 |
search_labels (group) |
+4 | 0 | GroupPolicy conditions read each label group's root ancestor |
#635535 (closed) |
The two new issues each carry a fix that was tested locally. With it applied, the matching EE example passes without threshold:.
References
Follow-up: #635891 adds the example to the other MCP tools that return more than one record.
Screenshots or screen recordings
No UI changes.
How to set up and validate locally
This MR changes only specs, a dev doc and a skill, so the specs are the validation.
-
Run the CE and EE specs in EE. All pass, and only the three
ee/specN+1 examples run:bundle exec rspec spec/services/mcp/tools/projects/list_projects_service_spec.rb spec/services/mcp/tools/labels/search_service_spec.rb spec/services/mcp/tools/merge_requests/list_merge_requests_service_spec.rb ee/spec/services/ee/mcp/tools/projects/list_projects_service_spec.rb ee/spec/services/ee/mcp/tools/labels/search_service_spec.rb ee/spec/services/ee/mcp/tools/merge_requests/list_merge_requests_service_spec.rb -
Run the three CE specs with
FOSS_ONLY=1. All pass, and the CE N+1 examples run with thresholds 0, 0 and 2. -
In
ee/spec/services/ee/mcp/tools/merge_requests/list_merge_requests_service_spec.rb, changethreshold: 8tothreshold: 10and rerun that file. It fails withExpected exactly 15 (+10) queries, got 23. -
Restore
threshold: 8, apply the diff under "Possible fixes" in #635534, and rerun. It fails withExpected exactly 20 (+8) queries, got 20, so the fix MR has to drop the threshold. -
Remove
threshold: 8and rerun. It passes.
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.