Performance Optimization¶
The tiger Rating Engine includes comprehensive performance optimizations that enable efficient processing of high-volume bulk rating operations. This guide covers the caching system, performance monitoring, and optimization strategies.
Overview¶
Performance Improvements¶
Screenshot Placeholder: Bar chart comparing processing time before optimization (80-120s for 1000 ratings) vs after (10-20s), showing 4-12x speedup¶
Optimization results:
Key optimization features:
Redis Caching: 70-90% faster through cached product configurations
Query Optimization: 98% reduction in database queries
Chunked Processing: Reduced memory footprint for large batches
Connection Pooling: Better resource utilization
Performance Monitoring: Real-time metrics and tracking
Caching System¶
Redis Configuration¶
The system uses Redis for high-performance caching:
# config/settings/base.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": REDIS_URL,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"IGNORE_EXCEPTIONS": True, # Don't crash if Redis is down
"CONNECTION_POOL_KWARGS": {
"max_connections": 50,
"retry_on_timeout": True,
},
},
"KEY_PREFIX": "rating",
"TIMEOUT": 1800, # 30 minutes default
}
}
Configuration settings:
Setting |
Description |
|---|---|
|
Cache timeout (default: 1800 seconds) |
|
Batch processing size (default: 1000) |
|
Redis connection pool size (default: 50) |
What Gets Cached¶
Screenshot Placeholder: Diagram showing cache layers - Product Config, Rating Factors, Rating Tables, Table Values, Formulas, Coverage Definitions¶
Cached data includes:
Product Configurations: * Product metadata and settings * Version information * Key:
rating:product:{code}:latestRating Factors: * All factors for a product * Validation rules * Key:
rating:factors:{product_id}Rating Tables: * Table definitions * Dimension configurations * Key:
rating:table:{product_id}:{table_code}Table Values: * Individual lookup results * Key:
rating:table_val:{table_id}:{dimension_hash}Formulas: * Formula definitions * Execution order * Key:
rating:formulas:{product_id}:{coverage_code}Coverage Definitions: * Coverage metadata * Validation rules * Key:
rating:coverage:{coverage_code}
Cache Performance¶
Performance impact of caching:
Operation |
Without Cache |
With Cache |
Speedup |
|---|---|---|---|
Product load |
15-25ms |
0.2-1ms |
15-125x |
Factors load |
10-20ms |
0.2-1ms |
10-100x |
Table lookup |
10-20ms |
0.1-0.5ms |
20-200x |
Coverage load |
5-10ms |
0.1-0.5ms |
10-100x |
Using the Cache¶
Cache Warming¶
Pre-load product data before bulk processing:
from tiger_rating.rating.utils.cache import warm_cache_for_product
# Warm cache for specific product
warm_cache_for_product(
product_id="123e4567-...",
product_code="BOP"
)
# This caches:
# - Product configuration
# - All rating factors
# - All formulas
# - All rating tables
When to warm cache:
Before large bulk rating jobs
During deployment
After product configuration changes
When adding new products
Cache Invalidation¶
Clear cache after configuration changes:
from tiger_rating.rating.utils.cache import invalidate_product_cache
# After updating product configuration
invalidate_product_cache("BOP", product_id="123e4567-...")
# This clears:
# - Product configuration
# - Product factors
# - Formulas
# - Rating tables
The system automatically invalidates caches when:
Product configurations are updated
Rating factors are modified
Formulas are changed
Rating tables are updated
Manual cache clearing:
from django.core.cache import cache
# Clear all rating caches
cache.delete_pattern("rating:*")
# Clear specific patterns
cache.delete_pattern("rating:product:*") # All products
cache.delete_pattern("rating:table:*") # All tables
cache.delete_pattern("rating:factors:*") # All factors
Cache Statistics¶
Track cache effectiveness:
from tiger_rating.rating.utils.cache import cache_stats
# Get statistics
stats = cache_stats.get_stats()
print(stats)
# Output:
# {
# "hits": 850,
# "misses": 150,
# "total": 1000,
# "hit_rate": 85.0
# }
# Reset statistics
cache_stats.reset()
Good cache performance:
Hit rate >80%: Excellent (cache is effective)
Hit rate 60-80%: Good (some optimization possible)
Hit rate <60%: Poor (check cache warming and TTL)
Performance Monitoring¶
Monitoring Tools¶
The system includes comprehensive performance monitoring:
from tiger_rating.rating.utils.performance import (
PerformanceMonitor,
track_execution_time,
log_query_count,
)
# Context manager usage
with PerformanceMonitor("bulk rating") as monitor:
# ... perform operations ...
pass
# Returns metrics:
# {
# "operation": "bulk rating",
# "execution_time_ms": 5240,
# "execution_time_s": 5.24,
# "query_count": 127
# }
Function decorators:
@track_execution_time
@log_query_count
def process_ratings(requests):
# ... implementation ...
pass
# Logs:
# Function process_ratings executed in 1250ms
# Function process_ratings executed 45 queries
Query Analysis¶
Analyze database queries:
from tiger_rating.rating.utils.performance import QueryOptimizer
from django.db import connection
# Analyze queries
analysis = QueryOptimizer.analyze_queries(connection.queries)
print(analysis)
# Output:
# {
# "total_queries": 100,
# "total_time_ms": 523.45,
# "average_time_ms": 5.23,
# "slowest_query_ms": 45.67,
# "fastest_query_ms": 0.12
# }
# Find N+1 query problems
n_plus_one = QueryOptimizer.find_n_plus_one_queries(
connection.queries,
threshold=5
)
Performance logging:
# Logs include performance metrics
logger.info("Bulk rating job completed", extra={
"job_id": job.id,
"total_requests": 1000,
"execution_time_ms": 15240,
"query_count": 342,
"cache_hit_rate": 94.5,
"success_rate": 98.7
})
Optimization Strategies¶
Bulk Rating Optimization¶
The bulk rating service is optimized for high-volume processing:
Features:
Cache Warming: * Identifies unique products in batch * Pre-loads all product data * Reduces database queries by 98%
Chunked Processing: * Processes in chunks (default: 1000) * Reduces memory footprint * Better progress tracking
Query Optimization: * Uses
select_related()for foreign keys * Batch operations where possible * Reuses cached data across requests
Performance expectations:
Batch Size |
Processing Time |
Throughput |
|---|---|---|
100 requests |
1-2 seconds |
50-100 ratings/sec |
500 requests |
5-10 seconds |
50-100 ratings/sec |
1,000 requests |
10-20 seconds |
50-100 ratings/sec |
5,000 requests |
50-100 seconds |
50-100 ratings/sec |
10,000 requests |
100-200 seconds |
50-100 ratings/sec |
Database Optimization¶
Optimize database performance:
Indexes:
Ensure proper indexes exist:
-- Product lookups
CREATE INDEX idx_product_code ON rating_products(code, is_active);
-- Factor lookups
CREATE INDEX idx_factor_product ON rating_factors(product_id);
-- Table lookups
CREATE INDEX idx_table_product ON rating_tables(product_id, table_code);
Query Optimization:
# Use select_related for foreign keys
products = RatingProduct.objects.select_related(
'created_by'
).filter(is_active=True)
# Use prefetch_related for many-to-many
products = RatingProduct.objects.prefetch_related(
'factors',
'coverages',
'formulas'
).filter(is_active=True)
# Avoid N+1 queries
# BAD:
for product in products:
factors = product.factors.all() # N+1 queries
# GOOD:
products = products.prefetch_related('factors')
for product in products:
factors = product.factors.all() # Single query
Memory Optimization¶
Manage memory for large batches:
Chunked Processing:
# Process in chunks to avoid loading everything
chunk_size = 1000
for chunk in chunks(rating_requests, chunk_size):
process_chunk(chunk)
Iterator Usage:
# Use iterator() for large querysets
for run in RatingRun.objects.filter(...).iterator(chunk_size=100):
process_run(run)
Clear References:
# Clear large objects when done
results = process_large_batch()
# ... use results ...
del results # Free memory
Best Practices¶
Development Best Practices¶
Enable Caching: Always test with caching enabled
Monitor Metrics: Check cache hit rates and execution times
Warm Cache: Pre-warm before large batches
Profile Code: Use performance monitoring tools
Test at Scale: Test with production-sized batches
Production Best Practices¶
Redis Sizing: Allocate sufficient memory for Redis * Estimate: ~1-5MB per product configuration * Example: 100 products = ~100-500MB
Connection Pooling: Use configured pool (max 50)
Monitor Redis: Set up monitoring for: * CPU usage * Memory usage * Connection count * Cache hit rate
Cache Warming: Warm cache during deployment:
# deployment_script.py
from tiger_rating.rating.models import RatingProduct
from tiger_rating.rating.utils.cache import warm_cache_for_product
for product in RatingProduct.objects.filter(is_active=True):
warm_cache_for_product(str(product.id), product.code)
Health Checks: Monitor system health:
from django.core.cache import cache
def health_check():
# Check cache connectivity
try:
cache.set('health_check', 'ok', 10)
value = cache.get('health_check')
cache_healthy = (value == 'ok')
except:
cache_healthy = False
return {
"cache": "healthy" if cache_healthy else "unhealthy",
"database": check_database(),
"redis": check_redis_stats()
}
Troubleshooting¶
Cache Issues¶
Issue: Cache not working
Symptoms: * No performance improvement * Cache hit rate is 0%
Solutions:
Check Redis is running:
docker ps | grep redis
Check Redis connectivity:
from django.core.cache import cache
cache.set('test', 'value', 10)
print(cache.get('test')) # Should print 'value'
Check settings: Verify
CACHESconfiguration inconfig/settings/base.pyCheck logs for cache errors:
just logs django | grep -i cache
Issue: High memory usage
Symptoms: * Redis using too much memory
Solutions:
Reduce cache TTL:
BULK_RATING_CACHE_TTL = 900 # 15 minutes
Reduce chunk size:
BULK_RATING_CHUNK_SIZE = 500
Clear old caches:
cache.delete_pattern("rating:*")
Monitor Redis memory:
docker stats redis
Performance Issues¶
Issue: Slow performance despite caching
Symptoms: * Still slow even with caching enabled
Solutions:
Check cache hit rate (should be >80% for warm cache):
from tiger_rating.rating.utils.cache import cache_stats
print(cache_stats.get_stats())
Warm cache before processing:
from tiger_rating.rating.utils.cache import warm_cache_for_product
warm_cache_for_product(product_id, product_code)
Check for N+1 queries:
from django.db import connection
print(f"Query count: {len(connection.queries)}")
Enable DEBUG temporarily to see queries:
# In Django shell
from django.conf import settings
settings.DEBUG = True
# ... run operation ...
from django.db import connection
print(len(connection.queries))
Issue: Memory errors
Symptoms: * Out of memory errors for large batches
Solutions:
Reduce chunk size:
BULK_RATING_CHUNK_SIZE = 500 # Default is 1000
Process in smaller batches
Increase server memory
Use iterator for large querysets
Monitoring and Metrics¶
Key Metrics to Track¶
Monitor these performance indicators:
Metric |
Description |
Target |
|---|---|---|
Cache Hit Rate |
% of cache hits vs misses |
>80% |
Avg Execution Time |
Time per rating (ms) |
<20ms |
Query Count |
DB queries per rating |
<5 |
Success Rate |
% successful ratings |
>95% |
Throughput |
Ratings per second |
50-100 |
Redis Memory |
Memory used by Redis |
<1GB typical |
Logging Best Practices¶
Log performance data:
import logging
logger = logging.getLogger('performance')
# Log job completion with metrics
logger.info("Bulk job completed", extra={
"job_id": job.id,
"total": job.total_requests,
"success": job.success_count,
"errors": job.error_count,
"execution_time_ms": job.execution_time_ms,
"cache_hit_rate": cache_stats.get_stats()['hit_rate'],
"queries_per_rating": query_count / job.total_requests
})
Example log output:
[INFO] Bulk job completed
job_id: JOB-2025-01-17-001
total: 1000
success: 987
errors: 13
execution_time_ms: 15240
cache_hit_rate: 94.5
queries_per_rating: 3.4
Next Steps¶
Implement: Apply optimizations to your deployment
Monitor: Set up performance monitoring
Test: Validate with production-sized batches
Learn More: See Bulk Rating for usage details
Tip
Start by enabling caching and warming the cache for your most-used products. This alone can provide 4-12x speedup for bulk operations.
Note
Performance improvements are most dramatic for bulk operations. Individual ratings may see smaller gains but will benefit from reduced database load.