Hone logo
Hone
Problems

Bulkhead Isolation Implementation in Python

Bulkhead isolation is a design pattern used to prevent failures in one part of a system from cascading to other parts. It works by isolating components, so that if one component fails, it doesn't bring down the entire system. This challenge asks you to implement a simplified bulkhead isolation mechanism in Python, focusing on limiting the number of concurrent calls to a potentially unreliable service.

Problem Description

You are tasked with creating a Bulkhead class in Python. This class will manage a pool of workers and limit the number of concurrent requests that can be made to a target function. The Bulkhead should accept a maximum concurrency limit and a target function (the service to be protected). When a request comes in, the Bulkhead should:

  1. Check if the current concurrency is below the maximum.
  2. If concurrency is below the limit, execute the target function and increment the concurrency count.
  3. If concurrency is at the limit, block the request until a worker becomes available (i.e., another request completes).
  4. Decrement the concurrency count after the target function completes, releasing a worker.

The Bulkhead should use threading to manage concurrency. The target function should be executed in a separate thread. The Bulkhead should handle potential exceptions raised by the target function and re-raise them after the worker is released.

Examples

Example 1:

Input:
max_concurrency = 2
target_function = lambda x: x * 2
bulkhead = Bulkhead(max_concurrency, target_function)

requests = [1, 2, 3, 4]

results = [bulkhead.execute(req) for req in requests]

Output:
[2, 4, 6, 8]
Explanation:
The first two requests (1 and 2) are executed concurrently. The next two requests (3 and 4) are blocked until the first two complete. The results are then returned in the order the requests were made.

Example 2:

Input:
max_concurrency = 1
target_function = lambda x: 10 / x  # Potential ZeroDivisionError
bulkhead = Bulkhead(max_concurrency, target_function)

requests = [2, 0, 3]

try:
    results = [bulkhead.execute(req) for req in requests]
except ZeroDivisionError as e:
    print(f"Caught ZeroDivisionError: {e}")

Output:
Caught ZeroDivisionError: division by zero
Explanation:
The first request (2) is executed. The second request (0) is blocked until the first completes. When the first request completes (and raises ZeroDivisionError), the exception is caught and re-raised after the worker is released. The third request is then processed.

Example 3: (Edge Case - Target function takes a long time)

Input:
max_concurrency = 2
target_function = lambda x: import time; time.sleep(5); return x * 2
bulkhead = Bulkhead(max_concurrency, target_function)

requests = [1, 2, 3]

results = [bulkhead.execute(req) for req in requests]

Output:
[2, 4, 6]
Explanation:
The first two requests (1 and 2) are executed concurrently. The third request (3) is blocked for 5 seconds while the first two are still running.  Once the first two complete, the third request is executed.

Constraints

  • max_concurrency must be a positive integer.
  • target_function must be a callable (e.g., a function, lambda expression).
  • The execute method should return the result of the target_function if it completes successfully.
  • Exceptions raised by the target_function should be caught and re-raised after the worker is released.
  • The order of results should reflect the order of requests, even if some requests are blocked.
  • The implementation should be thread-safe.

Notes

  • Consider using threading.Lock to protect shared resources (e.g., the concurrency counter).
  • threading.Condition can be helpful for blocking and notifying waiting threads.
  • Think about how to handle exceptions gracefully and ensure that the bulkhead doesn't get into a bad state.
  • The focus is on the core bulkhead isolation logic; error handling beyond re-raising exceptions is not required.
  • You do not need to implement a sophisticated queuing system; simply blocking and releasing threads is sufficient.
Loading editor...
python