Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions BLUE_GREEN_STRATEGY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Blue-Green Deployment Strategy Support

## Overview

The e6data Python connector now supports automatic detection and handling of blue-green deployments on the server side. When the server is deployed using a blue-green strategy, the connector will automatically detect which deployment (blue or green) is active and route all requests accordingly.

## How It Works

### Strategy Detection

1. **Initial Detection**: On the first authentication request, the connector tries to determine the active strategy by attempting authentication with both "blue" and "green" strategies.

2. **Strategy Header**: The connector adds a `strategy` header to all gRPC requests with the detected value ("blue" or "green").

3. **Graceful Transition**: When the server is updated, it includes a `new_strategy` field in query responses (prepare, execute, fetch, status). This allows:
- Current queries to complete with their original strategy
- New queries to use the updated strategy
- No interruption to in-flight queries

4. **Error Handling**: If a request fails with a 456 error code (indicating wrong strategy), the connector automatically:
- Clears the cached strategy
- Re-detects the correct strategy
- Retries the request with the new strategy

### Caching Mechanism

- The detected strategy is cached for 5 minutes (configurable via `STRATEGY_CACHE_TIMEOUT`)
- The cache is thread-safe and process-safe using `threading.Lock` and `multiprocessing.Manager`
- If multiprocessing.Manager is not available, it falls back to thread-local storage

### Automatic Retry

The connector includes automatic retry logic for:
- Authentication failures (existing behavior)
- Strategy mismatches (new behavior)

## Implementation Details

### Key Components

1. **Global Strategy Storage**:
```python
_shared_strategy = {
'active_strategy': 'blue' or 'green' or None,
'last_check_time': timestamp,
'pending_strategy': 'blue' or 'green' or None, # Next strategy to use
'query_strategy_map': {query_id: strategy} # Per-query strategy tracking
}
```

2. **Strategy Functions**:
- `_get_active_strategy()`: Returns the cached strategy if valid
- `_set_active_strategy(strategy)`: Updates the cached strategy
- `_clear_strategy_cache()`: Forces re-detection on next request
- `_set_pending_strategy(strategy)`: Sets strategy for future queries
- `_apply_pending_strategy()`: Applies pending strategy after query completion
- `_register_query_strategy(query_id, strategy)`: Tracks strategy per query
- `_get_query_strategy(query_id)`: Gets strategy for specific query
- `_cleanup_query_strategy(query_id)`: Removes completed query tracking

3. **Modified gRPC Headers**:
All gRPC requests now include the strategy header when available:
```python
metadata = [
('plannerip', engine_ip),
('cluster-uuid', cluster_uuid),
('strategy', 'blue' or 'green') # New header
]
```

4. **Response Handling**:
The connector checks for `new_strategy` in ALL API responses:
- AuthenticateResponse
- PrepareStatementResponse
- ExecuteStatementResponse
- GetNextResultBatchResponse
- GetNextResultRowResponse
- GetResultMetadataResponse
- StatusResponse
- ClearResponse
- ClearOrCancelQueryResponse
- CancelQueryResponse
- GetTablesResponse
- GetSchemaNamesResponse
- GetColumnsResponse
- ExplainResponse
- ExplainAnalyzeResponse
- DryRunResponse

## Usage

No changes are required in your application code. The connector handles strategy detection automatically:

```python
from e6data_python_connector import Connection

# Create connection as usual
conn = Connection(
host='your-host',
port=80,
username='your-email',
password='your-token',
database='your-db',
catalog='your-catalog'
)

# Use the connection normally
cursor = conn.cursor()
cursor.execute("SELECT * FROM your_table")
results = cursor.fetchall()
```

## Logging

The connector logs strategy-related events at INFO level:
- Strategy detection attempts
- Successful strategy detection
- Strategy cache clearing
- Strategy change detection

Enable logging to see these messages:
```python
import logging
logging.basicConfig(level=logging.INFO)
```

## Testing

Run the included test script to verify the implementation:

```bash
# Set environment variables
export ENGINE_IP=your-engine-ip
export DB_NAME=your-database
export EMAIL=your-email
export PASSWORD=your-token
export CATALOG=your-catalog
export PORT=80

# Run tests
python test_strategy.py
```

## Performance Considerations

- Strategy detection only occurs on the first request or after cache expiry
- Subsequent requests use the cached strategy with minimal overhead
- The cache timeout (5 minutes) balances between performance and responsiveness to strategy changes
- Thread-safe implementation ensures correct behavior in multi-threaded applications
- Process-safe implementation supports multi-process deployments

## Error Scenarios

1. **Both strategies fail**: If neither "blue" nor "green" strategy works, the original error is raised
2. **Strategy change during operation**: Automatically detected via 456 error and handled transparently
3. **Network issues**: Existing retry logic continues to work as before
4. **Graceful transition**: When server sends `new_strategy` in response:
- Current queries continue with their original strategy
- New queries use the updated strategy after the current query completes
- No queries are interrupted or fail due to strategy changes

