Packages
snakepit
0.3.1
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
High-performance pooler and session manager for external language integrations. Supports Python, Node.js, Ruby, and more with gRPC streaming, session management, and production-ready process cleanup.
Current section
Files
Jump to
Current section
Files
docs/specs/grpc_streaming_examples.md
# gRPC Streaming Examples & Use Cases
## Overview
This document outlines practical streaming examples that showcase the core value proposition of the gRPC bridge redesign. These examples demonstrate capabilities impossible with the current stdin/stdout approach.
## Streaming Categories
### 1. Server Streaming (Most Common)
**Pattern**: Single request → Multiple response chunks
**Use Case**: Long-running computations, progressive results
### 2. Client Streaming (Less Common)
**Pattern**: Multiple request chunks → Single response
**Use Case**: Large dataset uploads, incremental data processing
### 3. Bidirectional Streaming (Advanced)
**Pattern**: Multiple requests ↔ Multiple responses
**Use Case**: Real-time interactions, continuous feedback loops
## Core Streaming Examples
### Example 1: ML Model Inference with Progressive Results
**Scenario**: Run inference on a batch of images, stream results as they complete
```python
# Python gRPC server
def ExecuteStream(self, request, context):
if request.command == "batch_inference":
model_path = request.args["model_path"].decode()
image_paths = json.loads(request.args["image_paths"].decode())
model = load_model(model_path)
for i, image_path in enumerate(image_paths):
# Process each image
image = load_image(image_path)
prediction = model.predict(image)
# Stream individual result
yield snakepit_pb2.StreamResponse(
is_final=False,
chunk={
"image_index": str(i).encode(),
"image_path": image_path.encode(),
"prediction": json.dumps(prediction).encode(),
"confidence": str(prediction.confidence).encode()
},
timestamp=time.time_ns()
)
# Final summary
yield snakepit_pb2.StreamResponse(
is_final=True,
chunk={
"total_processed": str(len(image_paths)).encode(),
"batch_complete": b"true"
},
timestamp=time.time_ns()
)
```
```elixir
# Elixir client
def batch_inference_example() do
worker = get_worker()
# Stream results as they arrive
Snakepit.GRPCWorker.execute_stream(
worker,
"batch_inference",
%{
model_path: "/models/resnet50.pkl",
image_paths: Jason.encode!(["img1.jpg", "img2.jpg", "img3.jpg"])
},
fn chunk ->
if chunk["is_final"] do
IO.puts("✅ Batch complete: #{chunk["total_processed"]} images")
else
IO.puts("📸 Processed #{chunk["image_path"]}: #{chunk["confidence"]}% confidence")
end
end
)
end
```
### Example 2: Large Dataset Processing with Progress Updates
**Scenario**: Process a 1GB CSV file, stream progress and intermediate results
```python
def ExecuteStream(self, request, context):
if request.command == "process_large_dataset":
file_path = request.args["file_path"].decode()
chunk_size = int(request.args.get("chunk_size", b"1000").decode())
total_rows = count_rows(file_path)
processed = 0
for chunk_df in pd.read_csv(file_path, chunksize=chunk_size):
# Process chunk
result = process_dataframe_chunk(chunk_df)
processed += len(chunk_df)
# Stream progress + partial results
yield snakepit_pb2.StreamResponse(
is_final=False,
chunk={
"processed_rows": str(processed).encode(),
"total_rows": str(total_rows).encode(),
"progress_percent": str(round(processed/total_rows*100, 1)).encode(),
"chunk_summary": json.dumps(result.summary()).encode(),
"chunk_stats": json.dumps(result.stats()).encode()
}
)
# Final aggregated results
yield snakepit_pb2.StreamResponse(
is_final=True,
chunk={
"final_stats": json.dumps(aggregate_all_chunks()).encode(),
"processing_complete": b"true"
}
)
```
```elixir
def large_dataset_example() do
worker = get_worker()
Snakepit.GRPCWorker.execute_stream(
worker,
"process_large_dataset",
%{file_path: "/data/huge_dataset.csv", chunk_size: "5000"},
fn chunk ->
if chunk["is_final"] do
IO.puts("🎉 Processing complete!")
IO.puts("Final stats: #{chunk["final_stats"]}")
else
progress = chunk["progress_percent"]
IO.puts("📊 Progress: #{progress}% (#{chunk["processed_rows"]}/#{chunk["total_rows"]} rows)")
end
end
)
end
```
### Example 3: Real-time Log Analysis
**Scenario**: Tail a log file and stream analysis results in real-time
```python
def ExecuteStream(self, request, context):
if request.command == "tail_and_analyze":
log_path = request.args["log_path"].decode()
patterns = json.loads(request.args["error_patterns"].decode())
# Tail the file
with open(log_path, 'r') as f:
f.seek(0, 2) # Go to end
while True:
line = f.readline()
if not line:
time.sleep(0.1)
continue
# Analyze line
analysis = analyze_log_line(line, patterns)
if analysis["has_match"]:
yield snakepit_pb2.StreamResponse(
is_final=False,
chunk={
"timestamp": str(time.time()).encode(),
"log_line": line.encode(),
"severity": analysis["severity"].encode(),
"pattern_matched": analysis["pattern"].encode(),
"context": json.dumps(analysis["context"]).encode()
}
)
```
```elixir
def realtime_log_analysis() do
worker = get_worker()
Snakepit.GRPCWorker.execute_stream(
worker,
"tail_and_analyze",
%{
log_path: "/var/log/app.log",
error_patterns: Jason.encode!(["ERROR", "FATAL", "Exception"])
},
fn chunk ->
severity = chunk["severity"]
pattern = chunk["pattern_matched"]
IO.puts("🚨 [#{severity}] #{pattern}: #{String.slice(chunk["log_line"], 0, 100)}...")
end
)
end
```
### Example 4: Distributed Training Progress
**Scenario**: Monitor distributed ML training across multiple nodes
```python
def ExecuteStream(self, request, context):
if request.command == "distributed_training":
config = json.loads(request.args["training_config"].decode())
trainer = DistributedTrainer(config)
trainer.start()
for epoch_results in trainer.train_with_progress():
yield snakepit_pb2.StreamResponse(
is_final=False,
chunk={
"epoch": str(epoch_results.epoch).encode(),
"train_loss": str(epoch_results.train_loss).encode(),
"val_loss": str(epoch_results.val_loss).encode(),
"train_acc": str(epoch_results.train_accuracy).encode(),
"val_acc": str(epoch_results.val_accuracy).encode(),
"learning_rate": str(epoch_results.lr).encode(),
"time_elapsed": str(epoch_results.time_elapsed).encode(),
"gpu_memory": json.dumps(epoch_results.gpu_stats).encode()
}
)
# Training complete
yield snakepit_pb2.StreamResponse(
is_final=True,
chunk={
"final_model_path": trainer.save_model().encode(),
"total_epochs": str(trainer.epochs_completed).encode(),
"best_val_acc": str(trainer.best_val_accuracy).encode()
}
)
```
### Example 5: Financial Data Pipeline
**Scenario**: Stream real-time stock analysis with multiple indicators
```python
def ExecuteStream(self, request, context):
if request.command == "realtime_stock_analysis":
symbols = json.loads(request.args["symbols"].decode())
indicators = json.loads(request.args["indicators"].decode())
stream = MarketDataStream(symbols)
analyzer = TechnicalAnalyzer(indicators)
for market_data in stream:
analysis = analyzer.analyze(market_data)
yield snakepit_pb2.StreamResponse(
is_final=False,
chunk={
"symbol": market_data.symbol.encode(),
"price": str(market_data.price).encode(),
"volume": str(market_data.volume).encode(),
"rsi": str(analysis.rsi).encode(),
"macd": str(analysis.macd).encode(),
"signal": analysis.trading_signal.encode(),
"timestamp": str(market_data.timestamp).encode()
}
)
```
## Implementation Timeline
### Phase 1: Basic Streaming Infrastructure (Day 1-2)
- [ ] Protocol buffer definitions with streaming support
- [ ] Basic Elixir streaming client wrapper
- [ ] Python streaming server foundation
- [ ] Simple "ping stream" example (heartbeat every second)
### Phase 2: ML/Data Processing Examples (Day 3-4)
- [ ] **Example 1**: Batch inference streaming
- [ ] **Example 2**: Large dataset processing with progress
- [ ] Performance comparison vs current blocking approach
### Phase 3: Real-time Examples (Day 5-6)
- [ ] **Example 3**: Real-time log analysis
- [ ] **Example 4**: Training progress monitoring
- [ ] **Example 5**: Financial data pipeline
- [ ] Documentation and integration tests
## Example Applications
### Data Science Workflows
```elixir
# Process multiple datasets concurrently with progress tracking
datasets = ["sales_q1.csv", "sales_q2.csv", "sales_q3.csv", "sales_q4.csv"]
tasks = Enum.map(datasets, fn dataset ->
Task.async(fn ->
Snakepit.GRPCWorker.execute_stream(
worker,
"process_large_dataset",
%{file_path: dataset},
&handle_progress/1
)
end)
end)
Task.await_many(tasks)
```
### ML Model Serving
```elixir
# Real-time inference pipeline
Snakepit.GRPCWorker.execute_stream(
worker,
"realtime_inference",
%{model: "fraud_detection", input_stream: "transactions"},
fn result ->
if result["fraud_probability"] > 0.8 do
alert_fraud_team(result)
end
end
)
```
### DevOps Monitoring
```elixir
# Monitor multiple log files simultaneously
log_files = ["/var/log/app.log", "/var/log/db.log", "/var/log/nginx.log"]
Enum.each(log_files, fn log_file ->
Task.start(fn ->
Snakepit.GRPCWorker.execute_stream(
worker,
"tail_and_analyze",
%{log_path: log_file},
&handle_log_alert/1
)
end)
end)
```
## Success Metrics
### Functional Requirements
- [ ] Can stream ML inference results progressively
- [ ] Can process large datasets with real-time progress updates
- [ ] Can handle real-time data streams (logs, financial data, etc.)
- [ ] Maintains session affinity during streaming operations
### Performance Requirements
- [ ] Sub-100ms latency for streaming chunk delivery
- [ ] Can handle 1000+ chunks per stream without memory leaks
- [ ] Graceful handling of slow consumers (backpressure)
- [ ] Proper cleanup when streams are cancelled
### Developer Experience
- [ ] Simple callback-based API in Elixir
- [ ] Clear examples for each streaming pattern
- [ ] Good error messages when streams fail
- [ ] Easy debugging of streaming operations
## Migration Benefits Over Current Approach
**Current Limitations:**
- ❌ No way to get progress updates on long operations
- ❌ Large responses consume memory until complete
- ❌ No real-time data processing capabilities
- ❌ Poor user experience for long-running tasks
**gRPC Streaming Benefits:**
- ✅ Progressive results and real-time feedback
- ✅ Constant memory usage regardless of response size
- ✅ True real-time processing capabilities
- ✅ Better user experience with progress indicators
- ✅ Can cancel long-running operations mid-stream
---
**Bottom Line**: These streaming examples demonstrate that gRPC isn't just "better MessagePack" - it enables entirely new classes of applications that are impossible with the current architecture.