Production
Deploy, monitor, and scale Sandy’s franchise.
Sandy has 4 locations, 100+ orders per hour, and no visibility into what’s happening. Which location is slowest? Which orders are failing? She needs a dashboard.
evo install dashboard
from evoid_dashboard import create_dashboard
# Monitoring UI at http://localhost:8001
create_dashboard(port=8001)
The dashboard shows: service map, all registered Intents, message bus history, database connections, system info. Sandy can see every location’s health in one place.
Production Config
# evoid.toml
[project]
name = "sandy-franchise"
version = "1.0.0"
[runtime]
adapter = "asgi"
host = "0.0.0.0"
port = 8000
[engines]
schema = "native"
storage = "sqlite"
cache = "memory"
logger = "loguru"
[pipeline]
timeout = 10.0
Running in Production
# With uvicorn
uvicorn my_app:app --host 0.0.0.0 --port 8000 --workers 4
# Or with evo CLI
evo service run sandy-franchise --host 0.0.0.0 --port 8000
Strict Mode
Enable strict mode to catch missing processors before they silently fail in production:
from evoid.core.runtime import Config
config = Config(
name="sandy-prod",
strict=True, # Raises LookupError if a processor is not registered
)
Without strict mode, missing processors are silently skipped. In production, you want to know immediately.
Docker
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install uv && uv sync
EXPOSE 8000
CMD ["evo", "service", "run", "sandy-franchise"]
Monitoring
from evoid.core.pipeline import Result
async def monitor(intent: Intent) -> dict:
"""Track pipeline performance."""
result = await execute(intent)
# Log metrics
print(f"Intent: {intent.name}")
print(f"Duration: {result.duration:.3f}s")
print(f"Processors: {len(result.processors)}")
print(f"Success: {result.success}")
return result.value
Scaling
| Strategy | When |
|---|---|
| Multiple workers | CPU-bound, single location |
| Multiple services | Different domains (orders, inventory) |
| Message Bus | Cross-service communication |
| Parallel execution | Batch processing |
Health Checks
Add a health endpoint for load balancers and monitoring:
from evoid.adapters.asgi import get
from evoid.web.route import Service
app = Service("sandy-api")
@get("/health", level="ephemeral")
async def health() -> dict:
return {"status": "healthy", "version": "1.0.0"}
@get("/ready", level="ephemeral")
async def readiness() -> dict:
# Check database connection
try:
await db.execute("SELECT 1")
return {"status": "ready", "database": "ok"}
except Exception as e:
return {"status": "not_ready", "database": str(e)}, 503
Environment Config
Different configs for dev, staging, production:
# config/development.py
from evoid.core.runtime import Config
config = Config(
name="sandy-dev",
adapter="asgi",
engines={"storage": "memory", "cache": "memory"},
)
# config/production.py
config = Config(
name="sandy-prod",
adapter="asgi",
engines={"storage": "sqlite", "cache": "memory"},
)
Graceful Shutdown
Handle shutdown signals cleanly:
import signal
import asyncio
shutdown_event = asyncio.Event()
def handle_shutdown(sig, frame):
print("Shutting down...")
shutdown_event.set()
signal.signal(signal.SIGTERM, handle_shutdown)
signal.signal(signal.SIGINT, handle_shutdown)
# Wait for shutdown signal
await shutdown_event.wait()
# Cleanup: close DB connections, flush caches, etc.
await db.close()
cache.flush()
What You Learned
| Concept | What It Is |
|---|---|
| Production config | Settings for real deployment |
| uvicorn / Docker | Running in production |
| Monitoring | Track performance and errors |
| Scaling strategies | Workers, services, parallelism |
Next: What’s Next
Let’s recap Sandy’s journey — What’s Next.