codeconsole opened a new issue, #16219:
URL: https://github.com/apache/grails-core/issues/16219
`functionaltests.async.AsyncPromiseSpec` fails intermittently, and not
always on the same feature, when the HTTP client gives up waiting for a
response:
```
java.net.http.HttpTimeoutException: request timed out
at
java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:953)
at
org.apache.grails.testing.http.client.HttpClientSupport$Trait$Helper.send(HttpClientSupport.groovy:1133)
at
org.apache.grails.testing.http.client.HttpClientSupport$Trait$Helper.http(HttpClientSupport.groovy:203)
at
functionaltests.async.AsyncPromiseSpec.<feature>(AsyncPromiseSpec.groovy:<line>)
```
The client timeout is 120 seconds (`grails.http.client.timeout`, set in
`gradle/functional-test-config.gradle`).
## Occurrences
| Date (UTC) | Branch | Job | Feature that timed out |
|---|---|---|---|
| 2026-07-29 20:32 | `feat/8.0.x-legacy-command-compat` | [Hibernate7
Functional Tests (Java 21,
indy=true)](https://github.com/apache/grails-core/actions/runs/30488829586/job/90701275362)
| conditional async uses sync mode when requested |
| 2026-07-29 21:01 | **`8.0.x`** | [Functional Tests (Java 21,
indy=false)](https://github.com/apache/grails-core/actions/runs/30490747146/job/90707733744)
| multi-stage process reports all stages |
| 2026-08-22 17:23 | `fix/embedded-mongo-replica-set` | [Build Grails with
Groovy snapshot (shard
2)](https://github.com/apache/grails-core/actions/runs/32587420540/job/97066257811)
| conditional async uses sync mode when requested |
| 2026-08-24 23:53 | `ci/…` on #16214 | [Build Grails with Groovy snapshot
(shard
0)](https://github.com/apache/grails-core/actions/runs/32791117146/job/97633380610)
| async service processes string input |
Found by scanning the failed job logs of every `CI` and `CI - Groovy Joint
Validation Build` run between 2026-07-20 and 2026-08-24 - 308 logs, none
unavailable - for that spec together with that exception. These four are all of
them in that period.
Three different features, two different applications
(`grails-test-examples-app1` and `grails-test-examples-hibernate7-app1`), both
indy settings, and one of them on `8.0.x` itself. Everything else in the same
specification passes in the same run: on 2026-08-24, 695 tests completed, 1
failed.
## Why a timeout here is odd
The 2026-08-24 failure was on `/asyncTest/useAsyncService`, and that action
cannot take 120 seconds:
```groovy
def useAsyncService() {
def input = params.input ?: 'hello'
def future = asyncProcessingService.processAsync(input)
def result = future.get(5, TimeUnit.SECONDS) // bounded
render([input: input, result: result] as JSON)
}
```
The worst it can do is throw after five seconds and return a 500. A client
that waits 120 seconds and receives nothing suggests the request was never
served, rather than that this action was slow.
## Possibly relevant, not established
The feature declared immediately before it in the same controller
deliberately abandons a thread on every data-driven case:
```groovy
def workThread = Thread.start {
sleep(delay)
completed.set(true)
}
workThread.join(timeout) // 10 ms, while the work sleeps 100 ms
if (!completed.get()) {
workThread.interrupt()
message = "Task exceeded timeout of ${timeout} ms"
}
```
A raw thread per request, interrupted mid-sleep, is the kind of thing that
leaves debris under load - but nothing here shows that it is what starves the
later request, and the 2026-07-29 failures were on other features.
## What has been ruled out
The promise decorator change in #16192
(`AsyncWebRequestPromiseDecoratorLookupStrategy`, which stops decorating a
request the container will not start a new asynchronous cycle on). Two of the
four occurrences are from 2026-07-29, three and a half weeks before that change
was written, and one of those is on `8.0.x`.
## Reproducing
Not reproduced locally. `:grails-test-examples-app1:integrationTest --tests
"*AsyncPromiseSpec*"` was run four times in a row - 84 tests, no failures - and
the hibernate7 application's copy three times before that.
## Suggested next step
The failure says only that the client gave up. What would settle it is the
server's side of the same moment: whether the request arrived at all. An access
log on these applications during functional tests, or a thread dump taken when
a request exceeds the client timeout, would distinguish "never served" from
"served slowly" - which is the fork this depends on.
--
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]