Docker Compose ProcessRunner can hang or leave orphan processes#50963
Open
SebTardif wants to merge 1 commit into
Open
Docker Compose ProcessRunner can hang or leave orphan processes#50963SebTardif wants to merge 1 commit into
SebTardif wants to merge 1 commit into
Conversation
Release the output reader latch in a finally block so a failing stream read or output consumer cannot leave ProcessRunner.run blocked on CountDownLatch.await after the child process has exited. Destroy the child process when waitFor is interrupted so Docker Compose commands are not left running as orphans. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ProcessRunner can hang forever if a stream reader fails, and can leave orphan Docker Compose child processes when waitFor is interrupted.
Problem
ReaderThreadonly callsCountDownLatch.countDown()on the success path. If reading fails withIOException, or if the optionaloutputConsumerthrows, the latch is never released. After the child process exits,ProcessRunner.runblocks forever onlatch.await()inReaderThread.toString().Separately, when
process.waitFor()is interrupted, the interrupt flag is restored and an exception is thrown (fixed in #50451), but the child process is not destroyed. Docker Compose commands can remain as orphans.Change
latch.countDown()into afinallyblock inReaderThread.run().process.destroy()after restoring the interrupt flag whenwaitForis interrupted.Failure scenario
ProcessRunner.run(consumer, command...).toString()/await().For interrupt: a long-running compose command is interrupted during
waitFor; withoutdestroy(), the child keeps running after the Spring Boot thread has aborted.Origin
Introduced with Docker Compose support in
842e17ecedaf(2023-04-07, Moritz Halbritter). Present for ~3 years. Related prior fix: #50451 (interrupt flag restore only).Validation
runWhenOutputConsumerThrowsDoesNotHang: timed out without the latch fix; passes with it.runWhenInterruptedDestroysChildProcess: childsleepstayed alive without destroy; process is dead with the fix../gradlew :core:spring-boot-docker-compose:test --tests ProcessRunnerTestscheckFormatMain/checkFormatTest/checkstyleMain/checkstyleTestRelated
RunProcess.destroy()in loader-tools