Skip to main content

A simple pool connection manager based off of Netflix's Dyno package, but for Python

Project description

PyDyno Logo

PyDyno

Unified connection pooling for Python, inspired by Netflix's Dyno

PyDyno is a modern, async-first connection pooling library that provides a unified interface for managing connections to databases, caches, and HTTP services. Built with attrs and designed for production use.

Python 3.8+ MIT License Built with attrs

๐Ÿš€ Quick Start

import asyncio
from pydyno import PyDyno, PoolConfig
from pydyno.adapters.postgresql import PostgreSQLAdapter

async def main():
    # Create PyDyno manager
    dyno = PyDyno()
    
    # Configure PostgreSQL pool
    config = {
        'host': 'localhost',
        'user': 'postgres',
        'password': 'password',
        'database': 'mydb'
    }
    
    adapter = PostgreSQLAdapter(
        name="main_db",
        service_type="postgresql",
        config=config,
        pool_config=PoolConfig(max_connections=20)
    )
    
    # Add pool to manager
    await dyno.create_pool("main_db", adapter)
    
    # Use the pool
    pool = dyno.get_pool("main_db")
    async with pool.session_scope() as session:
        result = await session.execute(text("SELECT version()"))
        print(result.scalar())
    
    # Cleanup
    await dyno.close_all()

asyncio.run(main())

โœจ Features

  • ๐Ÿ”„ Unified Interface: One consistent API for all service types
  • โšก Async-First: Built for modern async Python applications
  • ๐Ÿ“Š Built-in Metrics: Track requests, response times, and health
  • ๐Ÿฅ Health Monitoring: Automatic background health checks
  • ๐Ÿ›ก๏ธ Production Ready: Robust error handling and connection recovery
  • ๐Ÿ”ง Highly Configurable: Fine-tune connection pools for your needs
  • ๐Ÿ“ฆ Clean Architecture: Easy to extend with new adapters

๐Ÿ“‹ Supported Services

Service Status Adapter
PostgreSQL โœ… Ready PostgreSQLAdapter
Redis ๐Ÿšง Planned RedisAdapter
HTTP APIs ๐Ÿšง Planned HTTPAdapter
Kafka ๐Ÿšง Planned KafkaAdapter

๐Ÿ› ๏ธ Installation

# Basic installation
pip install pydyno

# With PostgreSQL support
pip install pydyno[postgresql]

# Development installation
git clone https://github.com/yourusername/pydyno.git
cd pydyno
pip install -e .

๐Ÿ“– Documentation

Basic Concepts

PyDyno Manager: Central coordinator that manages multiple connection pools Adapters: Service-specific implementations (PostgreSQL, Redis, etc.) Pool Config: Configuration for connection pool behavior Metrics: Built-in monitoring and performance tracking

Configuration

from pydyno.core.pool_config import PoolConfig

# Customize pool behavior
pool_config = PoolConfig(
    max_connections=20,        # Maximum connections in pool
    min_connections=2,         # Minimum connections to maintain
    max_overflow=30,           # Additional connections beyond max
    timeout=30.0,              # Connection timeout in seconds
    pool_recycle=3600,         # Recycle connections after 1 hour
    pool_pre_ping=True,        # Verify connections before use
    retry_attempts=3,          # Retry failed operations
    health_check_interval=60.0, # Health check frequency
    echo=False                 # Log SQL queries (PostgreSQL)
)

PostgreSQL Adapter

from pydyno.adapters.postgresql import PostgreSQLAdapter, create_postgresql_adapter

# Method 1: Direct creation
adapter = PostgreSQLAdapter(
    name="my_db",
    service_type="postgresql",
    config={
        'host': 'localhost',
        'port': 5432,
        'user': 'postgres',
        'password': 'password',
        'database': 'myapp'
    },
    pool_config=PoolConfig(max_connections=10)
)

# Method 2: From environment variables
# Set: POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
adapter = create_postgresql_adapter("my_db")

# Method 3: From DATABASE_URL
# Set: DATABASE_URL=postgresql://user:pass@host:5432/db
adapter = create_postgresql_adapter("my_db")

Usage Examples

FastAPI Integration

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from pydyno import PyDyno
from pydyno.adapters.postgresql import create_postgresql_adapter

# Global PyDyno instance
dyno = PyDyno()

