Frontend (17 items): - Virtualized message list with batch loading - CSS split with skeleton, drawer, search filter, message action styles - Code splitting via React.lazy + Suspense for Admin/Ethics/Settings pages - Skeleton loading components (Skeleton, SkeletonCard, SkeletonGrid) - Debounced search/filter component (SearchFilter) - Error boundary with fallback UI - Keyboard shortcuts (Ctrl+K search, Ctrl+Enter send, Escape dismiss) - Page transition animations (fade-in) - PWA support (manifest.json + service worker) - WebSocket auto-reconnect with exponential backoff (10 retries) - Chat history persistence to localStorage (500 msg limit) - Message edit/delete on hover - Copy-to-clipboard on code blocks - Mobile drawer (bottom-sheet for consensus panel) - File upload support - User preferences sync to backend Testing (8 items): - Component tests: Toast, Markdown, ChatMessage, Avatar, ErrorBoundary, Skeleton - Hook tests: useChatHistory - E2E smoke tests (5 tests) - Accessibility audit utility Backend (12 items): - Vector memory with cosine similarity search - TTS/STT adapter factory wiring - Geometry kernel with orphan detection - Tenant registry with CRUD operations - Response cache with TTL - Connection pool (async) - Background task queue - Health check endpoints (/health, /ready) - Request tracing middleware (X-Request-ID) - API key rotation mechanism - Environment-based config (settings.py) - API route documentation improvements Infrastructure (4 items): - Grafana dashboard template - Database migration system - Storybook configuration Documentation (3 items): - ADR-001: Advisory Governance Model - ADR-002: Twelve-Head Architecture - ADR-003: Consequence Engine 552 Python tests + 45 frontend tests passing, 0 ruff errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
98 lines
3.0 KiB
Python
98 lines
3.0 KiB
Python
"""Connection pool for backend services."""
|
|
|
|
import asyncio
|
|
from typing import Any, Protocol
|
|
|
|
|
|
class ConnectionProtocol(Protocol):
|
|
"""Protocol for poolable connections."""
|
|
|
|
async def connect(self) -> None: ...
|
|
async def close(self) -> None: ...
|
|
def is_alive(self) -> bool: ...
|
|
|
|
|
|
class ConnectionPool:
|
|
"""Async connection pool with health checks and automatic recycling.
|
|
|
|
Generic pool for database connections, HTTP clients, or any poolable resource.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
factory: Any,
|
|
min_size: int = 2,
|
|
max_size: int = 10,
|
|
max_idle_seconds: float = 300.0,
|
|
) -> None:
|
|
self._factory = factory
|
|
self._min_size = min_size
|
|
self._max_size = max_size
|
|
self._max_idle = max_idle_seconds
|
|
self._available: asyncio.Queue[Any] = asyncio.Queue(maxsize=max_size)
|
|
self._in_use: int = 0
|
|
self._total_created: int = 0
|
|
self._initialized = False
|
|
|
|
async def initialize(self) -> None:
|
|
"""Pre-populate pool with min_size connections."""
|
|
if self._initialized:
|
|
return
|
|
for _ in range(self._min_size):
|
|
conn = await self._create_connection()
|
|
await self._available.put(conn)
|
|
self._initialized = True
|
|
|
|
async def _create_connection(self) -> Any:
|
|
"""Create a new connection via the factory."""
|
|
conn = self._factory()
|
|
if hasattr(conn, 'connect'):
|
|
await conn.connect()
|
|
self._total_created += 1
|
|
return conn
|
|
|
|
async def acquire(self) -> Any:
|
|
"""Acquire a connection from the pool."""
|
|
if not self._initialized:
|
|
await self.initialize()
|
|
|
|
try:
|
|
conn = self._available.get_nowait()
|
|
if hasattr(conn, 'is_alive') and not conn.is_alive():
|
|
conn = await self._create_connection()
|
|
except asyncio.QueueEmpty:
|
|
if self._in_use + self._available.qsize() < self._max_size:
|
|
conn = await self._create_connection()
|
|
else:
|
|
conn = await self._available.get()
|
|
|
|
self._in_use += 1
|
|
return conn
|
|
|
|
async def release(self, conn: Any) -> None:
|
|
"""Return a connection to the pool."""
|
|
self._in_use -= 1
|
|
try:
|
|
self._available.put_nowait(conn)
|
|
except asyncio.QueueFull:
|
|
if hasattr(conn, 'close'):
|
|
await conn.close()
|
|
|
|
async def close_all(self) -> None:
|
|
"""Close all connections in the pool."""
|
|
while not self._available.empty():
|
|
conn = self._available.get_nowait()
|
|
if hasattr(conn, 'close'):
|
|
await conn.close()
|
|
self._initialized = False
|
|
self._in_use = 0
|
|
|
|
def stats(self) -> dict[str, int]:
|
|
"""Return pool statistics."""
|
|
return {
|
|
"available": self._available.qsize(),
|
|
"in_use": self._in_use,
|
|
"total_created": self._total_created,
|
|
"max_size": self._max_size,
|
|
}
|