## Configuration

Currently, the strategy cache timeout is hardcoded to 5 minutes. If needed, this can be made configurable in future versions through the `grpc_options` parameter.
149 changes: 149 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is the e6data Python Connector - a DB-API 2.0 compliant database connector for the e6data distributed SQL Engine. The connector uses gRPC for communication with e6data clusters and provides SQLAlchemy dialect support.

### Key Features
- DB-API 2.0 compliant interface
- gRPC-based communication with SSL/TLS support
- SQLAlchemy dialect integration
- Blue-green deployment strategy support with automatic failover
- Thread-safe and process-safe operation
- Automatic retry and re-authentication logic

## Common Development Commands

### Building and Installing
```bash
# Install development dependencies
pip install -r requirements.txt

# Install the package in development mode
pip install -e .

# Build distribution packages
python setup.py sdist bdist_wheel

# Upload to PyPI (requires credentials)
twine upload dist/*
```

### Running Tests
```bash
# Run tests using unittest (requires environment variables)
# Set these environment variables first:
# - ENGINE_IP: IP address of the e6data engine
# - DB_NAME: Database name
# - EMAIL: Your e6data email
# - PASSWORD: Access token from e6data console
# - CATALOG: Catalog name
# - PORT: Port number (default: 80)

# Run all tests
python -m unittest tests.py tests_grpc.py

# Run specific test file
python -m unittest tests.py
python -m unittest tests_grpc.py
```

### Protocol Buffer Compilation
```bash
# Install protobuf compiler
pip install grpcio-tools

# Regenerate gRPC code from proto files (if proto files change)
python -m grpc_tools.protoc -I. --python_out=e6data_python_connector/server --grpc_python_out=e6data_python_connector/server e6x_engine.proto
python -m grpc_tools.protoc -I. --python_out=e6data_python_connector/cluster_server --grpc_python_out=e6data_python_connector/cluster_server cluster.proto
```

### Testing Blue-Green Strategy
```bash
# Start mock server (in one terminal)
python mock_grpc_server.py

# Run test client (in another terminal)
python test_mock_server.py

# Or use the convenience script
./run_mock_test.sh
```

## Architecture Overview

### Core Components

1. **Connection Management (`e6data_grpc.py`)**
- Main `Connection` class implementing DB-API 2.0 interface
- Handles gRPC channel creation (secure/insecure)
- Authentication using email/password (access token)
- Connection pooling and retry logic

2. **Cursor Implementation (`e6data_grpc.py`)**
- `GRPCCursor` class for query execution
- Supports parameterized queries using `pyformat` style
- Fetch operations: `fetchone()`, `fetchmany()`, `fetchall()`, `fetchall_buffer()`
- Query analysis with `explain_analyse()`

3. **gRPC Services**
- **Query Engine Service** (`server/`): Main query execution interface
- **Cluster Service** (`cluster_server/`): Cluster management operations
- Both use Protocol Buffers for message serialization

4. **SQLAlchemy Integration (`dialect.py`)**
- Custom dialect registered as `e6data+e6data_python_connector`
- Enables use with SQLAlchemy ORM and query builder

5. **Type System**
- `typeId.py`: Type mapping between e6data and Python types
- `date_time_utils.py`: Date/time handling utilities
- `datainputstream.py`: Binary data deserialization

### Key Design Patterns

1. **Error Handling**: Automatic retry with re-authentication for gRPC errors
2. **Resource Management**: Proper cleanup with `clear()`, `close()` methods
3. **Memory Efficiency**: `fetchall_buffer()` returns generator for large datasets
4. **Security**: SSL/TLS support for secure connections
5. **Blue-Green Deployment**:
- Automatic strategy detection and switching
- Graceful transitions without query interruption
- Thread-safe and process-safe strategy caching
- 456 error handling for strategy mismatches

### Configuration Options

The connector supports extensive gRPC configuration through `grpc_options`:
- Message size limits
- Keepalive settings
- Timeout configurations
- HTTP/2 ping settings

See TECH_DOC.md for detailed gRPC options documentation.

## Important Notes

- Always use environment variables for credentials in tests
- The connector requires network access to e6data clusters
- Port 80 must be open for inbound connections
- Tests require a running e6data cluster with valid credentials
- When modifying proto files, regenerate the Python code
- Follow DB-API 2.0 specification for any API changes
- Blue-green strategy is handled automatically - no code changes required
- All API responses now include optional `new_strategy` field
- Strategy transitions happen after query completion (on clear/cancel)

## Blue-Green Deployment Strategy

The connector automatically handles blue-green deployments:

1. **Initial Detection**: On first connection, tries both strategies
2. **Header Injection**: Adds "strategy" header to all gRPC requests
3. **Graceful Transition**: Current queries complete with old strategy
4. **Automatic Failover**: Handles 456 errors with strategy retry
5. **Caching**: 5-minute cache timeout for performance

See `BLUE_GREEN_STRATEGY.md` for detailed documentation.
Loading