Understanding the Bulkhead Pattern in Microservices-Handling Concurrent Fund Transfers with Virtual…
A practical deep-dive into how modern microservices handle 1000 concurrent requests using Bulkhead, Circuit Breaker, Java 21 Virtual…
Understanding the Bulkhead Pattern in Microservices-Handling Concurrent Fund Transfers with Virtual Threads, Kafka, and Kubernetes
A practical deep-dive into how modern microservices handle 1000 concurrent requests using Bulkhead, Circuit Breaker, Java 21 Virtual Threads, Kafka, and Kubernetes scaling.

When I first hear about the Bulkhead pattern, the explanation usually sounds simple
Limit how many calls go to a dependency
But once I try to apply this concept in a real microservice system, especially something critical like a banking fund transfer, several practical questions immediately come up in my mind
In microservices, aren’t services already isolated? Why do we still need Bulkhead?
In modern architectures where we use asynchronous queues like Kafka, do we still need Bulkhead at all?
With Java 21 Virtual Threads, doesn’t the thread exhaustion problem go away?
If 200 users hit the API and only 20 fraud checks are allowed, aren’t the other users effectively seeing a failure?
In this article, we will walk through a simple banking fund transfer architecture and see where Bulkhead, Circuit Breaker, Virtual Threads, async messaging, and Kubernetes pod scaling fit together.
Assume we have a Transfer Service running as a microservice.
It is deployed on Tomcat with a fixed thread pool.
Example configuration
Tomcat maxThreads = 200
Now imagine this scenario
1000 users initiate fund transfers at the same time
What happens inside the server?
1000 incoming requests
|
200 active threads
800 waiting in request queue
Tomcat can process only 200 requests at a time. The remaining requests must wait in the connection queue.
Horizontal Scaling with Kubernetes
In modern systems, services usually run inside Kubernetes clusters. Instead of one instance, multiple pods handle the load.
Example
Transfer Service Pods = 3 Threads per pod = 200
Total capacity becomes
3 pods × 200 threads = 600 concurrent requests
If traffic increases, Horizontal Pod Autoscaler (HPA) automatically scales the service.
Example scaling event
Pods scale from 3 → 5
New capacity
5 pods × 200 threads = 1000 concurrent requests
This is how cloud systems handle large traffic spikes.
However, scaling alone does not protect the system from slow or failing dependencies.
Typical Fund Transfer Flow
A simplified synchronous flow might look like this
Mobile App
|
POST /transfer
|
+---- Validate Account
+---- Fraud Check
+---- Check Balance
+---- Debit Sender Account
+---- Initiate Transfer
The API responds to the user:
Transfer Initiated
Transaction ID: TX123
But remember — a real transfer is not just debit. The money must also be credited to the receiver account, often in another bank. That part usually happens asynchronously.
Asynchronous Processing
After debiting the sender account, the system publishes an event.
TransferInitiated Event
|
v
Kafka
Downstream services process the transfer.
Payment Processor
|
+---- Payment Network
|
+---- Receiving Bank
|
+---- Credit Receiver Account
Once the receiver bank successfully credits the account, another event is generated.
TransferCompleted Event
Other systems react asynchronously
Send SMS
Send Email
Update Analytics
Update Loyalty Points
This design ensures that slow downstream systems do not block the user-facing API.
Why Microservices Alone Are Not Enough
A common assumption is:
Microservices already provide isolation.
Microservices isolate services, but not resources inside a service.
Example
Transfer Service
|
+---- Fraud Service
+---- Payment Network
+---- Notification Service
If Fraud Service becomes slow, it can still consume all threads inside Transfer Service.
Bulkheads isolate resource usage per dependency.
The Real Problem: Slow Dependencies
Now imagine the Fraud Service becomes slow.
Normally
Fraud check = 50 ms
But during heavy load
Fraud check = 5 seconds
Now all incoming requests call Fraud Service.
Transfer Service
|
200 Tomcat threads
|
Waiting on Fraud Service
What happens next?
- All threads become blocked
- New requests cannot start
- Even unrelated APIs may stop responding
The service appears down, even though the code itself is fine. This is known as a cascading failure.
Bulkhead Pattern to the Rescue
The Bulkhead pattern limits how many requests can access a dependency.
Example configuration:
Fraud Service concurrency limit = 20
Now when 200 requests arrive:
20 requests → allowed to call Fraud Service
180 requests → queued or throttled
The key idea is
A slow dependency should never consume all system resources.
Addressing the Common Concern
One natural question is:
If only 20 fraud calls are allowed, do the other 180 users see failures?
Not necessarily. Systems usually implement one of three approaches.
Queueing
Throttling
Prioritization
Queueing
20 processing
180 waiting
As soon as one fraud check finishes, another request starts.
Throttling
Extra requests are rejected temporarily.
HTTP 429
Please retry shortly
Prioritization
Important transactions are processed first.
Example
High-value transfers → immediate
Low-value transfers → delayed
This ensures the system remains stable instead of collapsing completely.
Circuit Breaker: Another Layer of Protection
Bulkhead limits concurrency.
Circuit Breaker protects against repeated failures.
Example scenario:
Fraud Service keeps failing
After multiple failures:
Circuit breaker opens
Now the system stops calling Fraud Service temporarily and returns a fallback response.
Example
Fraud system unavailable
Please retry later
After some time, the circuit closes and normal calls resume.
Where Virtual Threads Fit In
Java 21 introduced Virtual Threads through Project Loom.
Traditional servers use platform threads.
Example
200 platform threads
Virtual threads allow thousands of lightweight threads.
Example
10,000 virtual threads
This improves concurrency and resource efficiency.
However, virtual threads do not protect external dependencies.
If Fraud Service becomes slow:
10,000 virtual threads could call it simultaneously
This might overwhelm the Fraud system even faster.
So virtual threads improve scalability, not resilience.
Bulkheads are still necessary.
Putting Everything Together
A resilient banking transfer architecture typically looks like this

