gRPC Architecture & Design
This document explains the architecture and design patterns behind Django-CFG’s gRPC integration.
🏗️ System Architecture
High-Level Overview
🔄 Request Flow Architecture
Complete Request Lifecycle
Error Flow
🧩 Component Architecture
1. Interceptors Pipeline
Architecture Update (v1.5+): Interceptors have been consolidated into ObservabilityInterceptor to fix bidirectional streaming bugs. The old 5-layer architecture caused premature StopAsyncIteration after ~15 messages.
Current Architecture (Recommended)
Why ObservabilityInterceptor?
Each interceptor wraps request_iterator in an async generator. With 5 separate interceptors, buffer backpressure caused premature StopAsyncIteration in bidirectional streaming after ~15 messages.
Solution: Consolidate all observability features into ONE interceptor with a SINGLE counting_iterator().
ObservabilityInterceptor Features
| Feature | Description | Configuration |
|---|---|---|
| Metrics | Request counts, response times, error rates | Always ON |
| Logging | Structured console logging | Always ON |
| DB Logging | GRPCRequestLog entries | observability.log_to_db |
| Centrifugo | WebSocket event publishing | Auto-detected from config |
| Error Handling | Map exceptions to gRPC status | Built-in |
Configuration
from django_cfg import GRPCConfig, GRPCObservabilityConfig
grpc = GRPCConfig(
enabled=True,
observability=GRPCObservabilityConfig(
log_to_db=True, # Enable DB logging
log_streaming=False, # Don't log bidi streams (creates 'pending' entries)
log_errors_only=False, # Log all requests, not just errors
sampling_rate=1.0, # Log 100% of requests
),
)Key Design Decisions:
- Auth First - User context available for all observability features
- Single Observability Interceptor - Eliminates generator nesting bug
- Auto-Configuration - Centrifugo auto-detected from django-cfg config
- Streaming-Safe - No timeout on
anext(), uses ping/keepalive instead
2. Service Discovery System
3. Base Service Classes Hierarchy
🔐 Authentication Architecture
API Key Flow
Public vs Protected Methods
📊 Monitoring Architecture
Request Logging System
Statistics & Metrics
🔄 Phase 4: Dynamic Invocation
Dynamic gRPC Client Architecture
Key Features:
- Service Discovery - Automatically discover all available services
- Method Introspection - Get method signatures and schemas
- Dynamic Message Creation - Create protobuf messages from JSON/dict
- No Proto Files Required - Use reflection to understand service contracts
Learn more: Dynamic Invocation Guide
🎯 Design Patterns
1. Interceptor Pattern
Purpose: Cross-cutting concerns (logging, auth, metrics) without modifying service code.
Implementation:
class MyInterceptor(grpc.ServerInterceptor):
def intercept_service(self, continuation, handler_call_details):
# Pre-processing
# ...
# Call next interceptor/service
response = continuation(handler_call_details)
# Post-processing
# ...
return response2. Service Discovery Pattern
Purpose: Automatic registration of gRPC services from Django apps.
Key Components:
- Module scanning (grpc_services.py, grpc_handlers.py)
- Lazy loading support
- Convention over configuration
3. Django Integration Pattern
Purpose: Seamless access to Django features in gRPC services.
Integration Points:
- ORM via models
- User authentication via API keys
- Permissions via Django auth
- Admin interface for monitoring
- Signals for events
4. Error Mapping Pattern
Purpose: Convert Django exceptions to appropriate gRPC status codes.
Mapping:
Django Exception → gRPC Status Code
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ValidationError → INVALID_ARGUMENT
ObjectDoesNotExist → NOT_FOUND
PermissionDenied → PERMISSION_DENIED
NotImplementedError → UNIMPLEMENTED
TimeoutError → DEADLINE_EXCEEDED
Exception → INTERNALBidirectional Streaming Architecture
For long-running bidirectional connections, use BidirectionalStreamingService:
Key Components
| Component | Responsibility |
|---|---|
InputProcessor | Reads messages via anext(), routes to handlers |
OutputProcessor | Sends commands and ping/pong messages |
ConnectionManager | Tracks client state, handles cleanup |
ResponseRegistry | Maps command IDs to pending responses |
Critical Configuration
Never set connection_timeout - it causes premature StopAsyncIteration in grpcio. See Troubleshooting.
BidirectionalStreamingConfig(
streaming_mode=StreamingMode.ANEXT,
ping_strategy=PingStrategy.INTERVAL,
ping_interval=5.0,
ping_timeout=180.0,
connection_timeout=None, # CRITICAL!
)Learn more: Streaming Patterns
Performance Considerations
Threading Model
Configuration:
GRPCServerConfig(
max_workers=10, # Thread pool size
# More workers = more concurrent requests
# But higher memory usage
)Database Optimization
- Connection Pooling - Reuse database connections
- Select Related / Prefetch Related - Reduce N+1 queries
- Indexed Fields - Fast lookups on service/method/status
- Async Logging - Non-blocking request logging
Related Documentation
- Getting Started - Configure gRPC in your project
- Streaming Patterns - Bidirectional streaming patterns
- Troubleshooting - Common issues and solutions
- Configuration - Configure gRPC settings
- FAQ - Common questions and answers
Next: Learn how to get started with gRPC or explore streaming patterns.