Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions bulkhead/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
* Demonstrates Bulkhead Pattern
*/
public class App {

public static void main(String[] args) throws InterruptedException {
System.out.println("=== Bulkhead Pattern Demo ===");

// Create bulkhead with only 2 concurrent calls
BulkheadConfig config = new BulkheadConfig(2, 1000);
ServiceBulkhead bulkhead = BulkheadPattern.getBulkhead("PaymentService", config);

// Simulate multiple service calls
List<CompletableFuture<String>> futures = new ArrayList<>();

for (int i = 1; i <= 5; i++) {
final int taskId = i;
CompletableFuture<String> future = bulkhead.execute(() -> {
System.out.println("Task " + taskId + " started - Available permits: " +
bulkhead.getAvailablePermits());
Thread.sleep(2000); // Simulate work
System.out.println("Task " + taskId + " completed");
return "Result-" + taskId;
});

futures.add(future);
}

// Wait for completion
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
System.out.println("\nFinal metrics: " + bulkhead.getMetrics());

BulkheadPattern.shutdownAll();
}
}
15 changes: 15 additions & 0 deletions bulkhead/BulkheadConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Configuration for Bulkhead pattern
*/
public class BulkheadConfig {
private final int maxConcurrentCalls;
private final long maxWaitTimeMs;

public BulkheadConfig(int maxConcurrentCalls, long maxWaitTimeMs) {
this.maxConcurrentCalls = maxConcurrentCalls;
this.maxWaitTimeMs = maxWaitTimeMs;
}

public int getMaxConcurrentCalls() { return maxConcurrentCalls; }
public long getMaxWaitTimeMs() { return maxWaitTimeMs; }
}
20 changes: 20 additions & 0 deletions bulkhead/BulkheadMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import java.util.concurrent.atomic.AtomicLong;

/**
* Tracks bulkhead performance metrics
*/
public class BulkheadMetrics {
private final AtomicLong successfulCalls = new AtomicLong();
private final AtomicLong failedCalls = new AtomicLong();
private final AtomicLong rejectedCalls = new AtomicLong();

public void recordSuccessfulCall() { successfulCalls.incrementAndGet(); }
public void recordFailedCall() { failedCalls.incrementAndGet(); }
public void recordRejectedCall() { rejectedCalls.incrementAndGet(); }

@Override
public String toString() {
return String.format("Metrics[successful=%d, failed=%d, rejected=%d]",
successfulCalls.get(), failedCalls.get(), rejectedCalls.get());
}
}
25 changes: 25 additions & 0 deletions bulkhead/BulkheadPattern.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Bulkhead Pattern - isolates application elements to prevent cascading failures
*
* <p>Inspired by ship bulkheads that prevent water from flooding entire vessel.
* Limits concurrent executions to protect system resources.</p>
*/
public class BulkheadPattern {
private static final Map<String, ServiceBulkhead> BULKHEADS = new ConcurrentHashMap<>();

public static ServiceBulkhead getBulkhead(String name, BulkheadConfig config) {
return BULKHEADS.computeIfAbsent(name, k -> new ServiceBulkhead(name, config));
}

public static ServiceBulkhead getBulkhead(String name) {
return getBulkhead(name, new BulkheadConfig(10, 1000));
}

public static void shutdownAll() {
BULKHEADS.values().forEach(ServiceBulkhead::shutdown);
BULKHEADS.clear();
}
}
20 changes: 20 additions & 0 deletions bulkhead/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Bulkhead Pattern

## Intent

Isolate elements of an application into pools so that if one fails, others continue to function. Prevents cascading failures in distributed systems.

## Explanation

Similar to ship bulkheads that prevent flooding, this pattern limits concurrent executions to protect system resources. When a service becomes overloaded, the bulkhead contains the failure without affecting other services.

## Usage

```java
BulkheadConfig config = new BulkheadConfig(3, 1000);
ServiceBulkhead bulkhead = BulkheadPattern.getBulkhead("PaymentService", config);

CompletableFuture<String> result = bulkhead.execute(() -> {
// Your service call
return processPayment();
});
55 changes: 55 additions & 0 deletions bulkhead/ServiceBulkhead.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import java.util.concurrent.*;

/**
* Implements Bulkhead pattern to limit concurrent executions
*/
public class ServiceBulkhead {
private final Semaphore semaphore;
private final BulkheadConfig config;
private final String name;
private final BulkheadMetrics metrics;
private final ExecutorService executor;

public ServiceBulkhead(String name, BulkheadConfig config) {
this.name = name;
this.config = config;
this.semaphore = new Semaphore(config.getMaxConcurrentCalls());
this.metrics = new BulkheadMetrics();
this.executor = Executors.newCachedThreadPool();
}

public <T> CompletableFuture<T> execute(Callable<T> task) {
try {
if (!semaphore.tryAcquire(config.getMaxWaitTimeMs(), TimeUnit.MILLISECONDS)) {
metrics.recordRejectedCall();
throw new RuntimeException("Bulkhead '" + name + "' timeout after " +
config.getMaxWaitTimeMs() + "ms");
}

return CompletableFuture.supplyAsync(() -> {
try {
T result = task.call();
metrics.recordSuccessfulCall();
return result;
} catch (Exception e) {
metrics.recordFailedCall();
throw new CompletionException(e);
} finally {
semaphore.release();
}
}, executor);

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
metrics.recordRejectedCall();
throw new RuntimeException("Bulkhead acquisition interrupted", e);
}
}

public BulkheadMetrics getMetrics() { return metrics; }
public int getAvailablePermits() { return semaphore.availablePermits(); }

public void shutdown() {
executor.shutdown();
}
}
25 changes: 25 additions & 0 deletions bulkhead/TestBulkhead.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import java.util.concurrent.CompletableFuture;

public class TestBulkhead {
public static void main(String[] args) throws InterruptedException {
System.out.println("Testing Bulkhead Pattern...");

// Test 1: Basic functionality
BulkheadConfig config = new BulkheadConfig(2, 1000);
ServiceBulkhead bulkhead = BulkheadPattern.getBulkhead("TestService", config);

// Execute a simple task
CompletableFuture<String> future = bulkhead.execute(() -> {
Thread.sleep(500);
return "Success!";
});

future.thenAccept(result -> System.out.println("Result: " + result));

Thread.sleep(1000);
System.out.println("Metrics: " + bulkhead.getMetrics());

bulkhead.shutdown();
System.out.println("✅ Basic test passed!");
}
}
Loading