async def startup_event():
    """Initialize database pool on startup"""
    adapter = create_postgresql_adapter("main_db")
    await dyno.create_pool("main_db", adapter)

async def shutdown_event():
    """Cleanup on shutdown"""
    await dyno.close_all()

# FastAPI dependency
async def get_db_session() -> AsyncSession:
    """Get database session for routes"""
    pool = dyno.get_pool("main_db")
    return await pool.get_session()

# Use in routes
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db_session)):
    result = await db.execute(select(User).where(User.id == user_id))
    return result.scalar_one_or_none()

# FastAPI app setup
app = FastAPI()
app.add_event_handler("startup", startup_event)
app.add_event_handler("shutdown", shutdown_event)

Session Management

# Automatic transaction management
async with adapter.session_scope() as session:
    # Create user
    user = User(name="John", email="john@example.com")
    session.add(user)
    
    # Update user (same transaction)
    user.last_login = datetime.utcnow()
    
    # Automatically commits on success, rolls back on error

# Raw SQL queries
result = await adapter.execute_scalar("SELECT COUNT(*) FROM users")
print(f"Total users: {result}")

# Query with parameters
users = await adapter.execute_query(
    "SELECT * FROM users WHERE created_at > :date",
    {"date": datetime(2024, 1, 1)}
)

Health Monitoring

# Check health of all pools
health_results = await dyno.health_check()
print(health_results)  # {'main_db': True, 'cache': True}

# Check specific pool
is_healthy = await dyno.health_check("main_db")

# Get detailed metrics
metrics = await dyno.get_metrics_dict()
for pool_name, pool_metrics in metrics.items():
    print(f"Pool: {pool_name}")
    print(f"  Total requests: {pool_metrics['total_requests']}")
    print(f"  Success rate: {pool_metrics['success_rate']:.1f}%")
    print(f"  Avg response time: {pool_metrics['average_response_time']:.3f}s")
    print(f"  Health: {pool_metrics['health_status']}")

Multiple Database Pools

async def setup_multiple_databases():
    dyno = PyDyno()
    
    # Primary database
    primary_adapter = PostgreSQLAdapter(
        name="primary",
        service_type="postgresql",
        config={"url": "postgresql://user:pass@primary-db:5432/app"},
        pool_config=PoolConfig(max_connections=20)
    )
    
    # Analytics database (read-only)
    analytics_adapter = PostgreSQLAdapter(
        name="analytics",
        service_type="postgresql", 
        config={"url": "postgresql://user:pass@analytics-db:5432/analytics"},
        pool_config=PoolConfig(max_connections=5, echo=True)
    )
    
    # Add both pools
    await dyno.create_pool("primary", primary_adapter)
    await dyno.create_pool("analytics", analytics_adapter)
    
    return dyno

# Use different databases
async def get_user_analytics(dyno: PyDyno, user_id: int):
    # Write to primary
    primary = dyno.get_pool("primary")
    async with primary.session_scope() as session:
        user = User(id=user_id, name="John")
        session.add(user)
    
    # Read from analytics
    analytics = dyno.get_pool("analytics")
    result = await analytics.execute_scalar(
        "SELECT COUNT(*) FROM user_events WHERE user_id = :user_id",
        {"user_id": user_id}
    )
    
    return result

๐Ÿงช Testing

# Run basic functionality tests (no database required)
python test_pydyno_basic.py

# Run database tests (requires PostgreSQL)
export POSTGRES_HOST=localhost
export POSTGRES_USER=postgres
export POSTGRES_PASSWORD=password
export POSTGRES_DB=test_db
python test_pydyno_database.py

๐Ÿ—๏ธ Architecture

PyDyno follows a clean, extensible architecture:

pydyno/
โ”œโ”€โ”€ core/
โ”‚   โ”œโ”€โ”€ manager.py          # PyDyno main manager
โ”‚   โ”œโ”€โ”€ adapters.py         # Base adapter interface
โ”‚   โ”œโ”€โ”€ pool_config.py      # Configuration classes
โ”‚   โ”œโ”€โ”€ utils.py            # Metrics and utilities
โ”‚   โ””โ”€โ”€ exceptions.py       # Custom exceptions
โ””โ”€โ”€ adapters/
    โ”œโ”€โ”€ postgresql.py       # PostgreSQL adapter
    โ”œโ”€โ”€ redis.py           # Redis adapter (planned)
    โ””โ”€โ”€ http.py            # HTTP adapter (planned)

