You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@grails.apache.org by "jamesfredley (via GitHub)" <gi...@apache.org> on 2026/07/05 02:28:07 UTC

[PR] Improve hot path virtual-thread and codec performance [grails-core]

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

   ## Summary
   
   - Replace `GrailsHttpSession` wrapper monitor synchronization with a private `ReentrantLock` so lazy session access no longer pins virtual-thread carrier threads on `synchronized (this)`.
   - Static-compile `grails-codecs-core` codec extension helpers and replace dynamic self-dispatch with direct helper calls.
   - Preserve existing public Groovy extension method descriptors by keeping `Object` return types and the existing `HEXDIGITS` property shape.
   
   ## Why
   
   Grails 8 runs on modern JDKs where virtual-thread friendliness matters in hot request paths. `GrailsHttpSession` serialized wrapper access through an object monitor, which can pin carrier threads when code blocks inside synchronized regions. A private `ReentrantLock` keeps the wrapper's serialization behavior without monitor pinning and also protects lazy session creation consistently.
   
   The codecs are small but widely used hot-path extension methods. Static compilation removes dynamic dispatch overhead inside the codec implementations while preserving the public dynamic extension surface that existing compiled callers expect.
   
   ## Verification
   
   - `./gradlew :grails-codecs-core:test :grails-web-common:test --tests "grails.web.servlet.mvc.GrailsHttpSessionSpec" :grails-codecs-core:codeStyle :grails-web-common:codeStyle --rerun-tasks`
   - `./gradlew clean aggregateViolations :grails-test-report:check --continue`
     - Violation reports were clean: Checkstyle, CodeNarc, PMD, SpotBugs.
     - The full gate still hits pre-existing unrelated failures on clean `origin/8.0.x`: `grails-validation:compileGroovy`, `grails-data-graphql-core:compileGroovy`, and MongoDB/Testcontainers environment failures. The two compile failures reproduce in a separate clean `origin/8.0.x` worktree.
   
   ## Review
   
   - Review gate passed for diff hash `c44c66150eb78763308fec125cd0b86260881a48` with both Oracle and Codex reviewers green.
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "codecov[bot] (via GitHub)" <gi...@apache.org>.
codecov[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4906214316

   ## [Codecov](https://app.codecov.io/gh/apache/grails-core/pull/15828?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   :x: Patch coverage is `62.00000%` with `38 lines` in your changes missing coverage. Please review.
   :white_check_mark: Project coverage is 49.3765%. Comparing base ([`2650b8c`](https://app.codecov.io/gh/apache/grails-core/commit/2650b8c9dfe6afe16345c03a072e25a236a295a5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`0aa3569`](https://app.codecov.io/gh/apache/grails-core/commit/0aa35690ee9447533b96cd588404d39c13787131?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
   :warning: Report is 21 commits behind head on 8.0.x.
   
   | [Files with missing lines](https://app.codecov.io/gh/apache/grails-core/pull/15828?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
   |---|---|---|
   | [...oovy/grails/web/servlet/mvc/GrailsHttpSession.java](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-web-common%2Fsrc%2Fmain%2Fgroovy%2Fgrails%2Fweb%2Fservlet%2Fmvc%2FGrailsHttpSession.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLXdlYi1jb21tb24vc3JjL21haW4vZ3Jvb3Z5L2dyYWlscy93ZWIvc2VydmxldC9tdmMvR3JhaWxzSHR0cFNlc3Npb24uamF2YQ==) | 58.8235% | [21 Missing :warning: ](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   | [...ils/plugins/codecs/HexCodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FHexCodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0hleENvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | 37.5000% | [8 Missing and 2 partials :warning: ](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   | [...roovy/org/grails/plugins/codecs/DigestUtils.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FDigestUtils.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0RpZ2VzdFV0aWxzLmdyb292eQ==) | 76.9231% | [5 Missing and 1 partial :warning: ](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   | [.../plugins/codecs/Base64CodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FBase64CodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0Jhc2U2NENvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | 66.6667% | [1 Missing :warning: ](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   
   <details><summary>Additional details and impacted files</summary>
   
   
   
   [![Impacted file tree graph](https://app.codecov.io/gh/apache/grails-core/pull/15828/graphs/tree.svg?width=650&height=150&src=pr&token=qJrryhpk7u&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   
   ```diff
   @@                Coverage Diff                 @@
   ##                8.0.x     #15828        +/-   ##
   ==================================================
   + Coverage     49.2998%   49.3765%   +0.0767%     
   - Complexity      16727      16794        +67     
   ==================================================
     Files            1985       1985                
     Lines           93252      93342        +90     
     Branches        16316      16341        +25     
   ==================================================
   + Hits            45973      46089       +116     
   + Misses          40166      40130        -36     
   - Partials         7113       7123        +10     
   ```
   
   | [Files with missing lines](https://app.codecov.io/gh/apache/grails-core/pull/15828?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [...lugins/codecs/MD5BytesCodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FMD5BytesCodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL01ENUJ5dGVzQ29kZWNFeHRlbnNpb25NZXRob2RzLmdyb292eQ==) | `75.0000% <ø> (ø)` | |
   | [...ils/plugins/codecs/MD5CodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FMD5CodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL01ENUNvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | `100.0000% <100.0000%> (ø)` | |
   | [...ugins/codecs/SHA1BytesCodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FSHA1BytesCodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL1NIQTFCeXRlc0NvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | `75.0000% <ø> (ø)` | |
   | [...ls/plugins/codecs/SHA1CodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FSHA1CodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL1NIQTFDb2RlY0V4dGVuc2lvbk1ldGhvZHMuZ3Jvb3Z5) | `75.0000% <100.0000%> (ø)` | |
   | [...ins/codecs/SHA256BytesCodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FSHA256BytesCodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL1NIQTI1NkJ5dGVzQ29kZWNFeHRlbnNpb25NZXRob2RzLmdyb292eQ==) | `75.0000% <ø> (ø)` | |
   | [.../plugins/codecs/SHA256CodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FSHA256CodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL1NIQTI1NkNvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | `75.0000% <100.0000%> (+25.0000%)` | :arrow_up: |
   | [.../plugins/codecs/Base64CodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FBase64CodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0Jhc2U2NENvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | `60.0000% <66.6667%> (ø)` | |
   | [...roovy/org/grails/plugins/codecs/DigestUtils.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FDigestUtils.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0RpZ2VzdFV0aWxzLmdyb292eQ==) | `71.4286% <76.9231%> (+13.0952%)` | :arrow_up: |
   | [...ils/plugins/codecs/HexCodecExtensionMethods.groovy](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-codecs-core%2Fsrc%2Fmain%2Fgroovy%2Forg%2Fgrails%2Fplugins%2Fcodecs%2FHexCodecExtensionMethods.groovy&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLWNvZGVjcy1jb3JlL3NyYy9tYWluL2dyb292eS9vcmcvZ3JhaWxzL3BsdWdpbnMvY29kZWNzL0hleENvZGVjRXh0ZW5zaW9uTWV0aG9kcy5ncm9vdnk=) | `38.0952% <37.5000%> (+3.3126%)` | :arrow_up: |
   | [...oovy/grails/web/servlet/mvc/GrailsHttpSession.java](https://app.codecov.io/gh/apache/grails-core/pull/15828?src=pr&el=tree&filepath=grails-web-common%2Fsrc%2Fmain%2Fgroovy%2Fgrails%2Fweb%2Fservlet%2Fmvc%2FGrailsHttpSession.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-Z3JhaWxzLXdlYi1jb21tb24vc3JjL21haW4vZ3Jvb3Z5L2dyYWlscy93ZWIvc2VydmxldC9tdmMvR3JhaWxzSHR0cFNlc3Npb24uamF2YQ==) | `64.0625% <58.8235%> (+32.8125%)` | :arrow_up: |
   
   ... and [18 files with indirect coverage changes](https://app.codecov.io/gh/apache/grails-core/pull/15828/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   </details>
   <details><summary> :rocket: New features to boost your workflow: </summary>
   
   - :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
   - :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
   </details>


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "bito-code-review[bot] (via GitHub)" <gi...@apache.org>.
bito-code-review[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4884578154

   <!-- Bito Reply -->
   The current implementation of `decodeHex` uses a restrictive guard (`theTarget == null || theTarget instanceof NullObject || theTarget.toString().length() == 0`) which deviates from the previous Groovy-truthiness check (`if (!theTarget) return null`). This change can cause exceptions or unexpected behavior for inputs that were previously considered falsy in Groovy, such as `0` or empty collections. To restore the original contract and ensure compatibility with Groovy-truthiness, the guard should be updated to use the `!theTarget` check.
   
   **grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/HexCodecExtensionMethods.groovy**
   ```
   static Object decodeHex(Object theTarget) {
           if (!theTarget) return null
   ```


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#discussion_r3538024503


##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/Base64CodecExtensionMethods.groovy:
##########
@@ -24,32 +24,35 @@ import org.codehaus.groovy.runtime.NullObject
 
 import org.apache.commons.codec.binary.Base64
 
+import groovy.transform.CompileStatic
+
 /**
  * A codec that encodes and decodes Objects using Base64 encoding.
  *
  * @author Drew Varner
  */
+@CompileStatic
 class Base64CodecExtensionMethods {
 
-    static encodeAsBase64(theTarget) {
+    static Object encodeAsBase64(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
         if (theTarget instanceof Byte[] || theTarget instanceof byte[]) {
-            return new String(Base64.encodeBase64(theTarget))
+            return new String(Base64.encodeBase64(DigestUtils.toByteArray(theTarget)), StandardCharsets.UTF_8)

Review Comment:
   UTF_8 should be an added method argument that is defaulted to this value.



-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty merged PR #15828:
URL: https://github.com/apache/grails-core/pull/15828


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#discussion_r3538013254


##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/DigestUtils.groovy:
##########
@@ -18,30 +18,69 @@
  */
 package org.grails.plugins.codecs
 
+import java.lang.reflect.Array
 import java.nio.charset.StandardCharsets
 import java.security.MessageDigest
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 abstract class DigestUtils {
 
     // Digest byte[], any list/array or string into a byte[]
-    static digest(String algorithm, data) {
+    static Object digest(String algorithm, Object data) {
         if (data == null) {
             return null
         }
 
-        def md = MessageDigest.getInstance(algorithm)
-        def src
-        if (data instanceof Byte[] || data instanceof byte[]) {
-            src = data
+        MessageDigest md = MessageDigest.getInstance(algorithm)
+        byte[] src = toByteArray(data)
+        md.update(src) // This probably needs to use the thread's Locale encoding
+        return md.digest()
+    }
+
+    protected static byte[] toByteArray(Object data) {
+        if (data instanceof byte[]) {
+            return (byte[]) data
         }
-        else if (data instanceof List || data.getClass().isArray()) {
-            src = new byte[data.size()]
-            data.eachWithIndex { v, i -> src[i] = v }
+        if (data instanceof Byte[]) {
+            return toByteArrayFromWrapper((Byte[]) data)
         }
-        else {
-            src = data.toString().getBytes(StandardCharsets.UTF_8)
+        if (data instanceof List) {
+            return toByteArrayFromList((List<?>) data)
         }
-        md.update(src) // This probably needs to use the thread's Locale encoding
-        return md.digest()
+        if (data.getClass().isArray()) {
+            return toByteArrayFromArray(data, Array.getLength(data))
+        }
+
+        return data.toString().getBytes(StandardCharsets.UTF_8)
+    }
+
+    private static byte[] toByteArrayFromWrapper(Byte[] data) {
+        byte[] result = new byte[data.length]
+        for (int i = 0; i < data.length; i++) {
+            result[i] = data[i].byteValue()
+        }
+        return result
+    }
+
+    private static byte[] toByteArrayFromList(List<?> data) {
+        byte[] result = new byte[data.size()]
+        for (int i = 0; i < data.size(); i++) {
+            result[i] = toByte(data.get(i))
+        }
+        return result
+    }
+
+    private static byte[] toByteArrayFromArray(Object data, int length) {
+        byte[] result = new byte[length]
+        for (int i = 0; i < length; i++) {
+            result[i] = toByte(Array.get(data, i))
+        }
+        return result
+    }
+
+    private static byte toByte(Object value) {
+        return ((Number) value).byteValue()

Review Comment:
   This narrows the accepted element types compared to the old dynamic code. Previously `src[i] = v` went through Groovy's `DefaultTypeTransformation` coercion, which also accepted `Character` elements (and `char[]` arrays via the array path); now any non-`Number` element throws `ClassCastException`. If preserving the old tolerance matters, `DefaultTypeTransformation.castToNumber(value).byteValue()` keeps the coercion while staying statically compiled. If the narrowing is intentional, it's probably worth a line in the PR compatibility notes.



##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/DigestUtils.groovy:
##########
@@ -18,30 +18,69 @@
  */
 package org.grails.plugins.codecs
 
+import java.lang.reflect.Array
 import java.nio.charset.StandardCharsets
 import java.security.MessageDigest
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 abstract class DigestUtils {
 
     // Digest byte[], any list/array or string into a byte[]
-    static digest(String algorithm, data) {
+    static Object digest(String algorithm, Object data) {
         if (data == null) {
             return null
         }
 
-        def md = MessageDigest.getInstance(algorithm)
-        def src
-        if (data instanceof Byte[] || data instanceof byte[]) {
-            src = data
+        MessageDigest md = MessageDigest.getInstance(algorithm)
+        byte[] src = toByteArray(data)
+        md.update(src) // This probably needs to use the thread's Locale encoding
+        return md.digest()
+    }
+
+    protected static byte[] toByteArray(Object data) {

Review Comment:
   Nit: `protected` reads as "for subclasses", but the actual consumers (`Base64CodecExtensionMethods`, `HexCodecExtensionMethods`) rely on `protected`'s package-access side effect. `@PackageScope` (or a brief comment) would make the intent clearer.



##########
grails-codecs-core/src/test/groovy/org/grails/web/codecs/HexCodecTests.groovy:
##########
@@ -48,6 +49,13 @@ class HexCodecTests {
         assertIterableEquals(new Byte[] {65, 32, 66, 32, 67, 32, 68, 32, 69}.toList(), result.toList())
         //make sure decoding null returns null
         assertEquals(null.decodeHex(), null)
+
+        //make sure decoding Groovy-falsy values returns null
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(''))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(0))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(false))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex([]))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(new byte[0]))

Review Comment:
   Per the repo rule to test via public APIs, these could use the extension-method form users actually call — `''.decodeHex()`, `0.decodeHex()`, `false.decodeHex()`, `[].decodeHex()`, `new byte[0].decodeHex()` — which also exercises the extension-module registration path rather than the static class directly (matching the existing `null.decodeHex()` assertion above).



##########
grails-codecs-core/src/test/groovy/org/grails/web/codecs/HexCodecTests.groovy:
##########
@@ -48,6 +49,13 @@ class HexCodecTests {
         assertIterableEquals(new Byte[] {65, 32, 66, 32, 67, 32, 68, 32, 69}.toList(), result.toList())
         //make sure decoding null returns null
         assertEquals(null.decodeHex(), null)
+
+        //make sure decoding Groovy-falsy values returns null
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(''))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(0))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(false))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex([]))
+        assertEquals(null, HexCodecExtensionMethods.decodeHex(new byte[0]))
     }
 
     void testRoundtrip() {

Review Comment:
   Pre-existing, but since this file is being touched: `testRoundtrip` has no `@Test` annotation, so it never runs — and it's exactly the round-trip coverage the new static implementations want. Note that enabling it as-is will likely fail on the first assertion: `assertIterableEquals` compares `Integer` expected values against the `Byte` elements from `decodeHex(...).toList()`, and `Integer(65).equals(Byte(65))` is false — the expectations need a byte-typed list.



##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/MD5CodecExtensionMethods.groovy:
##########
@@ -18,15 +18,19 @@
  */
 package org.grails.plugins.codecs
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 class MD5CodecExtensionMethods {
 
     // Returns the byte[] of the digest, taken from UTF-8 of the string representation
     // or the raw data coerced to bytes
-    static encodeAsMD5(theTarget) {
-        theTarget.encodeAsMD5Bytes()?.encodeAsHex()
+    static Object encodeAsMD5(Object theTarget) {
+        byte[] digest = (byte[]) MD5BytesCodecExtensionMethods.encodeAsMD5Bytes(theTarget)

Review Comment:
   Just confirming this is intended: the old `theTarget.encodeAsMD5Bytes()?.encodeAsHex()` dispatched dynamically, so a runtime metaclass override of `encodeAsMD5Bytes` (e.g. an application-supplied codec replacing the default) would also change what `encodeAsMD5` produced. The direct static call hardwires the default implementation. Same applies to the SHA-1/SHA-256 variants. Probably fine — it's the whole point of removing dynamic dispatch — but worth a note in the compatibility section.



##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/HexCodecExtensionMethods.groovy:
##########
@@ -21,51 +21,47 @@ package org.grails.plugins.codecs
 import java.nio.charset.StandardCharsets
 
 import org.codehaus.groovy.runtime.NullObject
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 class HexCodecExtensionMethods {
 
-    static HEXDIGITS = '0123456789abcdef'
+    static Object HEXDIGITS = '0123456789abcdef'
 
     // Expects an array/list of numbers
-    static encodeAsHex(theTarget) {
+    static Object encodeAsHex(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
-        def result = new StringBuilder()
-        if (theTarget instanceof String) {
-            theTarget = theTarget.getBytes(StandardCharsets.UTF_8)
-        }
-        theTarget.each() {
-            result << HexCodecExtensionMethods.HEXDIGITS[(it & 0xF0) >> 4]
-            result << HexCodecExtensionMethods.HEXDIGITS[it & 0x0F]
+        byte[] bytes = theTarget instanceof String ? ((String) theTarget).getBytes(StandardCharsets.UTF_8) : DigestUtils.toByteArray(theTarget)
+        StringBuilder result = new StringBuilder(bytes.length * 2)
+        String hexDigits = (String) HEXDIGITS
+        for (byte value : bytes) {
+            int unsignedValue = value & 0xFF
+            result.append(hexDigits.charAt((unsignedValue & 0xF0) >> 4))
+            result.append(hexDigits.charAt(unsignedValue & 0x0F))
         }
         return result.toString()
     }
 
-    static decodeHex(theTarget) {
-        if (!theTarget) return null
+    static Object decodeHex(Object theTarget) {
+        if (theTarget == null || theTarget instanceof NullObject || !DefaultTypeTransformation.castToBoolean(theTarget)) return null
 
-        def output = []
-
-        def str = theTarget.toString().toLowerCase()
-        if (str.size() % 2) {
+        String str = theTarget.toString().toLowerCase()
+        if (str.size() % 2 != 0) {
             throw new UnsupportedOperationException('Decode of hex strings requires strings of even length')
         }
 
-        def currentByte
-        str.eachWithIndex { val, idx ->
-            if (!(idx % 2)) {
-                currentByte = HEXDIGITS.indexOf(val) << 4
-            }
-            else {
-                output << (currentByte | HEXDIGITS.indexOf(val))
-                currentByte = 0
-            }
+        byte[] result = new byte[str.size().intdiv(2)]
+        String hexDigits = (String) HEXDIGITS
+        for (int i = 0; i < str.size(); i += 2) {
+            int high = hexDigits.indexOf((int) str.charAt(i))
+            int low = hexDigits.indexOf((int) str.charAt(i + 1))
+            result[i.intdiv(2)] = (byte) ((high << 4) | low)

Review Comment:
   Nit for a perf-focused PR: `i.intdiv(2)` boxes on every iteration; `i >> 1` (and `str.length() >> 1` on line 58) avoids it. Same for `str.size()` vs plain `str.length()`. Also, on line 51 the explicit `null`/`NullObject` checks are redundant — `DefaultTypeTransformation.castToBoolean` already returns `false` for both.



##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/HexCodecExtensionMethods.groovy:
##########
@@ -21,51 +21,47 @@ package org.grails.plugins.codecs
 import java.nio.charset.StandardCharsets
 
 import org.codehaus.groovy.runtime.NullObject
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 class HexCodecExtensionMethods {
 
-    static HEXDIGITS = '0123456789abcdef'
+    static Object HEXDIGITS = '0123456789abcdef'
 
     // Expects an array/list of numbers
-    static encodeAsHex(theTarget) {
+    static Object encodeAsHex(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
-        def result = new StringBuilder()
-        if (theTarget instanceof String) {
-            theTarget = theTarget.getBytes(StandardCharsets.UTF_8)
-        }
-        theTarget.each() {
-            result << HexCodecExtensionMethods.HEXDIGITS[(it & 0xF0) >> 4]
-            result << HexCodecExtensionMethods.HEXDIGITS[it & 0x0F]
+        byte[] bytes = theTarget instanceof String ? ((String) theTarget).getBytes(StandardCharsets.UTF_8) : DigestUtils.toByteArray(theTarget)

Review Comment:
   Behavior change for iterable targets that aren't `List`s or arrays (e.g. a `Set<Integer>` or an `Iterator`): the old code element-wise hex-encoded anything via `theTarget.each { ... }`, but `DigestUtils.toByteArray` only special-cases `byte[]`/`Byte[]`/`List`/arrays and otherwise falls through to `toString().getBytes(UTF_8)` — so a `Set` of numbers now silently encodes its string representation instead of its elements. Consider handling `Collection`/`Iterable` in `toByteArray` (or at least calling this out in the compatibility notes, since the failure mode is silent wrong output rather than an exception).



-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4884658632

   ## 🚨 TestLens detected 1 failed test 🚨
   
   Here is what you can do:
   
   1) Inspect the test failures carefully.
   2) If you are convinced that some of the tests are flaky, you can mute them below.
   3) Finally, trigger a rerun by checking the rerun checkbox.
   
   ### Test Summary
   
   #### [CI / Build Grails-Core \(windows-latest, 25\)](https://github.com/apache/grails-core/actions/runs/28726960058/job/85185877949?pr=15828) > :grails-shell-cli:test
   
   | Test | Runs |
   |---|---|
   | RunningApplicationProcessSpec > stop terminates a running process and removes the PID file | ❌ |
   
   🏷️ Commit: fe0dd6965d604094227b6d0c1924a97b34596ac3
   ▶️ Tests:  36931 executed
   🟡 Checks: 30/40 completed
   
   ### Test Failures
   
   <details>
   
   <summary><strong>RunningApplicationProcessSpec > stop terminates a running process and removes the PID file</strong> (:grails-shell-cli:test in <a href="https://github.com/apache/grails-core/actions/runs/28726960058/job/85185877949?pr=15828">CI / Build Grails-Core (windows-latest, 25)</a>)</summary>
   
   ```
   Condition not satisfied:
   
   !process.isAlive()
   ||       |
   ||       true
   |Process[pid=6608, exitValue=1]
   false
   
   	at org.grails.cli.gradle.RunningApplicationProcessSpec.stop terminates a running process and removes the PID file(RunningApplicationProcessSpec.groovy:217)
   ```
   
   </details>
   
   ### Muted Tests
   > [!NOTE]
   > Checks are currently running using the configuration below.
   
   Select tests to mute in this pull request:
   
   🔲 RunningApplicationProcessSpec > stop terminates a running process and removes the PID file <!-- uniqueId=[engine:spock]/[spec:org.grails.cli.gradle.RunningApplicationProcessSpec]/[feature:$spock_feature_0_13] -->
   
   Reuse successful test results:
   
   🔲 ♻️ Only rerun the tests that failed or were muted before
   
   Click the checkbox to trigger a rerun:
   
   🔲 **Rerun jobs**
   
   ---
   _Learn more about TestLens at [testlens.app](https://testlens.app)._
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#discussion_r3524949500


##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/HexCodecExtensionMethods.groovy:
##########
@@ -22,50 +22,45 @@ import java.nio.charset.StandardCharsets
 
 import org.codehaus.groovy.runtime.NullObject
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 class HexCodecExtensionMethods {
 
-    static HEXDIGITS = '0123456789abcdef'
+    static Object HEXDIGITS = '0123456789abcdef'
 
     // Expects an array/list of numbers
-    static encodeAsHex(theTarget) {
+    static Object encodeAsHex(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
-        def result = new StringBuilder()
-        if (theTarget instanceof String) {
-            theTarget = theTarget.getBytes(StandardCharsets.UTF_8)
-        }
-        theTarget.each() {
-            result << HexCodecExtensionMethods.HEXDIGITS[(it & 0xF0) >> 4]
-            result << HexCodecExtensionMethods.HEXDIGITS[it & 0x0F]
+        byte[] bytes = theTarget instanceof String ? ((String) theTarget).getBytes(StandardCharsets.UTF_8) : DigestUtils.toByteArray(theTarget)
+        StringBuilder result = new StringBuilder(bytes.length * 2)
+        String hexDigits = (String) HEXDIGITS
+        for (byte value : bytes) {
+            int unsignedValue = value & 0xFF
+            result.append(hexDigits.charAt((unsignedValue & 0xF0) >> 4))
+            result.append(hexDigits.charAt(unsignedValue & 0x0F))
         }
         return result.toString()
     }
 
-    static decodeHex(theTarget) {
-        if (!theTarget) return null
+    static Object decodeHex(Object theTarget) {
+        if (theTarget == null || theTarget instanceof NullObject || theTarget.toString().length() == 0) return null
 

Review Comment:
   Fixed in 211a1ffae6. `decodeHex` now preserves the old Groovy truth behavior under `@CompileStatic` by using `DefaultTypeTransformation.castToBoolean`, and `HexCodecTests` covers falsy values including empty string, zero, false, empty list, and empty byte array.



-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#discussion_r3538028243


##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/Base64CodecExtensionMethods.groovy:
##########
@@ -24,32 +24,35 @@ import org.codehaus.groovy.runtime.NullObject
 
 import org.apache.commons.codec.binary.Base64
 
+import groovy.transform.CompileStatic
+
 /**
  * A codec that encodes and decodes Objects using Base64 encoding.
  *
  * @author Drew Varner
  */
+@CompileStatic
 class Base64CodecExtensionMethods {
 
-    static encodeAsBase64(theTarget) {
+    static Object encodeAsBase64(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
         if (theTarget instanceof Byte[] || theTarget instanceof byte[]) {
-            return new String(Base64.encodeBase64(theTarget))
+            return new String(Base64.encodeBase64(DigestUtils.toByteArray(theTarget)), StandardCharsets.UTF_8)
         }
 
-        return new String(Base64.encodeBase64(theTarget.toString().getBytes(StandardCharsets.UTF_8)))
+        return new String(Base64.encodeBase64(theTarget.toString().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)
     }
 
-    static decodeBase64(theTarget) {
+    static Object decodeBase64(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
         if (theTarget instanceof Byte[] || theTarget instanceof byte[]) {
-            return Base64.decodeBase64(theTarget)
+            return Base64.decodeBase64(DigestUtils.toByteArray(theTarget))
         }
 
         return Base64.decodeBase64(theTarget.toString().getBytes(StandardCharsets.UTF_8))

Review Comment:
   I think you should take the opportunity to make UTF_8 an added method argument that's defaulted



-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-5545210210

   ## ✅ All tests passed ✅
   
   🏷️ Commit: ad5d5c5eff90814702e0e19b9202b70d09adc568
   ▶️ Tests:  69269 executed
   ⚪️ Checks: 89/89 completed
   
   ---
   _Learn more about TestLens at [testlens.app/docs](https://testlens.app/docs/features/pr-comment/)._
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#discussion_r3524190428


##########
grails-codecs-core/src/main/groovy/org/grails/plugins/codecs/HexCodecExtensionMethods.groovy:
##########
@@ -22,50 +22,45 @@ import java.nio.charset.StandardCharsets
 
 import org.codehaus.groovy.runtime.NullObject
 
+import groovy.transform.CompileStatic
+
+@CompileStatic
 class HexCodecExtensionMethods {
 
-    static HEXDIGITS = '0123456789abcdef'
+    static Object HEXDIGITS = '0123456789abcdef'
 
     // Expects an array/list of numbers
-    static encodeAsHex(theTarget) {
+    static Object encodeAsHex(Object theTarget) {
         if (theTarget == null || theTarget instanceof NullObject) {
             return null
         }
 
-        def result = new StringBuilder()
-        if (theTarget instanceof String) {
-            theTarget = theTarget.getBytes(StandardCharsets.UTF_8)
-        }
-        theTarget.each() {
-            result << HexCodecExtensionMethods.HEXDIGITS[(it & 0xF0) >> 4]
-            result << HexCodecExtensionMethods.HEXDIGITS[it & 0x0F]
+        byte[] bytes = theTarget instanceof String ? ((String) theTarget).getBytes(StandardCharsets.UTF_8) : DigestUtils.toByteArray(theTarget)
+        StringBuilder result = new StringBuilder(bytes.length * 2)
+        String hexDigits = (String) HEXDIGITS
+        for (byte value : bytes) {
+            int unsignedValue = value & 0xFF
+            result.append(hexDigits.charAt((unsignedValue & 0xF0) >> 4))
+            result.append(hexDigits.charAt(unsignedValue & 0x0F))
         }
         return result.toString()
     }
 
-    static decodeHex(theTarget) {
-        if (!theTarget) return null
+    static Object decodeHex(Object theTarget) {
+        if (theTarget == null || theTarget instanceof NullObject || theTarget.toString().length() == 0) return null
 

Review Comment:
   `decodeHex` used to treat any Groovy-falsy target (e.g. `''`, `0`, `false`, empty collections/arrays) as `null` via `if (!theTarget) return null`. The new guard only checks for `null`/`NullObject`/empty-string, which can turn previously-non-throwing calls like `0.decodeHex()` or `[].decodeHex()` into exceptions or garbage output. If the intent is to preserve the public extension-method contract, restore the original Groovy-truthiness check.



-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4906142956

   ## 🚨 TestLens detected 2 failed tests 🚨
   
   Here is what you can do:
   
   1) Inspect the test failures carefully.
   2) If you are convinced that some of the tests are flaky, you can mute them below.
   3) Finally, trigger a rerun by checking the rerun checkbox.
   
   ### Test Summary
   
   #### [CI / Build Grails Forge \(Java 25, indy\=false\)](https://github.com/apache/grails-core/actions/runs/28881058036/job/85669077060?pr=15828) > :test-core:test [grails-forge]
   
   | Test | Runs |
   |---|---|
   | CreateAppSpec > test basic create-app build task | ❌ |
   | CreateRestApiSpec > test rest-api app with default json-views | ❌ |
   
   🏷️ Commit: 0aa35690ee9447533b96cd588404d39c13787131
   ▶️ Tests:  17662 executed
   🟡 Checks: 23/53 completed
   
   ### Test Failures
   
   <details>
   
   <summary><strong>CreateAppSpec > test basic create-app build task</strong> (:test-core:test [grails-forge] in <a href="https://github.com/apache/grails-core/actions/runs/28881058036/job/85669077060?pr=15828">CI / Build Grails Forge (Java 25, indy=false)</a>)</summary>
   
   ```
   org.gradle.testkit.runner.UnexpectedBuildFailure: Unexpected build execution failure in /tmp/test-app15473703256003729809 with arguments [build, -x, iT]
   
   Output:
   
   [Incubating] Problems report is available at: file:///tmp/test-app15473703256003729809/build/reports/problems/problems-report.html
   
   FAILURE: Build failed with an exception.
   
   * What went wrong:
   A problem occurred configuring root project 'test-app15473703256003729809'.
   > Failed to notify project evaluation listener.
      > org/codehaus/plexus/util/xml/XmlStreamReader
   
   * Try:
   > Run with --stacktrace option to get the stack trace.
   > Run with --info or --debug option to get more log output.
   > Run with --scan to get full insights from a Build Scan (powered by Develocity).
   > Get more help at https://help.gradle.org.
   
   Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.
   
   You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
   
   For more on this, please refer to https://docs.gradle.org/9.6.0/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
   
   BUILD FAILED in 1m 1s
   
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.lambda$build$2(DefaultGradleRunner.java:275)
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.run(DefaultGradleRunner.java:374)
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.build(DefaultGradleRunner.java:273)
   	at org.grails.forge.utils.CommandSpec.executeGradle(CommandSpec.groovy:79)
   	at org.grails.forge.create.CreateAppSpec.test basic create-app build task(CreateAppSpec.groovy:49)
   ```
   
   </details>
   <details>
   
   <summary><strong>CreateRestApiSpec > test rest-api app with default json-views</strong> (:test-core:test [grails-forge] in <a href="https://github.com/apache/grails-core/actions/runs/28881058036/job/85669077060?pr=15828">CI / Build Grails Forge (Java 25, indy=false)</a>)</summary>
   
   ```
   org.gradle.testkit.runner.UnexpectedBuildFailure: Unexpected build execution failure in /tmp/testrestapi13699236811266658816 with arguments [build]
   
   Output:
   
   [Incubating] Problems report is available at: file:///tmp/testrestapi13699236811266658816/build/reports/problems/problems-report.html
   
   FAILURE: Build failed with an exception.
   
   * What went wrong:
   A problem occurred configuring root project 'testrestapi13699236811266658816'.
   > Failed to notify project evaluation listener.
      > org/codehaus/plexus/util/xml/XmlStreamReader
   
   * Try:
   > Run with --stacktrace option to get the stack trace.
   > Run with --info or --debug option to get more log output.
   > Run with --scan to get full insights from a Build Scan (powered by Develocity).
   > Get more help at https://help.gradle.org.
   
   Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.
   
   You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
   
   For more on this, please refer to https://docs.gradle.org/9.6.0/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
   
   BUILD FAILED in 57s
   
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.lambda$build$2(DefaultGradleRunner.java:275)
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.run(DefaultGradleRunner.java:374)
   	at org.gradle.testkit.runner.internal.DefaultGradleRunner.build(DefaultGradleRunner.java:273)
   	at org.grails.forge.utils.CommandSpec.executeGradle(CommandSpec.groovy:79)
   	at org.grails.forge.create.CreateRestApiSpec.test rest-api app with default json-views(CreateRestApiSpec.groovy:30)
   ```
   
   </details>
   
   ### Muted Tests
   > [!NOTE]
   > Checks are currently running using the configuration below.
   
   Select tests to mute in this pull request:
   
   🔲 CreateAppSpec > test basic create-app build task <!-- uniqueId=[engine:spock]/[spec:org.grails.forge.create.CreateAppSpec]/[feature:$spock_feature_1_0] -->
   🔲 CreateRestApiSpec > test rest-api app with default json-views <!-- uniqueId=[engine:spock]/[spec:org.grails.forge.create.CreateRestApiSpec]/[feature:$spock_feature_1_0] -->
   
   Reuse successful test results:
   
   🔲 ♻️ Only rerun the tests that failed or were muted before
   
   Click the checkbox to trigger a rerun:
   
   🔲 **Rerun jobs**
   
   ---
   _Learn more about TestLens at [testlens.app](https://testlens.app)._
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4931086377

   ## ✅ All tests passed ✅
   
   🏷️ Commit: 6cfb084e03f866c97b546e6176ff0e79f5d14108
   ▶️ Tests:  9388 executed
   ⚪️ Checks: 59/59 completed
   
   ---
   _Learn more about TestLens at [testlens.app](https://testlens.app)._
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4886247666

   ## 🚨 TestLens detected 1 failed test 🚨
   
   Here is what you can do:
   
   1) Inspect the test failures carefully.
   2) If you are convinced that some of the tests are flaky, you can mute them below.
   3) Finally, trigger a rerun by checking the rerun checkbox.
   
   ### Test Summary
   
   #### [CI / Functional Tests \(Java 21, indy\=false\)](https://github.com/apache/grails-core/actions/runs/28741823918/job/85225847520?pr=15828) > :grails-test-examples-gorm:integrationTest
   
   | Test | Runs |
   |---|---|
   | DirtyCheckBindingSpec > an explicit bindable:true id constraint declared on a \@DirtyCheck base is inherited and binds over HTTP | ❌ |
   
   🏷️ Commit: 211a1ffae65aaf11b930d2a73a5837daeff36ee1
   ▶️ Tests:  1562 executed
   🟡 Checks: 19/39 completed
   
   ### Test Failures
   
   <details>
   
   <summary><strong>DirtyCheckBindingSpec > an explicit bindable:true id constraint declared on a @DirtyCheck base is inherited and binds over HTTP</strong> (:grails-test-examples-gorm:integrationTest in <a href="https://github.com/apache/grails-core/actions/runs/28741823918/job/85225847520?pr=15828">CI / Functional Tests (Java 21, indy=false)</a>)</summary>
   
   ```
   groovy.lang.MissingPropertyException: No such property: FORM for class: gorm.DirtyCheckBindingSpec
   	at gorm.DirtyCheckBindingSpec.an explicit bindable:true id constraint declared on a @DirtyCheck base is inherited and binds over HTTP(DirtyCheckBindingSpec.groovy:83)
   ```
   
   </details>
   
   ### Muted Tests
   > [!NOTE]
   > Checks are currently running using the configuration below.
   
   Select tests to mute in this pull request:
   
   🔲 DirtyCheckBindingSpec > an explicit bindable:true id constraint declared on a \@DirtyCheck base is inherited and binds over HTTP <!-- uniqueId=[engine:spock]/[spec:gorm.DirtyCheckBindingSpec]/[feature:$spock_feature_0_2] -->
   
   Reuse successful test results:
   
   🔲 ♻️ Only rerun the tests that failed or were muted before
   
   Click the checkbox to trigger a rerun:
   
   🔲 **Rerun jobs**
   
   ---
   _Learn more about TestLens at [testlens.app](https://testlens.app)._
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Improve hot path virtual-thread and codec performance [grails-core]

Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on PR #15828:
URL: https://github.com/apache/grails-core/pull/15828#issuecomment-4997825862

   Thanks for the detailed pass (and the Fabled review). These read as review-then-commit nits rather than blockers, and they're all reasonable - I'll take them:
   
   1. **Narrowed element types in `DigestUtils`** - restore the old `Character` / `char[]` tolerance with `DefaultTypeTransformation.castToNumber(value).byteValue()` (keeps the coercion while staying statically compiled). If we decide the narrowing is intentional instead, I'll add a line to the compatibility notes.
   2. **`protected` vs `@PackageScope`** - switch to `@PackageScope` so the package-access intent (for `Base64CodecExtensionMethods` / `HexCodecExtensionMethods`) is explicit rather than relying on `protected`'s side effect.
   3. **Iterable-target behavior change** - handle `Collection` / `Iterable` in `DigestUtils.toByteArray` so a `Set<Integer>` / `Iterator` hex-encodes its *elements* instead of silently falling through to `toString().getBytes(UTF_8)` (the silent-wrong-output failure mode you flagged).
   4. **Hot-loop nits** - `i >> 1` and `str.length() >> 1` instead of `intdiv` (avoids boxing), plain `str.length()`, and drop the redundant `null`/`NullObject` checks (`castToBoolean` already returns false).
   5. **MD5/SHA static dispatch** - confirmed intentional (that's the point of removing dynamic dispatch); I'll note in the compatibility section that a runtime metaclass override of `encodeAsMD5Bytes`/SHA variants no longer changes the result.
   6. **`HexCodecTests`** - move to the extension-method form (`''.decodeHex()`, `0.decodeHex()`, ...) per the test-via-public-API rule.
   7. **`testRoundtrip`** - enable it (add `@Test`) with byte-typed expectations so `assertIterableEquals` compares `Byte` to `Byte`.
   8. **`UTF_8`** - make it an added method argument defaulted to `StandardCharsets.UTF_8` in the Base64 methods.
   
   I'll push these fixes. On the `GrailsHttpSession` lock conversion - glad that read correctly. Since this is COMMENTED (not blocked), let me know if you'd prefer I split the pure compatibility-note items from the code fixes.
   


-- 
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: notifications-unsubscribe@grails.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org