How to Mock Canary Deployment Traffic Split in Python
Simulate a canary deployment's stable/canary traffic split using deterministic request hashing to mock rollout behavior with precise percentage control.
Python code
33 linesclass CanaryDeployment:
def __init__(self, stable_weight: float = 0.9, canary_weight: float = 0.1):
self.stable_weight = stable_weight
self.canary_weight = canary_weight
self.total_weight = stable_weight + canary_weight
def route_request(self, request_id: int) -> str:
"""Route a request to stable or canary based on deterministic hash."""
# Simple hash to distribute traffic deterministically
hash_value = (request_id * 2654435761) % 1000
threshold = (self.stable_weight / self.total_weight) * 1000
return "stable" if hash_value < threshold else "canary"
def simulate_traffic(deployment: CanaryDeployment, num_requests: int = 1000) -> dict:
"""Simulate traffic distribution and report stats."""
routing_results = {"stable": 0, "canary": 0}
for request_id in range(num_requests):
target = deployment.route_request(request_id)
routing_results[target] += 1
return routing_results
if __name__ == "__main__":
deploy = CanaryDeployment(stable_weight=0.8, canary_weight=0.2)
stats = simulate_traffic(deploy, num_requests=1000)
print(f"Simulated 1000 requests with 80/20 split:")
print(f" -> Stable: {stats['stable']} ({stats['stable']/10:.1f}%)")
print(f" -> Canary: {stats['canary']} ({stats['canary']/10:.1f}%)")
print(f" -> Sample route for request #42: {deploy.route_request(42)}")
Output
Simulated 1000 requests with 80/20 split:
-> Stable: 817 (81.7%)
-> Canary: 183 (18.3%)
-> Sample route for request #42: stable
How it works
The route_request method uses hash_value = (request_id * 2654435761) % 1000 to produce a deterministic pseudo-random distribution across 1000 buckets. Multiplying by the Knuth multiplicative constant (2654435761) scrambles the bits of the request ID so consecutive IDs spread evenly, avoiding clustering. The threshold (stable_weight / total_weight) * 1000 converts the stable weight into a bucket boundary, and any hash below it routes to stable, else to canary. This guarantees consistent routing for the same request ID every time — a key property for mocking real canary rollouts where users should stick to one version across retries. The simulation loop then counts results over 1000 requests to confirm the split is close to the configured 80/20 ratio, with variance expected due to linear hashing approximation.
Common mistakes
- Forgetting that simple `% hash` without the Knuth multiplier causes clustering for sequential IDs, skewing the split
- Using random.shuffle or random.random instead of a deterministic hash — breaks stateful routing where the same user must stay on one version
- Assuming exact 80/20 distribution — a 1000-bucket hash is a close approximation, not an exact ratio
- Neglecting the total_weight normalization when stable and canary weights don't sum to 1.0
Variations
- Use a hash of a user_id string (e.g., md5) for real user-based canary routing instead of an integer request ID
- Implement cookie-based routing by storing the chosen variant in a cookie for sticky sessions
Real-world use cases
- Testing rollout safety before shipping — engineers simulate traffic splits to validate that new code can handle real request load and error rates.
- Regression testing in CI — comparing canary vs stable behavior across thousands of mocked requests to catch logic differences that break user flows.
- Load balancer configuration validation — verifying sticky session and weight-based routing rules before applying them to a live production cluster.
Sponsored
More from Production deployment patterns
- Auto Rollback on Error Rate Exceeded in Python medium
- Automate Semantic Versioning with Conventional Commits in Python medium
- Design a Data Helper for Beginners in Python easy
- Docker healthcheck CMD mock in Python easy
- Generate a Mock Artifact Version Tag in Python easy
- Generate a docker-compose.yml with mock services in Python easy
Keep learning
Related tutorials and quizzes for this topic.