From 0f02917bbd3659dcf37e7229282eb507a35f229f Mon Sep 17 00:00:00 2001 From: Eric Proulx Date: Thu, 10 Sep 2026 21:09:55 +0200 Subject: [PATCH] Skip the qualifying-params lookup for scopes other than `given` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A nested scope resolves its params through `@parent.qualifying_params.presence`, which reads the request's tracker out of fiber storage and looks the parent up in its identity Hash. That runs twice per validator on a nested scope, once in `should_validate?` and once in the attributes iterator, on every request. Only `#meets_dependency?` ever stores qualifying params, and it returns before doing so unless the scope has a dependency, i.e. is a `given`. Every other scope therefore always answered empty, and now does so without the lookups: about 190 ns per resolution, and a nested validator went from 1.77 to 1.39 µs. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 1 + lib/grape/validations/params_scope.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdc7d3a3e..b4e6b62ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#2918](https://github.com/ruby-grape/grape/pull/2918): Skip the dry-types round trip when a value already is the declared type - [@ericproulx](https://github.com/ericproulx). * [#2917](https://github.com/ruby-grape/grape/pull/2917): Read path captures out of the router's union match instead of re-running the route's pattern - [@ericproulx](https://github.com/ericproulx). * [#2921](https://github.com/ruby-grape/grape/pull/2921): Pin the router's request-time isolation regressions through requests instead of its instance variables - [@ericproulx](https://github.com/ericproulx). +* [#2925](https://github.com/ruby-grape/grape/pull/2925): Skip the qualifying-params lookup for params scopes other than `given` - [@ericproulx](https://github.com/ericproulx). * Your contribution here. ### 4.0.0 (2026-09-07) diff --git a/lib/grape/validations/params_scope.rb b/lib/grape/validations/params_scope.rb index be26ab992..099ede647 100644 --- a/lib/grape/validations/params_scope.rb +++ b/lib/grape/validations/params_scope.rb @@ -5,7 +5,14 @@ module Validations class ParamsScope attr_reader :parent, :type, :nearest_array_ancestor, :array_depth, :full_path + # The elements a +given+ scope narrowed its Array params down to during + # this request (see #meets_dependency?). Only a scope with a dependency + # ever stores any, so every other scope answers without the fiber-storage + # and tracker lookups -- which #params pays on each nested resolution, + # twice per validator, on every request. def qualifying_params + return unless @dependent_on + ParamScopeTracker.current&.qualifying_params(self) end