temporalio / temporalio/sdk-java
TimeLockingFuture implementation is incorrect and easy to break
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 433
- Forks
- 249
- Avg merge
- 5d 6h
- Merged PRs (30d)
- 26
Description
Expected Behavior
CompletableFuture returned from WorkflowStub#getResult and WorkflowStub#getResultAsync that has time skipping functionality built-in can correctly work with all CompletableFuture methods without losing time skipping functionality.
Actual Behavior
CompletableFuture that is returned from #getResultAsync when we use TestWorkflowService is TimeLockingFuture. It overrides CompletableFuture#get and CompletableFuture#join to provide time skipping functionality.
This implementation is extremely brittle because any chaining of this CompletableFuture will not call the overridden #get and #join. So even trivial chaining completely and unexpectedly breaks the time-skipping feature.
Steps to Reproduce the Problem
- Use TestWorkflowService
- Perform any trivial chaining on the result returning from getResultAsync
- Time skipping doesn't work on
#getcall on a chained CompletableFuture from step 2.
Demonstration of the problem: https://github.com/Spikhalskiy/java-sdk/commit/e73d574428a7001534601c3bebabb60a23612bff
Adding a trivial result = result.thenApply(s -> s); chaining breaks the test and it hangs.
Proposed solution
The current solution with intercepting stub methods and extending CompletableFuture looks like a hack and I'm not sure if it's possible to make it work correctly with CompletableFuture implementation.
My proposal is to completely remove built-in time skipping from WorkflowStub and TimeLockingFuture.
One of the correct and nice ways to give time-skipping to tests developers can be through exposing of method .skipTime on some helper object that would return an AutoCloseable object that will trigger disabling of time-skipping at the end. It could be used by the test writers using the following pattern:
try (TimeSkipUtils.forStub(worflowStub).skipTimeSegment()) {
result = resultFuture.get()
}
TimeSkipUtils.forStub can dig into worflowStub and make a helper that is aware of specific TestWorkflowService and provides
.skipTimeSegment() that returns our ```AutoCloseable``` resource described above.
Specifications
- Version: Current master (5f2ac6133c6560984eff35cc85faafb85cbf2c94)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with WorkflowStub#getResultAsync, TimeLockingFuture, and TestWorkflowService, then reproduce the reported failure by applying thenApply to the returned CompletableFuture. Review how CompletableFuture chaining bypasses the overridden get and join methods. Done means the proposed time-skipping behavior remains reliable for chained futures, or the replacement test-facing API is defined and verified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- testing
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100