Skip to content

Getting Started

Frody edited this page Sep 3, 2026 · 1 revision

Getting Started with Fuse

This guide covers bootstrapping, protected invocation signatures, and production-grade fallback handling with Fuse.


Installation

Fuse is available on Maven Central under the coordinates:

Maven

Add the dependency to your pom.xml:

<dependency>
    <groupId>io.github.frodygr</groupId>
    <artifactId>fuse</artifactId>
    <version>1.0.0</version>
</dependency>

Gradle

implementation("io.github.frodygr:fuse:1.0.0")

Fuse requires Java 17 or higher and brings zero transitive runtime dependencies.


Protected Execution Overloads

The CircuitBreaker interface provides three execution methods tailored to standard Java functional contracts:

1. Operations returning values without checked exceptions (Supplier<T>)

Use execute(Supplier<T>) for standard operations that only throw runtime exceptions:

CircuitBreaker cb = new DefaultCircuitBreaker("inventory-service", config);

// Invocations returning a response object
InventoryStatus status = cb.execute(() -> inventoryClient.checkStock(sku));

2. Operations throwing checked exceptions (Callable<T>)

Use executeChecked(Callable<T>) when interacting with legacy libraries or network clients (such as standard java.net.http.HttpClient or JDBC) that declare checked exceptions:

try {
    HttpResponse<String> response = cb.executeChecked(() -> {
        HttpRequest request = HttpRequest.newBuilder(uri).build();
        return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
    });
} catch (CircuitBreakerOpenException e) {
    // Fast-path rejection when circuit is open
} catch (IOException | InterruptedException e) {
    // Handled downstream I/O failures
} catch (Exception e) {
    // Generic catch block
}

3. Fire-and-forget operations without return values (Runnable)

Use execute(Runnable) for side-effects, cache flushes, or background notification triggers:

cb.execute(() -> auditPublisher.publishEvent(event));

Fallback Design Patterns

When a circuit transitions to OPEN, calling execute(...) immediately throws a CircuitBreakerOpenException. Designing deliberate fallback paths is essential to keeping upstream systems resilient.

Pattern 1: Cache Fallback (Stale-While-Revalidate)

Serve cached or degraded data when the live downstream fails:

public ProductDetails getProductDetails(String productId) {
    try {
        ProductDetails liveData = circuitBreaker.execute(() -> productClient.fetch(productId));
        localCache.put(productId, liveData);
        return liveData;
    } catch (CircuitBreakerOpenException | ServiceUnavailableException ex) {
        log.warn("Product service unavailable. Serving cached snapshot for {}", productId);
        return localCache.getOrDefault(productId, ProductDetails.empty(productId));
    }
}

Pattern 2: Degraded Feature Mode

Gracefully disable non-critical functionality (e.g., product recommendations, real-time reviews) without breaking the core checkout flow:

public OrderSummary finalizeCheckout(OrderRequest request) {
    OrderSummary summary = orderService.process(request);

    try {
        List<Recommendation> recs = recBreaker.execute(() -> recommendationClient.getRecommendations(request.userId()));
        summary.setRecommendations(recs);
    } catch (CircuitBreakerOpenException ex) {
        // Recommendations are non-essential; omit them gracefully
        summary.setRecommendations(Collections.emptyList());
    }

    return summary;
}

Pattern 3: Explicit HTTP 503 Service Unavailable Translation

In RESTful APIs, translate CircuitBreakerOpenException into an RFC-compliant HTTP 503 with a Retry-After header:

@ExceptionHandler(CircuitBreakerOpenException.class)
public ResponseEntity<ErrorResponse> handleCircuitBreakerOpen(CircuitBreakerOpenException ex) {
    return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
            .header("Retry-After", "30")
            .body(new ErrorResponse("DOWNSTREAM_UNAVAILABLE", "Upstream service temporarily isolated. Retry in 30s."));
}

Manual Administrative Reset

In emergency scenarios (e.g., after an operational failover or database restart), operators can manually clear failure counters and restore normal traffic immediately:

// Forcefully resets state to CLOSED and clears all error counters
circuitBreaker.reset();

Clone this wiki locally