- Kubernetes Pod Scaling: Pods scale horizontally (e.g., 3 → 6 → 10) to distribute load across instances. Purpose: handle traffic spikes. Important: scaling solves high traffic, not downstream failures.
- Virtual Threads: From Project Loom allow thousands of lightweight threads per pod for high concurrency. Purpose: efficiently handle many blocking requests. Important: they don’t protect dependencies.
- Bulkhead Pattern: Limits concurrent calls to services (e.g., FraudService 20 calls). Purpose: isolate failures. Important: one slow service cannot consume all resources.
- Circuit Breaker Pattern: Stops repeated failing calls. Purpose: prevent cascading failures. Important: system fails fast instead of waiting for long timeouts.
- Synchronous Flow: User → API Gateway → Transfer Service → Fraud → Balance → Debit. Purpose: complete critical validation before response.
- Asynchronous Flow: Event published to Apache Kafka. Purpose: decouple services and improve scalability.
- Async Processing: Kafka consumers handle payment network and receiving bank credit. Important: user does not wait for these steps.
- Notifications: TransferCompleted event triggers SMS, Email, and Analytics. Purpose: notify users and update downstream systems.
If you found this helpful, don’t forget to give this article a clap 👏 and follow me for more tips and insights! Your support means a lot.
메타데이터
- post_id
- bb289a1071af
- slug
- understanding-the-bulkhead-pattern-in-microservices-handling-concurrent-fund-transfers-with-virtual-bb289a1071af
- url
- https://levelup.gitconnected.com/understanding-the-bulkhead-pattern-in-microservices-handling-concurrent-fund-transfers-with-virtual-bb289a1071af
- canonical_url
- https://levelup.gitconnected.com/understanding-the-bulkhead-pattern-in-microservices-handling-concurrent-fund-transfers-with-virtual-bb289a1071af
- author_url
- https://medium.com/@sudhass
- status
- ok
- fetched_at
- 2026-06-15 22:55:51