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

   Builds directly on @maczikasz's commit from #16132, preserved at the base of 
this branch. That commit diagnosed the problem and fixed the reproducibility 
half; the checksum layer on top is what keeps runtime reloading working.
   
   ## The problem
   
   `GroovyPageCompiler` baked the `.gsp` source's modification time into every 
generated class as a `LAST_MODIFIED` constant. Git stores no modification 
times, so every fresh clone or CI checkout gives each source a new one, and 
byte-for-byte identical sources compile to different classes.
   
   Because the value is a compile-time constant it belongs to the class's ABI 
and is inlined into callers, so the difference survives even Gradle's 
`COMPILE_CLASSPATH` normalization. Every task downstream of a jar carrying 
precompiled GSPs misses the build cache — #16132 measured roughly 616 hours of 
avoidable CI task re-execution over one week.
   
   It also means those jars do not reproduce. `etc/bin/verify-reproducible.sh` 
rebuilds published artifacts locally and diffs them, with no GSP exclusion, so 
a verifier's checkout yields different bytes than the release builder's for 
`grails-fields`, `grails-spring-security`, and anything else shipping 
precompiled pages.
   
   ## Why the timestamp could not simply be zeroed
   
   `LAST_MODIFIED` is read at runtime. 
`GroovyPageMetaInfo.checkIfReloadableResourceHasChanged` compares it against 
the live source to decide whether a precompiled page is stale, and 
`DefaultGroovyPageLocator.createGroovyPageCompiledScriptSource` installs a 
resource callable for *any* precompiled page when reloading is enabled — only 
the binary-plugin path nulls it.
   
   Zeroing the constant alone therefore switches off reloading for an 
application's own precompiled pages, which is a documented feature:
   
   > GSP reloading is supported for precompiled GSPs since Grails 1.3.5.
   > — *Making Changes to a Deployed Application*
   
   And it fails silently: the page renders stale with nothing logged.
   
   ## The change
   
   `GroovyPageParser` emits a `SOURCE_CHECKSUM` constant recording what the 
source *is* rather than when it was last touched. `GroovyPageMetaInfo` prefers 
it and falls back to `LAST_MODIFIED` for pages compiled by earlier versions, so 
@maczikasz's `lastModified > 0` guard stays load-bearing as the "nothing 
recorded" case.
   
   Identical sources now compile to identical bytes on every machine, and 
reload detection becomes *more* accurate than the timestamp it replaces:
   
   - a page merely copied or checked out afresh is no longer treated as changed
   - an edit is caught however close together two writes fall, where the 2000 
ms `reload.granularity` window could miss it
   
   ## Notes for reviewers
   
   - **This adds public API** — `GroovyPageParser.checksumOf`, plus 
`getSourceChecksum`/`setSourceChecksum` on `GroovyPageParser` and 
`GroovyPageMetaInfo`. The patch-branch policy says no API changes, so please 
flag it if that is disqualifying here. `checksumOf` cannot be avoided: 
`GroovyPageMetaInfo` (`org.grails.gsp`) and the compiler 
(`org.grails.gsp.compiler`) sit in different packages and must not drift on how 
a source is digested.
   - **`getLastModified()` now returns `0`** for pages precompiled by this 
version. Nothing in the repository calls it, and `getSourceChecksum()` is the 
replacement. 7.0.x has no within-7.0 upgrade guide, so this likely wants a 
release-note line instead.
   - **The reload check now reads and hashes the source** rather than only 
stat'ing it. Bounded by `grails.gsp.reload.interval` (5 s) and only when 
reloading is enabled, which is off by default for a deployed application.
   - **`GroovyPageCompiler`'s own up-to-date check is untouched** — it still 
compares mtimes at line 214. Gradle's stale-output handling covers the normal 
path, so I left it rather than change it blind.
   
   The doc change also corrects the documented default for 
`grails.gsp.reload.granularity` (the table said 1000, the code says 2000) and 
describes the content-based check.
   
   Fixes #16131
   


-- 
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