codeconsole opened a new pull request, #16149:
URL: https://github.com/apache/grails-core/pull/16149

   Reduces per-request work across the request path, and adds an opt-in JMH 
module so the
   changes are measured rather than asserted.
   
   ## Measured
   
   Before/after on the same machine and JDK (21.0.7, Apple M4 Max), 2 forks x 
(5 warmup +
   5 measurement):
   
   | Benchmark | before ns/op | after ns/op | |
   |---|---:|---:|---|
   | `ControllerActionBenchmark.plainAction` | 34.8 | **3.6** | −89.7% |
   | `InterceptorChainBenchmark.oneInterceptorNoOpRegistry` | 295.3 | **127.5** 
| −56.8% |
   | `InterceptorChainBenchmark.threeInterceptorsNoOpRegistry` | 1116.9 | 
**545.6** | −51.2% |
   | `GrailsWebRequestBenchmark.construct` | 16.3 | **11.7** | −28.1% |
   | `UrlMappingBenchmark.matchRestfulUriCacheMiss` | 1550.0 | **1287.8** | 
−16.9% |
   | `UrlMappingBenchmark.matchDefaultMappingUriCacheMiss` | 1881.3 | 
**1595.4** | −15.2% |
   
   Nothing regressed. Run them with:
   
   ```
   ./gradlew :grails-web-benchmarks:jmh
   ```
   
   ## What changed
   
   - **Multipart**: `request` is no longer replaced by the resolved 
`MultipartHttpServletRequest`.
     It is discovered from the wrapper chain instead, so the security and 
method-override
     wrappers survive. `request.getFile(..)`, `params.myFile` and `byte[]` 
binding are unchanged.
   - **Application attributes** were built per request through a reflective 
constructor, discarding
     the five beans they cache. Now resolved once per servlet context.
   - **Handler chain** assembly delegates to `AbstractHandlerMapping` instead 
of duplicating it, so
     Grails-mapped requests no longer miss what Spring adds there (currently 
the API version
     deprecation interceptor). Two per-request interceptor allocations removed.
   - **Interceptors** no longer allocate a JDK dynamic proxy per phase: the `as 
BooleanSupplier`
     coercion ran even when the ObservationRegistry is a no-op.
   - **Generated actions** no longer carry allowed-methods bookkeeping when the 
controller declares
     no `allowedMethods`. A plain action went from 8 request-attribute 
operations to none. It was
     also being emitted twice into the same method.
   - **Data binding** resolves its collaborators once per `ApplicationContext`, 
and caches the
     negative result of the whitelist field lookup, which previously rebuilt a 
`NoSuchFieldException`
     on every bind for untransformed classes.
   - **Redirect / render** cache the controller namespace, the redirector and 
the view resolver
     instead of resolving them per call.
   - **URL matching** skips candidate patterns whose segment count rules them 
out, before any regex
     runs. Candidates are skipped, never reordered, so precedence is unchanged.
   - Deletes unreachable multipart resolution in `DefaultUrlMappingInfo`, and 
repairs a
     `SpringSecurityUtils` branch that read an attribute only that dead code 
ever wrote.
   
   ## Behaviour changes
   
   Documented in the 8.0 upgrade guide, sections 45 and 46:
   
   - `request instanceof MultipartHttpServletRequest` and casts to it no longer 
work. The file
     methods are unaffected.
   - API versioning headers (`spring.mvc.apiversion.*`) are now emitted for 
Grails-mapped requests.
   - The `LocaleContext` is restored rather than cleared at the end of a 
request.
   - `GrailsWebRequest.getCurrentRequest()` is deprecated in favour of 
`getRequest()`. Tests that
     stubbed it need updating; `getRequest()` is `final` on Spring's 
`ServletRequestAttributes`, so
     that was the only stubbable accessor.
   
   ## Limitations
   
   - Only the benchmarks above are measured. The interceptor, codegen and data 
binding changes are
     additionally evidenced by bytecode (proxy allocations gone, attribute 
operations gone), but the
     redirect/render caches have no number.
   - This does **not** address the oversized-upload half of #16145 — that needs 
early multipart
     resolution, which #16146 provides. The two overlap; see the discussion 
there.
   - `collectControllerMappings` remains the largest cost on the path 
(~360-1200 ns per request,
     against 2.5 ns for a cached URL match). Addressed separately.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to