{"data":{"id":"magento2-40867","source":"github-pr","sourceRef":"magento/magento2#40867","title":"BundleGraphQL Resolvers Create Separate Deferred Product Instances, Defeating Query Batching (N+1)","pr":{"number":40867,"url":"https://github.com/magento/magento2/pull/40867","author":"aasim110","mergedAt":"2026-08-31T09:21:05Z","mergeCommit":"7e8e77b8f3ebca43a44b535d738ea41300c85060","headCommit":"27a62abf579d4e88f63738d0672859b586f80600","baseRef":"2.4-develop","diffSha256":"c22ece7db6adb16b032a34f47488043f492fe5f274283f381256dcc4502956e8"},"issues":[{"number":40595,"url":"https://github.com/magento/magento2/issues/40595","title":"BundleGraphQL Resolvers Create Separate Deferred Product Instances, Defeating Query Batching (N+1)","labels":["Area: Product","Component: GraphQL","Issue: Confirmed","Priority: P2","Progress: PR Created","Progress: done","Reproduced on 2.4.x"],"kind":"human"}],"fixedIn":null,"containingTags":[],"reportedOn":"2.4.7-p9","codeMatch":{"2.4.6":"conflict","2.4.6-p1":"conflict","2.4.6-p2":"conflict","2.4.6-p3":"conflict","2.4.6-p4":"conflict","2.4.6-p5":"conflict","2.4.6-p6":"conflict","2.4.6-p7":"conflict","2.4.6-p8":"conflict","2.4.6-p9":"conflict","2.4.6-p10":"conflict","2.4.6-p11":"conflict","2.4.6-p12":"conflict","2.4.6-p13":"conflict","2.4.6-p14":"conflict","2.4.6-p15":"conflict","2.4.7":"conflict","2.4.7-p1":"conflict","2.4.7-p2":"conflict","2.4.7-p3":"conflict","2.4.7-p4":"conflict","2.4.7-p5":"conflict","2.4.7-p6":"conflict","2.4.7-p7":"conflict","2.4.7-p8":"conflict","2.4.7-p9":"conflict","2.4.7-p10":"conflict","2.4.8":"clean","2.4.8-p1":"clean","2.4.8-p2":"clean","2.4.8-p3":"clean","2.4.8-p4":"clean","2.4.8-p5":"clean","2.4.9":"clean"},"affectedVersions":["2.4.8","2.4.8-p1","2.4.8-p2","2.4.8-p3","2.4.8-p4","2.4.8-p5","2.4.9"],"components":["magento/module-bundle-graph-ql","magento/module-catalog-graph-ql"],"files":[{"path":"app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php","change":"modified","package":"magento/module-bundle-graph-ql"},{"path":"app/code/Magento/BundleGraphQl/Model/Resolver/PriceRange.php","change":"modified","package":"magento/module-bundle-graph-ql"},{"path":"app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php","change":"modified","package":"magento/module-catalog-graph-ql"}],"stripped":{"tests":["app/code/Magento/BundleGraphQl/Test/Unit/Model/Resolver/Options/LabelTest.php","app/code/Magento/BundleGraphQl/Test/Unit/Model/Resolver/PriceRangeTest.php","app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/ProductTest.php"],"docs":[],"outsideCode":[]},"linesChanged":71,"mergeBatched":false,"excluded":null,"sections":{"description":"Each bundle child product triggers its own isolated `COUNT(DISTINCT e.entity_id)` + full product `SELECT` query pair. On a page with 20 bundle products averaging 6 selections each, this produces **~120 extra COUNT queries** and **~120 extra SELECT queries** — 240 unnecessary queries per request.","stepsToReproduce":"1. Enable database query logging: `bin/magento dev:query-log:enable`\n2. Send a GraphQL `products` query for a category containing Bundle Products (e.g. requesting `items { ... magentoBundledProduct }` with child product fields like `label`)\n3. Inspect `var/debug/db.log`","expectedResult":"`Deferred\\Product` batches all child product SKUs and loads them in a single query.","actualResult":"Each bundle child product triggers its own isolated `COUNT(DISTINCT e.entity_id)` + full product `SELECT` query pair. On a page with 20 bundle products averaging 6 selections each, this produces **~120 extra COUNT queries** and **~120 extra SELECT queries** — 240 unnecessary queries per request.","source":"issue"},"signatures":[],"labels":{"area":["Product"],"component":["GraphQL"],"priority":"P2","severity":null,"reportedOn":[]},"categories":["Catalog/Product","GraphQL"],"triage":{"model":"@cf/cloudflare/clef","requestHash":"926f41eb763b83fd895e30e29abd8cdbb69c77b5c2bf2aab9691e26c8c6d9a20","isBugfix":0.9672,"changeKind":{"choice":"bugfix","probabilities":{"bugfix":0.8842,"feature":0.0114,"refactor":0.0716,"tests_only":0.0167,"docs_only":0.0061,"dependency":0.01},"confidence":0.7449},"scope":{"score":1.0519,"probabilities":{"0":0.2396,"1":0.4688,"2":0.2916},"confidence":0.0433},"risk":{"score":0.712,"probabilities":{"0":0.4284,"1":0.4312,"2":0.1404},"confidence":0.0838},"area":{"choice":"graphql_api","probabilities":{"catalog":0.0991,"checkout":0.0056,"customer":0.0036,"admin":0.0056,"graphql_api":0.8577,"framework":0.0157,"frontend":0.0066,"other":0.0061},"confidence":0.7096},"securityRelevant":0.007,"reportedVersion":{"choice":"2.4.7-p9","probabilities":{"2.4.0":0.002,"2.4.0-p1":0.0022,"2.4.1":0.002,"2.4.1-p1":0.0022,"2.4.2":0.002,"2.4.2-p1":0.0022,"2.4.2-p2":0.0021,"2.4.3":0.0021,"2.4.3-p1":0.0023,"2.4.3-p2":0.0024,"2.4.3-p3":0.0023,"2.4.4":0.0023,"2.4.4-p1":0.0024,"2.4.4-p10":0.0033,"2.4.4-p11":0.004,"2.4.4-p12":0.0031,"2.4.4-p13":0.0032,"2.4.4-p14":0.0032,"2.4.4-p15":0.0029,"2.4.4-p16":0.003,"2.4.4-p17":0.004,"2.4.4-p18":0.0028,"2.4.4-p2":0.002,"2.4.4-p3":0.0021,"2.4.4-p4":0.0023,"2.4.4-p5":0.0024,"2.4.4-p6":0.0028,"2.4.4-p7":0.0034,"2.4.4-p8":0.0036,"2.4.4-p9":0.0048,"2.4.5":0.0023,"2.4.5-p1":0.0022,"2.4.5-p10":0.0026,"2.4.5-p11":0.0027,"2.4.5-p12":0.0036,"2.4.5-p13":0.0031,"2.4.5-p14":0.0029,"2.4.5-p15":0.0033,"2.4.5-p16":0.0027,"2.4.5-p17":0.0029,"2.4.5-p2":0.0021,"2.4.5-p3":0.0022,"2.4.5-p4":0.0024,"2.4.5-p5":0.0024,"2.4.5-p6":0.003,"2.4.5-p7":0.0031,"2.4.5-p8":0.0028,"2.4.5-p9":0.0025,"2.4.6":0.0044,"2.4.6-p1":0.0028,"2.4.6-p10":0.0051,"2.4.6-p11":0.0043,"2.4.6-p12":0.0049,"2.4.6-p13":0.0046,"2.4.6-p14":0.0043,"2.4.6-p15":0.0039,"2.4.6-p2":0.0023,"2.4.6-p3":0.0026,"2.4.6-p4":0.0033,"2.4.6-p5":0.003,"2.4.6-p6":0.004,"2.4.6-p7":0.0042,"2.4.6-p8":0.0042,"2.4.6-p9":0.0033,"2.4.7":0.1109,"2.4.7-p1":0.0029,"2.4.7-p10":0.0063,"2.4.7-p2":0.0024,"2.4.7-p3":0.0038,"2.4.7-p4":0.0043,"2.4.7-p5":0.0038,"2.4.7-p6":0.0063,"2.4.7-p7":0.0098,"2.4.7-p8":0.017,"2.4.7-p9":0.6011,"2.4.8":0.0056,"2.4.8-p1":0.0054,"2.4.8-p2":0.0051,"2.4.8-p3":0.0062,"2.4.8-p4":0.0056,"2.4.8-p5":0.0048,"2.4.9":0.0044,"unspecified":0.0029},"confidence":0.3673},"backportWorthy":{"score":1.5363,"probabilities":{"0":0.1084,"1":0.2469,"2":0.6447},"confidence":0.2326}},"curated":{"title":"Fixes N+1 product queries for bundle options in GraphQL products queries","description":"In GraphQL products queries for bundle products, each child product ran its own COUNT and SELECT query, as the Label and Product resolvers created a separate Deferred product instance per resolve call and PriceRange fetched its product before other SKUs were queued. Label and Product now use the injected ProductDataProvider singleton, and PriceRange runs its fetch inside a deferred closure.","categories":["GraphQL","Catalog/Product"],"author":"claude-code/opus-5.5","date":"2026-10-06","reviewedBy":null},"tests":{"2.4.8-p5":{"before":"not-runnable","after":"error","adapted":false,"runAt":"2026-10-06T09:20:29.949Z","releaseCommit":"870a22c63d9d9b68fa3297962e2d5d5841115814","suites":{"unit":{"before":"not-runnable","after":"error","runAt":"2026-10-06T09:20:29.949Z"}}},"2.4.9":{"before":"not-runnable","after":"pass","adapted":false,"runAt":"2026-10-06T09:20:30.355Z","releaseCommit":"755e34dd689021c5165db9d35ecff74f7dc51527","suites":{"unit":{"before":"not-runnable","after":"pass","runAt":"2026-10-06T09:20:30.355Z"}}}}},"_documentation":"https://magento.watch/api","_description":"Upstream fix magento2-40867 details"}