Skip to main content

bulkhead-pattern

Resilience pattern isolating system components to prevent cascade failures and enable partial degradation

ソース情報

リポジトリ
ffsshhttiikk/opencode-agents-skills
ソースの最終更新活動
2026年2月28日 22:54
検出された SKILL.md の言語
英語
スター
3
フォーク
2

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
Bulkhead Pattern
description
Resilience pattern isolating system components to prevent cascade failures and enable partial degradation
category
software-development
# Bulkhead Pattern ## What I do I provide a pattern that isolates system components to prevent failures in one part from affecting the entire system. Named after ship bulkheads that contain flooding, the bulkhead pattern allocates separate resources (threads, connections, capacity) to different components. If one component fails or becomes slow, it doesn't exhaust resources for other components, enabling graceful degradation and improved system resilience. ## When to use me Use bulkheads when you have multiple services or components that share common resources, when you need to prioritize certain operations over others, or when preventing cascade failures is critical. They're essential in high-traffic systems, microservices architectures, and applications with varying priority operations. Bulkheads are valuable when different operations have different resource requirements or failure tolerance. ## Core Concepts - **Isolation Levels**: Complete, pooled, or hybrid isolation - **Thread Pool**: Separate pools for different operations - **Connection Pooling**: Dedicated database connections per service - **Rate Limiting**: Controlling request rates per component - **Priority Queuing**: Processing high-priority first - **Resource Partitioning**: Dividing resources between components - **Semaphore**: Controlling concurrent access - **Failure Containment**: Containing failures within partitions - **Graceful Degradation**: Partial functionality during failures - **Resource Exhaustion**: Preventing total system failure ## Code Examples ### Thread Pool Bulkhead ```python import queue import threading import time from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Callable, Generic, TypeVar from uuid import uuid4 from enum import Enum T = TypeVar("T") class TaskPriority(Enum): HIGH = 0 MEDIUM = 1 LOW = 2 @dataclass class Task(Generic[T]): id: str func: Callable[..., T] args: tuple kwargs: dict priority: TaskPriority created_at: float = time.time() class PriorityThreadPoolExecutor: def __init__( self, max_workers: int, max_high_priority: int = 10, max_medium_priority: int = 20, max_low_priority: int = 50 ): self._max_workers = max_workers self._queues: dict[TaskPriority, queue.PriorityQueue] = { TaskPriority.HIGH: queue.PriorityQueue(maxsize=max_high_priority), TaskPriority.MEDIUM: queue.PriorityQueue(maxsize=max_medium_priority), TaskPriority.LOW: queue.PriorityQueue(maxsize=max_low_priority) } self._workers: list[threading.Thread] = [] self._running = True self._metrics = { "submitted": 0, "completed": 0, "rejected": 0 } def submit( self, func: Callable[..., T], *args, priority: TaskPriority = TaskPriority.MEDIUM, **kwargs ) -> str: task_id = str(uuid4()) task = Task( id=task_id, func=func, args=args, kwargs=kwargs, priority=priority ) q = self._queues[priority] if q.full(): self._metrics["rejected"] += 1 raise QueueFullError(f"Queue for {priority} is full") q.put((priority.value, task)) self._metrics["submitted"] += 1 return task_id def start(self) -> None: for _ in range(self._max_workers): worker = threading.Thread(target=self._worker_loop) worker.daemon = True worker.start() self._workers.append(worker) def _worker_loop(self) -> None: while self._running: for priority in TaskPriority: q = self._queues[priority] try: _, task = q.get(timeout=0.1) result = task.func(*task.args, **task.k kwargs) self._metrics["completed"] += 1 except queue.Empty: continue except Exception as e: print(f"Task failed: {e}") def shutdown(self) -> None: self._running = False for q in self._queues.values(): q.join() @property def metrics(self) -> dict: return self._metrics.copy() class QueueFullError(Exception): pass def process_order(order_id: str) -> dict: time.sleep(0.5) return {"order_id": order_id, "status": "processed"} def main(): executor = PriorityThreadPoolExecutor( max_workers=5, max_high_priority=5, max_medium_priority=10, max_low_priority=100 ) executor.start() try: high_id = executor.submit( process_order, "order-1", priority=TaskPriority.HIGH ) low_id = executor.submit( process_order, "order-2", priority=TaskPriority.LOW ) print(f"Submitted tasks: {high_id}, {low_id}") finally: time.sleep(2) executor.shutdown() print(f"Metrics: {executor.metrics}") ``` ### Connection Pool Bulkhead ```python import threading import queue from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Protocol, Generic, TypeVar from contextlib import contextmanager from uuid import uuid4 T = TypeVar("T", bound='PooledConnection') @dataclass class ConnectionConfig: max_connections: int min_idle: int = 0 connection_timeout: float = 30.0 idle_timeout: float = 600.0 class PooledConnection(Protocol): @property def id(self) -> str: pass def is_healthy(self) -> bool: pass def execute(self, query: str) -> dict: pass def close(self) -> None: pass class ConnectionPool(Generic[T]): def __init__(self, factory: Callable[[], T], config: ConnectionConfig): self._factory = factory self._config = config self._pool: queue.Queue[T] = queue.Queue() self._in_use: dict[str, T] = {} self._lock = threading.Lock() self._metrics = { "total_created": 0, "currently_in_use": 0, "total_errors": 0 } @contextmanager def acquire(self): conn = self._get_connection() self._in_use[conn.id] = conn self._metrics["currently_in_use"] += 1 try: yield conn finally: self._return_connection(conn) del self._in_use[conn.id] self._metrics["currently_in_use"] -= 1 def _get_connection(self) -> T: try: conn = self._pool.get_nowait() if conn.is_healthy(): return conn conn.close() except queue.Empty: pass with self._lock: if self._metrics["total_created"] < self._config.max_connections: conn = self._factory() self._metrics["total_created"] += 1 return conn conn = self._pool.get(timeout=self._config.connection_timeout) return conn def _return_connection(self, conn: T) -> None: if conn.is_healthy(): try: self._pool.put_nowait(conn) except queue.Full: conn.close() else: conn.close() def prewarm(self, count: int) -> None: for _ in range(min(count, self._config.max_connections)): conn = self._factory() self._pool.put(conn) self._metrics["total_created"] += 1 def close_all(self) -> None: while not self._pool.empty(): try: conn = self._pool.get_nowait() conn.close() except queue.Empty: break for conn in self._in_use.values(): conn.close() @property def metrics(self) -> dict: return { **self._metrics, "available_in_pool": self._pool.qsize(), "in_use": len(self._in_use) } class DatabaseConnection: def __init__(self, db_name: str): self.id = str(uuid4()) self.db_name = db_name self._healthy = True def is_healthy(self) -> bool: return self._healthy def execute(self, query: str) -> dict: return {"query": query, "result": "success"} def close(self) -> None: self._healthy = False pool = ConnectionPool( factory=lambda: DatabaseConnection("main"), config=ConnectionConfig(max_connections=10, min_idle=2) ) pool.prewarm(2) with pool.acquire() as conn: result = conn.execute("SELECT * FROM users") print(f"Result: {result}") print(f"Pool metrics: {pool.metrics}") ``` ### Semaphore-Based Bulkhead ```python import asyncio import threading from typing import Callable from dataclasses import dataclass from datetime import datetime @dataclass class BulkheadConfig: max_concurrent_calls: int max_waiting: int = 0 timeout_seconds: float = 30.0 class ThreadBulkhead: def __init__(self, config: BulkheadConfig): self._semaphore = threading.BoundedSemaphore(config.max_concurrent_calls) self._waiting = 0 self._lock = threading.Lock() self._metrics = { "total_calls": 0, "rejected_calls": 0, "active_calls": 0 } def execute(self, func: Callable, *args, **kwargs): with self._lock: self._metrics["total_calls"] acquired = self._semaphore.acquire(timeout=30) if not acquired: with self._lock: self._metrics["rejected_calls"] += 1 raise BulkheadRejectedError("Concurrent call limit reached") try: with self._lock: self._metrics["active_calls"] += 1 return func(*args, **kwargs) finally: with self._lock: self._metrics["active_calls"] -= 1 self._semaphore.release() @property def metrics(self) -> dict: return self._metrics.copy() class AsyncBulkhead: def __init__(self, config: BulkheadConfig): self._semaphore = asyncio.Semaphore(config.max_concurrent_calls) self._waiting = 0 self._metrics = { "total_calls": 0, "rejected_calls": 0, "active_calls": 0 } async def execute(self, func: Callable, *args, **kwargs): self._metrics["total_calls"] += 1 try: async with asyncio.timeout(30): await self._semaphore.acquire() self._metrics["active_calls"] += 1 try: return await func(*args, **kwargs) finally: self._metrics["active_calls"] -= 1 self._semaphore.release() except asyncio.TimeoutError: self._metrics["rejected_calls"] += 1 raise BulkheadRejectedError("Call timed out") class BulkheadRejectedError(Exception): pass def external_api_call(service_name: str) -> dict: import time time.sleep(0.1) return {"service": service_name, "status": "ok"} bulkhead = ThreadBulkhead(BulkheadConfig(max_concurrent_calls=3)) results = [] for i in range(10): try: result = bulkhead.execute(external_api_call, f"service-{i}") results.append(result) except BulkheadRejectedError: results.append(f"Rejected service-{i}") print(f"Results: {len(results)} calls, {bulkhead.metrics['rejected_calls']} rejected") ``` ### Service Partitioning ```python from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Protocol @dataclass class ServicePartition: name: str max_concurrent: int max_queue: int priority: int class ServiceRouter(ABC): @abstractmethod def get_partition(self, request: 'Request') -> 'ServicePartition': pass class TieredServiceBulkhead: def __init__(self, partitions: list[ServicePartition]): self._partitions = { p.name: { "config": p, "semaphore": asyncio.Semaphore(p.max_concurrent) if hasattr(asyncio, 'Semaphore') else None, "queue": queue.Queue(p.max_queue), "metrics": { "calls": 0, "rejected": 0, "executed": 0 } } for p in partitions } def get_partition_info(self, partition_name: str) -> dict: return self._partitions.get(partition_name, {}) def route_request( self, request: 'Request', router: ServiceRouter ) -> 'PartitionResult': partition = router.get_partition(request) partition_info = self._partitions.get(partition.name) if not partition_info: return PartitionResult(status="unknown_partition") try: partition_info["queue"].put_nowait(request) return PartitionResult(status="queued")
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る