01

What Is the Bulkhead Pattern?

The bulkhead pattern partitions system resources into isolated groups to limit the impact of failures.

When a group of resources is exhausted, only that group is affected.

Other parts of the system continue to function normally.

02

The Ship Analogy

Think of a ship with bulkheads—internal walls that divide compartments.

If one compartment fills with water, the ship remains afloat.

The Titanic sank because water overflowed bulkheads, flooding the entire hull.

03

Application to Notification Service

Instead of one shared thread pool for all consumers,

create multiple smaller thread pools.

Each pool serves a subset of consumers.

If one consumer slows down and uses all threads in a pool,

other pools continue delivering messages to remaining consumers.

04

Use in Services with Multiple Dependencies

For example, an order service must:

  • Check inventory

  • Process payment

The order service should not be overwhelmed by a degraded dependency.

The bulkhead pattern prevents this.

05

Three Implementation Methods

  1. Limit connections per dependency using separate connection pools.

  2. Limit concurrent requests using semaphores.

  3. Use dedicated thread pools per dependency.

06

Method 1: Connection Pool Limits

Set up a separate connection pool for each dependency.

Define a maximum number of connections per pool.

When all connections are in use, new requests are blocked.

This ensures only a limited number of threads call any one dependency.

07

Method 2: Request Counting with Semaphores

Use semaphores to track concurrent requests.

When the limit is reached, new requests are immediately rejected.

This prevents resource exhaustion.

08

Method 3: Dedicated Thread Pools

Create a separate thread pool for each dependency.

Each thread pool has a fixed size.

Worker threads route requests to the appropriate pool.

Only a limited number of threads can call any given dependency.

09

Trade-offs

Method 1 is simplest.

It’s a configuration setting in the client.

But it relies on client trust.

10

Trust in Client Libraries

Client libraries are constantly changing.

They may have bugs.

Performance may degrade.

Connection handling may change.

Therefore, service teams treat client libraries as black boxes.

11

Why Methods 2 and 3 Matter

They reduce reliance on client behavior.

They provide protection against client library failures.

12

Method 3: Maximum Isolation

Dedicated thread pools offer the strongest isolation.

But they introduce computational overhead.

13

Method 2: A Balanced Approach

Less overhead than method 3.

But less flexible.

For example, it does not allow request timeouts.

A worker thread remains blocked until the network call times out.

This can lead to unresponsive behavior in degraded conditions.

14

The Bucket Pattern and Resource Isolation

The bucket pattern divides system resources into bounded groups to isolate failures and limit impact.

15

Limited Group Sizes Are Essential

Each group must have a defined, finite size to prevent uncontrolled resource consumption.

16

Bounded System Elements

Critical components must be constrained, including:

  • Queue capacities
  • Thread pool sizes
  • Message payload sizes
  • Workload execution times
17

Traffic Shaping and Rate Limiting

The system must shape incoming traffic by imposing request rate limits.

18

Challenges in Defining Limits

Identifying appropriate limits across all dimensions is complex and often requires deep system understanding.

19

Ongoing Limit Management

Limits must be reviewed and updated regularly to reflect changing workloads and conditions.

20

Need for Periodic Testing

The system must undergo regular load testing and stress testing.

21

Testing Failure Modes

Failure scenarios must be tested to ensure resilience under adverse conditions.

22

Topic Scope

These testing and operational practices will be discussed in a separate section on testing and operational excellence in distributed systems.

Questions

  • What does the Titanic example reveal about the limitations of bulkhead design in real-world systems?
  • How does the first option (separate connection pools) differ from the second (semaphore-based request counting) in terms of trust and failure resilience?
  • Can the bulkhead pattern be applied to a service with multiple dependencies without changing the underlying client libraries?
  • Why might option three (separate thread pools) be considered more isolating than the other options?
  • What are the trade-offs between computational overhead and flexibility in implementing the bulkhead pattern?
  • Does the bulkhead pattern inherently address the issue of request timeouts or failure recovery?
  • How does the concept of bounded resources relate to the bulkhead pattern in practice?
  • What role does periodic testing play in maintaining the effectiveness of bulkhead boundaries?

Key takeaways

  • The bulkhead pattern partitions resources into isolated groups to limit the impact of failures.
  • A ship’s bulkhead analogy illustrates how compartmentalization prevents total system failure.
  • The pattern is applied by splitting thread pools or connection pools across different dependencies.
  • Each option—connection limits, request counting, or thread pools—offers different levels of isolation and trust.
  • The first option is simple but relies on trusting client libraries to behave correctly.
  • Options two and three provide better protection against client library bugs, though at different costs.
  • Thread pools in option three offer maximum isolation but introduce significant computational overhead.
  • Request counting (option two) is less flexible because it blocks new requests without allowing timeouts or recovery.
  • All limits—like queue sizes, thread pool sizes, or request rates—must be periodically tested and adjusted.