CompletableFuture.applyToEither applies a function when either input completes normally. It is a result race, not automatic hedging, timeout, or loser cancellation.
Java applyToEither: use the first normal result of two stages
Operational contract
Two already-started replica lookups can feed this method. It rejects null results in the winning callback, then returns the first normal value that reaches that callback. Exceptional completion rules should be tested for the application's failure combinations; do not assume the other request always rescues an early failure. A service that needs guaranteed first-success behavior across failures should coordinate those results explicitly. Both replica requests can still consume bandwidth after one result is chosen.
Failure case
The east replica responds normally while the west request is still blocked. The returned future can complete from east, but west remains active unless its caller cancels or times it out. An early failure must be handled under an explicit fallback policy rather than presumed harmless.
Java code
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class ReplicaReceiptRace {
public static CompletableFuture<String> firstReceipt(
CompletableFuture<String> eastReplica,
CompletableFuture<String> westReplica) {
Objects.requireNonNull(eastReplica);
Objects.requireNonNull(westReplica);
return eastReplica.applyToEither(westReplica, receipt ->
Objects.requireNonNull(receipt, "Replica returned no receipt"));
}
}Performance and ownership cost
The continuation adds O(1) bookkeeping. Starting two requests can nearly double upstream resource use, even when one wins quickly; latency follows completion timing and failure handling, not a fixed bound.
Common Mistakes
- Do not assume the losing request is cancelled.
- Do not describe this stage as a guaranteed first-success fallback for every failure order.
- Do not launch duplicate writes through a result race without idempotency rules.
Connected lessons
- Java thenCombine: join two independent results without serializing them
- Java CompletableFuture cancellation: distinguish result status from worker interruption
- Java CompletableFuture timeout: completion is not worker cancellation
- Java exceptionallyCompose: recover with another asynchronous stage
- Java asynchronous contracts quiz
- Advanced Java