Creating Custom Adapters

from pydyno.core.adapters import ConnectionAdapter

class CustomAdapter(ConnectionAdapter):
    """Custom service adapter"""
    
    async def initialize(self):
        """Set up your service connection pool"""
        # Initialize your client/connection pool
        self._client = YourServiceClient(
            **self.config,
            max_connections=self.pool_config.max_connections
        )
        self._initialized = True
    
    async def health_check(self) -> bool:
        """Check service health"""
        try:
            await self._client.ping()
            self.metrics.record_health_check(True)
            return True
        except Exception:
            self.metrics.record_health_check(False)
            return False
    
    async def close(self):
        """Clean up resources"""
        if self._client:
            await self._client.close()
        self._closed = True

๐ŸŽฏ Why PyDyno?

The Problem

Modern applications often need to connect to multiple services:

  • Primary database (PostgreSQL)
  • Cache layer (Redis)
  • External APIs (HTTP)
  • Message queues (Kafka)

Each service has its own connection pooling mechanism, configuration format, and management approach. This leads to:

  • Inconsistent APIs across your codebase
  • Scattered configuration and monitoring
  • Duplicate connection management logic
  • No unified health checking

The Solution

PyDyno provides a single, unified interface for all your connection pools:

  • โœ… One API for all services
  • โœ… Consistent configuration patterns
  • โœ… Unified metrics and monitoring
  • โœ… Centralized health checking
  • โœ… Production-ready error handling

Inspired by Netflix

Netflix's Dyno library solved this problem for Java applications at massive scale. PyDyno brings these same architectural patterns to Python, adapted for modern async applications.

๐Ÿ”ฎ Roadmap

v0.2.0 - Redis Support

  • Redis connection adapter
  • Pub/Sub support
  • Redis Cluster support

v0.3.0 - HTTP Client Pooling

  • HTTP adapter for API calls
  • Load balancing strategies
  • Circuit breaker pattern

v0.4.0 - Advanced Features

  • Kafka adapter
  • Service discovery integration
  • Prometheus metrics export

v1.0.0 - Production Ready

  • Comprehensive test suite
  • Performance optimizations
  • Full documentation
  • Stability guarantees

๐Ÿค Contributing

We welcome contributions! Areas where help is needed:

  1. New Adapters: Redis, HTTP, Kafka, MongoDB
  2. Testing: More test cases and edge cases
  3. Documentation: Examples and tutorials
  4. Performance: Benchmarks and optimizations
# Development setup
git clone https://github.com/yourusername/pydyno.git
cd pydyno
pip install -e ".[dev]"

# Run tests
python test_pydyno_basic.py

# Code formatting
black src/
isort src/

๐Ÿ“„ License

MIT License - see LICENSE file for details.

๐Ÿ™ Acknowledgments

  • Inspired by Netflix's Dyno library
  • Built with attrs for clean Python classes
  • Uses SQLAlchemy for PostgreSQL support

๐Ÿ“ž Support


PyDyno - Making connection pooling simple, unified, and powerful. ๐Ÿš€

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydyno_pool-0.1.0.tar.gz (1.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydyno_pool-0.1.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file pydyno_pool-0.1.0.tar.gz.

File metadata

  • Download URL: pydyno_pool-0.1.0.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.14

File hashes

Hashes for pydyno_pool-0.1.0.tar.gz
Algorithm Hash digest
SHA256 17bf5ffc44d79d4b79a1b52b0161f9a48866e954e77b71e88ab3c0c71be8443c
MD5 7f84ca2e6a1e268a64229ce3f18d5f6c
BLAKE2b-256 b4c2c738d99a43e27e5e003f3fd2e9c3642c1551a5afe1e88d2f7366d7f211c9

See more details on using hashes here.

File details

Details for the file pydyno_pool-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pydyno_pool-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ebb59ed68fd54296b573c5ee7e1e043cbefb485ed0c28c4c32d0e36e88d4dbef
MD5 e9038734801b58e2e23127a7b2c61337
BLAKE2b-256 1174df367314dd2cbd4accd61ed441f103ff2c7e64b9c9522e63c16395fb3ceb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page