Java exceptionallyCompose: fallback แบบ async โดยไม่ซ้อน CompletableFuture
exceptionally เหมาะกับ fallback ที่คืนค่าได้ทันที แต่ถ้าต้องอ่าน cache หรือเรียก service สำรองซึ่งคืน future อีกตัว เราจะได้โครงสร้างซ้อนถ้าพยายามคืน CompletableFuture<T> จากมัน exceptionallyCompose แบน recovery future ให้เหลือชั้นเดียวเหมือน thenCompose ฝั่ง error ครับ
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class Main {
static CompletableFuture<String> primary(boolean fail) {
if (fail) return CompletableFuture.failedFuture(new IllegalStateException("offline"));
return CompletableFuture.completedFuture("live");
}
public static void main(String[] args) {
String recovered = primary(true)
.exceptionallyCompose(error ->
CompletableFuture.completedFuture("cached"))
.join();
assert recovered.equals("cached");
assert primary(false).join().equals("live");
try {
primary(true).join();
throw new AssertionError("failure expected");
} catch (CompletionException expected) {
assert expected.getCause() instanceof IllegalStateException;
}
System.out.println("exceptionallyCompose tests passed");
}
}
อย่า fallback ทุก exception เหมือนกัน authentication failure, invalid input และ cancellation มักไม่ควรถูกแปลงเป็นข้อมูล cache ควร inspect cause แล้วกู้เฉพาะ transient failure อีกจุดคือเมธอดนี้ไม่ได้สร้าง timeout ให้เอง งาน primary และ fallback ต่างต้องมี deadline ของตัวเองเพื่อไม่ให้ recovery แขวนยาวกว่า request เดิม
Recap
- ใช้เมื่อ recovery คืน
CompletionStageอีกตัว - กู้เฉพาะ error ที่ fallback รับมือได้
- กำหนด timeout แยกให้ primary และ fallback
- รักษา cause เดิมเมื่อ recovery ก็ล้ม