fix(cost): actual cost inflation when sizedFields parent is a non-list wrapper field - #1492
fix(cost): actual cost inflation when sizedFields parent is a non-list wrapper field#1492budziam wants to merge 2 commits into
Conversation
…parent When a non-list wrapper field (e.g. items_page: ItemsPage!) is used as a sizedFields parent via @listsize, its occurrences are not recorded in actualListSizes because it does not return a list type. This causes the child list multiplier to use total items (2) as its denominator instead of the number of parent occurrences (4), inflating the combined actual cost above the sum of individually queried costs. The test asserts the expected contract — combined cost must not exceed the sum of separate costs — and will pass once the fix is in place.
When computing the actual cost multiplier for a list field, the averaging logic looked only at the immediate parent path to find the parent occurrence count. If the parent is a non-list wrapper field, it is never recorded in actualListSizes because it does not return a list type. This caused parentCount to default to 1, inflating the multiplier by the total item count instead of the per-parent average. Fix by climbing the CostTreeNode parent chain to find the nearest ancestor where returnsListType is true, then look up its occurrence count. This uses the type information already on each node rather than inferring it from map absence via string path manipulation.
📝 WalkthroughWalkthroughThis PR refactors how parent list sizes are tracked during nested cost calculations. Instead of inferring parent list size by truncating ChangesParent List Tracking Logic
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 I have created a release *beep* *boop* --- ## [2.3.0](v2.2.0...v2.3.0) (2026-05-15) ### Features * support dot-path in slicingArguments ([#1485](#1485)) ([2cb8d5e](2cb8d5e)) ### Bug Fixes * find proper parent when sizedFields parent is a non-list wrapper ([#1493](#1493)) ([6b96976](6b96976)), closes [#1492](#1492) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [1.15.0](execution/v1.14.0...execution/v1.15.0) (2026-05-15) ### Features * support dot-path in slicingArguments ([#1485](#1485)) ([2cb8d5e](2cb8d5e)) ### Bug Fixes * find proper parent when sizedFields parent is a non-list wrapper ([#1493](#1493)) ([6b96976](6b96976)), closes [#1492](#1492) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Problem
When a non-list wrapper field (e.g.
items_page: ItemsPage!) is annotatedwith
@listSize(slicingArguments: ["limit"], sizedFields: ["items"]), its occurrences are never recordedin
actualListSizesbecause the resolver only tracks array fields there.This caused the averaging denominator for the child list multiplier to default
to
1(total items across all parents) instead of the number of parent fieldoccurrences, inflating the combined actual cost above what you'd get by summing
the same entities queried individually.
Example:
{ boards(limit: 4) { items_page(limit: 1) { items { id } } } }With 4
boardsand 2 totalitems, the multiplier was computed as2/1 = 2instead of the correct
2/4 = 0.5.Fix
Instead of looking up the immediate parent by chopping the JSON path string,
climb the
CostTreeNodeparent chain to find the nearest ancestor wherereturnsListType == true, then use itsactualListSizescount as thedenominator. This uses the type information already on each node rather than
inferring list-ness from map absence.