matrei opened a new pull request, #16837: URL: https://github.com/apache/grails-core/pull/16837
## Description A draft to see on CI whether the unit test tasks can keep their test JVMs for the whole task instead of restarting them every 50 test classes. Not for merge as is. 1. A new, unpublished module, `grails-test-listener`, registers a JUnit Platform `TestExecutionListener` through `META-INF/services` that calls `Introspector.flushCaches()` after each test class. This is the cache #16834 flushes after each TCK spec: Groovy reads the BeanInfo of every class it builds a MetaClass for, and the JDK keeps it until the class is unloaded, which the classes of a test JVM never are. `gradle/test-config.gradle` and `grails-test-suite-uber` put the module on the test runtime classpath. 2. `forkEvery` now defaults to 0 in `gradle/test-config.gradle` (`-PforkEveryUnitTest` still overrides it), and `grails-test-suite-uber` no longer sets it. ### Local measurements The largest four modules that need no Docker, at the CI values (`-PmaxTestParallel=4 -PtestForkHeapMb=768`). A forces `forkEvery = 50` with the listener off; B is `forkEvery = 0` with the listener off; C is `forkEvery = 0` with the listener on. A probe listener (not committed) forced a full GC every 10 test classes and at the end of each JVM and recorded the live heap and the size of the Introspector's cache. Times are the median of three runs for uber and datamapping-core and of one run for the others. | Module | JVMs, A → B/C | Time, A / B / C | Peak live heap, A / B / C | Cached BeanInfos, B → C | |---|---|---|---|---| | grails-test-suite-uber | 20 → 4 | 55 s / 39 s / 38 s | 111 / 164 / 132 MB | 1,470 → 4 | | grails-datamapping-core | 8 → 4 | 52 s / 44 s / 45 s | 122 / 170 / 141 MB | 1,737 → 2 | | grails-gsp | 4 → 4 | 72 s / 73 s / 69 s | 313 / 313 / 220 MB | 3,196 → 5 | | grails-core | 8 → 8 | 47 s / 46 s / 47 s | 105 / 105 / 93 MB | 799 → 0 | Every run passed with the same test counts, and there was no full GC other than the probe's own. A JVM that runs a whole task holds 9-12 MB more metaspace. - `forkEvery` counts the top-level classes Gradle sends to a JVM, not the test classes JUnit runs, and Gradle deals them to the forks in turn. `grails-core` (187 class files, with 4 more JVMs for `testCli`) and `grails-gsp` (192) never reach 50 per fork, so they did not restart in A either. - `grails-test-suite-uber` does not apply `test-config.gradle` and so has no `**/*$*.class` exclude: all 910 of its class files, closures included, count, and its JVMs restart after about 12 test classes. - The `configureEach` of `test-config.gradle` runs after the settings of the suites that apply it: `grails-test-suite-web:execIsolatedTests` runs with `forkEvery = 50`, not the 1 its build script sets, and `grails-test-suite-persistence` loses its `forkEvery`, its local 2 GB heap and its three `excludes`, so the classes it isolates in their own tasks also run in `test`. Not changed here. ### What to look at - The durations of the `Build Grails-Core` jobs against recent 8.0.x runs. - New failures: a class that leaks static state now reaches every later class in its JVM rather than at most the next 49. - Out of memory errors, or heap or metaspace growth, in the larger modules. `./gradlew clean aggregateViolations --continue` passes locally. The full unit and integration suite (`:grails-test-report:check`) was not run locally: this pull request's CI run is that run. -- 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]
