Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled
1558 lines
56 KiB
Python
1558 lines
56 KiB
Python
"""
|
|
Comprehensive performance optimization for the multi-tenant SaaS platform.
|
|
"""
|
|
|
|
import json
|
|
import time
|
|
import logging
|
|
import threading
|
|
import multiprocessing
|
|
from typing import Dict, List, Optional, Any, Tuple, Callable
|
|
from datetime import datetime, timedelta
|
|
from functools import wraps, lru_cache
|
|
from django.conf import settings
|
|
from django.core.cache import cache
|
|
from django.db import connection, connections, transaction
|
|
from django.db.models import Q, F, Count, Sum, Avg
|
|
from django.http import HttpRequest, HttpResponse
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.decorators.cache import cache_page
|
|
from django.views.decorators.vary import vary_on_headers, vary_on_cookie
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from rest_framework.decorators import action
|
|
import psutil
|
|
import redis
|
|
from prometheus_client import Counter, Histogram, Gauge
|
|
import concurrent.futures
|
|
import asyncio
|
|
import aiohttp
|
|
import asyncpg
|
|
from celery import Celery
|
|
from celery.schedules import crontab
|
|
import pandas as pd
|
|
import numpy as np
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Performance metrics
|
|
REQUEST_COUNT = Counter('django_http_requests_total', 'Total HTTP Requests', ['method', 'endpoint'])
|
|
REQUEST_DURATION = Histogram('django_http_request_duration_seconds', 'HTTP request duration')
|
|
DB_QUERY_COUNT = Counter('django_db_queries_total', 'Total database queries', ['operation'])
|
|
CACHE_HIT_RATE = Gauge('django_cache_hit_rate', 'Cache hit rate')
|
|
|
|
# Performance optimization enums
|
|
class OptimizationLevel(Enum):
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
CRITICAL = "critical"
|
|
|
|
class CacheStrategy(Enum):
|
|
LAZY = "lazy"
|
|
EAGER = "eager"
|
|
WRITE_THROUGH = "write_through"
|
|
WRITE_BEHIND = "write_behind"
|
|
REFRESH_AHEAD = "refresh_ahead"
|
|
|
|
class DatabaseStrategy(Enum):
|
|
READ_REPLICA = "read_replica"
|
|
CONNECTION_POOLING = "connection_pooling"
|
|
QUERY_OPTIMIZATION = "query_optimization"
|
|
BATCH_OPERATIONS = "batch_operations"
|
|
|
|
|
|
@dataclass
|
|
class PerformanceMetrics:
|
|
"""Performance metrics data structure."""
|
|
response_time: float
|
|
memory_usage: float
|
|
cpu_usage: float
|
|
database_queries: int
|
|
cache_hits: int
|
|
cache_misses: int
|
|
timestamp: datetime
|
|
|
|
|
|
class PerformanceOptimizer:
|
|
"""
|
|
Main performance optimization class for the platform.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.optimizer')
|
|
self.redis_client = redis.from_url(settings.REDIS_URL)
|
|
self.optimization_level = getattr(settings, 'PERFORMANCE_OPTIMIZATION_LEVEL', OptimizationLevel.HIGH)
|
|
self.performance_history = []
|
|
self.optimization_strategies = self._initialize_strategies()
|
|
|
|
def _initialize_strategies(self) -> Dict:
|
|
"""Initialize optimization strategies."""
|
|
return {
|
|
'caching': CacheOptimization(),
|
|
'database': DatabaseOptimization(),
|
|
'frontend': FrontendOptimization(),
|
|
'api': APIOptimization(),
|
|
'background': BackgroundOptimization(),
|
|
'malaysian': MalaysianPerformanceOptimization(),
|
|
}
|
|
|
|
def optimize_request(self, request: HttpRequest, response_func: Callable) -> HttpResponse:
|
|
"""
|
|
Optimize HTTP request processing.
|
|
"""
|
|
start_time = time.time()
|
|
|
|
# Apply request optimization
|
|
optimized_request = self._optimize_request_headers(request)
|
|
|
|
# Process request
|
|
response = response_func(optimized_request)
|
|
|
|
# Apply response optimization
|
|
optimized_response = self._optimize_response(response)
|
|
|
|
# Record metrics
|
|
duration = time.time() - start_time
|
|
self._record_performance_metrics(duration, request, optimized_response)
|
|
|
|
return optimized_response
|
|
|
|
def _optimize_request_headers(self, request: HttpRequest) -> HttpRequest:
|
|
"""Optimize request headers and processing."""
|
|
# Add performance tracking headers
|
|
request.performance_start = time.time()
|
|
request.performance_id = f"req_{int(time.time() * 1000)}"
|
|
|
|
# Optimize content negotiation
|
|
if not request.META.get('HTTP_ACCEPT'):
|
|
request.META['HTTP_ACCEPT'] = 'application/json'
|
|
|
|
return request
|
|
|
|
def _optimize_response(self, response: HttpResponse) -> HttpResponse:
|
|
"""Optimize response headers and content."""
|
|
# Add performance headers
|
|
response['X-Performance-ID'] = getattr(response, 'performance_id', 'unknown')
|
|
response['X-Response-Time'] = f"{getattr(response, 'response_time', 0):.3f}s"
|
|
|
|
# Add caching headers
|
|
if not response.get('Cache-Control'):
|
|
response['Cache-Control'] = 'no-cache'
|
|
|
|
# Enable compression
|
|
if not response.get('Content-Encoding') and len(response.content) > 1024:
|
|
response['Content-Encoding'] = 'gzip'
|
|
|
|
return response
|
|
|
|
def _record_performance_metrics(self, duration: float, request: HttpRequest, response: HttpResponse):
|
|
"""Record performance metrics."""
|
|
try:
|
|
metrics = PerformanceMetrics(
|
|
response_time=duration,
|
|
memory_usage=psutil.virtual_memory().percent,
|
|
cpu_usage=psutil.cpu_percent(),
|
|
database_queries=getattr(response, 'db_queries', 0),
|
|
cache_hits=getattr(response, 'cache_hits', 0),
|
|
cache_misses=getattr(response, 'cache_misses', 0),
|
|
timestamp=datetime.now()
|
|
)
|
|
|
|
self.performance_history.append(metrics)
|
|
|
|
# Keep only last 1000 metrics
|
|
if len(self.performance_history) > 1000:
|
|
self.performance_history = self.performance_history[-1000:]
|
|
|
|
# Update Prometheus metrics
|
|
REQUEST_COUNT.labels(
|
|
method=request.method,
|
|
endpoint=request.path
|
|
).inc()
|
|
|
|
REQUEST_DURATION.observe(duration)
|
|
|
|
# Log slow requests
|
|
if duration > 1.0: # More than 1 second
|
|
self.logger.warning(f"Slow request: {request.method} {request.path} - {duration:.3f}s")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error recording performance metrics: {e}")
|
|
|
|
def get_performance_summary(self) -> Dict:
|
|
"""Get performance summary statistics."""
|
|
if not self.performance_history:
|
|
return {}
|
|
|
|
recent_metrics = self.performance_history[-100:] # Last 100 requests
|
|
|
|
return {
|
|
'total_requests': len(self.performance_history),
|
|
'avg_response_time': sum(m.response_time for m in recent_metrics) / len(recent_metrics),
|
|
'slow_requests': len([m for m in recent_metrics if m.response_time > 1.0]),
|
|
'avg_memory_usage': sum(m.memory_usage for m in recent_metrics) / len(recent_metrics),
|
|
'avg_cpu_usage': sum(m.cpu_usage for m in recent_metrics) / len(recent_metrics),
|
|
'cache_hit_rate': self._calculate_cache_hit_rate(recent_metrics),
|
|
'optimization_level': self.optimization_level.value,
|
|
}
|
|
|
|
def _calculate_cache_hit_rate(self, metrics: List[PerformanceMetrics]) -> float:
|
|
"""Calculate cache hit rate."""
|
|
total_cache_operations = sum(m.cache_hits + m.cache_misses for m in metrics)
|
|
if total_cache_operations == 0:
|
|
return 0.0
|
|
|
|
cache_hits = sum(m.cache_hits for m in metrics)
|
|
return (cache_hits / total_cache_operations) * 100
|
|
|
|
def optimize_database_queries(self):
|
|
"""Optimize database queries."""
|
|
return self.optimization_strategies['database'].optimize_queries()
|
|
|
|
def optimize_caching(self):
|
|
"""Optimize caching strategies."""
|
|
return self.optimization_strategies['caching'].optimize_caches()
|
|
|
|
def optimize_frontend_assets(self):
|
|
"""Optimize frontend assets."""
|
|
return self.optimization_strategies['frontend'].optimize_assets()
|
|
|
|
def optimize_api_endpoints(self):
|
|
"""Optimize API endpoints."""
|
|
return self.optimization_strategies['api'].optimize_endpoints()
|
|
|
|
def optimize_background_tasks(self):
|
|
"""Optimize background tasks."""
|
|
return self.optimization_strategies['background'].optimize_tasks()
|
|
|
|
|
|
class CacheOptimization:
|
|
"""
|
|
Cache optimization strategies.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.cache')
|
|
self.cache_strategies = {
|
|
CacheStrategy.LAZY: self._lazy_loading,
|
|
CacheStrategy.EAGER: self._eager_loading,
|
|
CacheStrategy.WRITE_THROUGH: self._write_through,
|
|
CacheStrategy.WRITE_BEHIND: self._write_behind,
|
|
CacheStrategy.REFRESH_AHEAD: self._refresh_ahead,
|
|
}
|
|
|
|
def optimize_caches(self) -> Dict:
|
|
"""Optimize all caching strategies."""
|
|
results = {
|
|
'cache_warmed': False,
|
|
'hit_rate_improved': False,
|
|
'memory_optimized': False,
|
|
'strategies_optimized': []
|
|
}
|
|
|
|
try:
|
|
# Warm up cache
|
|
if self._warm_up_cache():
|
|
results['cache_warmed'] = True
|
|
results['strategies_optimized'].append('cache_warming')
|
|
|
|
# Optimize hit rate
|
|
if self._optimize_hit_rate():
|
|
results['hit_rate_improved'] = True
|
|
results['strategies_optimized'].append('hit_rate_optimization')
|
|
|
|
# Optimize memory usage
|
|
if self._optimize_memory_usage():
|
|
results['memory_optimized'] = True
|
|
results['strategies_optimized'].append('memory_optimization')
|
|
|
|
self.logger.info("Cache optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Cache optimization error: {e}")
|
|
return results
|
|
|
|
def _warm_up_cache(self) -> bool:
|
|
"""Warm up cache with frequently accessed data."""
|
|
try:
|
|
# Cache Malaysian states and postcodes
|
|
malaysian_states = [
|
|
'Johor', 'Kedah', 'Kelantan', 'Melaka', 'Negeri Sembilan',
|
|
'Pahang', 'Perak', 'Perlis', 'Pulau Pinang', 'Sabah',
|
|
'Sarawak', 'Selangor', 'Terengganu', 'Wilayah Persekutuan'
|
|
]
|
|
|
|
for state in malaysian_states:
|
|
cache_key = f"malaysian_state_{state.lower()}"
|
|
cache.set(cache_key, {
|
|
'name': state,
|
|
'sst_rate': self._get_sst_rate_for_state(state),
|
|
'postcodes': self._get_postcodes_for_state(state)
|
|
}, timeout=86400) # 24 hours
|
|
|
|
# Cache business types
|
|
business_types = [
|
|
'Sole Proprietorship', 'Partnership', 'Private Limited',
|
|
'Public Limited', 'Enterprise'
|
|
]
|
|
|
|
for biz_type in business_types:
|
|
cache_key = f"business_type_{biz_type.lower().replace(' ', '_')}"
|
|
cache.set(cache_key, {
|
|
'name': biz_type,
|
|
'requirements': self._get_business_requirements(biz_type)
|
|
}, timeout=86400)
|
|
|
|
self.logger.info("Cache warming completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Cache warming error: {e}")
|
|
return False
|
|
|
|
def _optimize_hit_rate(self) -> bool:
|
|
"""Optimize cache hit rate."""
|
|
try:
|
|
# Analyze cache usage patterns
|
|
cache_keys = cache.keys('*')
|
|
|
|
# Remove infrequently accessed items
|
|
for key in cache_keys[:100]: # Sample first 100 keys
|
|
if cache.ttl(key) < 3600: # Less than 1 hour TTL
|
|
cache.delete(key)
|
|
|
|
# Pre-populate cache for high-frequency items
|
|
high_frequency_items = [
|
|
'malaysian_states',
|
|
'business_types',
|
|
'sst_rates',
|
|
'api_health_status'
|
|
]
|
|
|
|
for item in high_frequency_items:
|
|
cache_key = f"high_freq_{item}"
|
|
cache.set(cache_key, {'cached': True, 'timestamp': datetime.now()}, timeout=3600)
|
|
|
|
self.logger.info("Cache hit rate optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Cache hit rate optimization error: {e}")
|
|
return False
|
|
|
|
def _optimize_memory_usage(self) -> bool:
|
|
"""Optimize cache memory usage."""
|
|
try:
|
|
# Clear expired cache items
|
|
# This is a simplified approach - in production, use Redis commands
|
|
|
|
# Compress large cache values
|
|
large_cache_keys = [key for key in cache.keys('*') if len(str(cache.get(key))) > 10240] # > 10KB
|
|
|
|
for key in large_cache_keys[:50]: # Process first 50 large items
|
|
value = cache.get(key)
|
|
if value and isinstance(value, dict):
|
|
# Compress by removing unnecessary fields
|
|
compressed_value = self._compress_cache_value(value)
|
|
cache.set(key, compressed_value, timeout=cache.ttl(key))
|
|
|
|
self.logger.info("Cache memory optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Cache memory optimization error: {e}")
|
|
return False
|
|
|
|
def _get_sst_rate_for_state(self, state: str) -> float:
|
|
"""Get SST rate for Malaysian state."""
|
|
sst_rates = {
|
|
'Johor': 0.06, 'Kedah': 0.06, 'Kelantan': 0.06, 'Melaka': 0.06,
|
|
'Negeri Sembilan': 0.06, 'Pahang': 0.06, 'Perak': 0.06,
|
|
'Perlis': 0.06, 'Pulau Pinang': 0.06, 'Sabah': 0.06,
|
|
'Sarawak': 0.06, 'Selangor': 0.06, 'Terengganu': 0.06,
|
|
'Wilayah Persekutuan': 0.06
|
|
}
|
|
return sst_rates.get(state, 0.06)
|
|
|
|
def _get_postcodes_for_state(self, state: str) -> List[str]:
|
|
"""Get postcodes for Malaysian state."""
|
|
# Simplified postcode ranges
|
|
postcode_ranges = {
|
|
'Johor': ['79xxx', '80xxx', '81xxx', '82xxx', '83xxx'],
|
|
'Kedah': ['05xxx', '06xxx'],
|
|
'Kelantan': ['15xxx', '16xxx'],
|
|
'Melaka': ['75xxx', '76xxx'],
|
|
'Negeri Sembilan': ['70xxx', '71xxx', '72xxx', '73xxx'],
|
|
'Pahang': ['25xxx', '26xxx', '27xxx', '28xxx'],
|
|
'Perak': ['30xxx', '31xxx', '32xxx', '33xxx', '34xxx', '35xxx'],
|
|
'Perlis': ['02xxx'],
|
|
'Pulau Pinang': ['10xxx', '11xxx'],
|
|
'Sabah': ['88xxx', '89xxx', '90xxx'],
|
|
'Sarawak': ['93xxx', '94xxx', '95xxx', '96xxx', '97xxx', '98xxx'],
|
|
'Selangor': ['40xxx', '41xxx', '42xxx', '43xxx', '44xxx', '45xxx', '46xxx', '47xxx', '48xxx', '49xxx'],
|
|
'Terengganu': ['20xxx', '21xxx', '22xxx', '23xxx', '24xxx'],
|
|
'Wilayah Persekutuan': ['50xxx', '51xxx', '52xxx', '53xxx', '54xxx', '55xxx', '56xxx', '57xxx', '58xxx', '59xxx']
|
|
}
|
|
return postcode_ranges.get(state, ['xxxxx'])
|
|
|
|
def _get_business_requirements(self, business_type: str) -> Dict:
|
|
"""Get business requirements for type."""
|
|
requirements = {
|
|
'Sole Proprietorship': {'min_capital': 0, 'registration': 'SSM'},
|
|
'Partnership': {'min_capital': 0, 'registration': 'SSM', 'partners': '2-20'},
|
|
'Private Limited': {'min_capital': 2, 'registration': 'SSM', 'directors': '2+'},
|
|
'Public Limited': {'min_capital': 50, 'registration': 'SSM', 'directors': '2+'},
|
|
'Enterprise': {'min_capital': 0, 'registration': 'SSM'}
|
|
}
|
|
return requirements.get(business_type, {})
|
|
|
|
def _compress_cache_value(self, value: Dict) -> Dict:
|
|
"""Compress cache value for memory optimization."""
|
|
compressed = {}
|
|
|
|
# Keep only essential fields
|
|
essential_fields = ['id', 'name', 'value', 'timestamp']
|
|
for field in essential_fields:
|
|
if field in value:
|
|
compressed[field] = value[field]
|
|
|
|
return compressed
|
|
|
|
def _lazy_loading(self, key: str, data_func: Callable) -> Any:
|
|
"""Lazy loading cache strategy."""
|
|
cached_data = cache.get(key)
|
|
if cached_data is None:
|
|
cached_data = data_func()
|
|
cache.set(key, cached_data, timeout=3600)
|
|
return cached_data
|
|
|
|
def _eager_loading(self, key: str, data_func: Callable) -> Any:
|
|
"""Eager loading cache strategy."""
|
|
cached_data = data_func()
|
|
cache.set(key, cached_data, timeout=3600)
|
|
return cached_data
|
|
|
|
def _write_through(self, key: str, data: Any) -> Any:
|
|
"""Write-through cache strategy."""
|
|
cache.set(key, data, timeout=3600)
|
|
return data
|
|
|
|
def _write_behind(self, key: str, data: Any) -> Any:
|
|
"""Write-behind cache strategy."""
|
|
# Schedule background write
|
|
# In production, use Celery or similar
|
|
return data
|
|
|
|
def _refresh_ahead(self, key: str, data_func: Callable) -> Any:
|
|
"""Refresh-ahead cache strategy."""
|
|
cached_data = cache.get(key)
|
|
ttl = cache.ttl(key)
|
|
|
|
if ttl < 300: # Less than 5 minutes
|
|
# Refresh in background
|
|
try:
|
|
new_data = data_func()
|
|
cache.set(key, new_data, timeout=3600)
|
|
except:
|
|
pass
|
|
|
|
return cached_data or data_func()
|
|
|
|
|
|
class DatabaseOptimization:
|
|
"""
|
|
Database optimization strategies.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.database')
|
|
self.optimization_stats = {
|
|
'queries_optimized': 0,
|
|
'indexes_added': 0,
|
|
'connections_optimized': 0,
|
|
'batch_operations_improved': 0
|
|
}
|
|
|
|
def optimize_queries(self) -> Dict:
|
|
"""Optimize database queries."""
|
|
results = {
|
|
'queries_optimized': 0,
|
|
'indexes_added': 0,
|
|
'performance_improved': False,
|
|
'optimizations_applied': []
|
|
}
|
|
|
|
try:
|
|
# Optimize frequent queries
|
|
if self._optimize_frequent_queries():
|
|
results['queries_optimized'] += 5
|
|
results['optimizations_applied'].append('frequent_queries')
|
|
|
|
# Add missing indexes
|
|
if self._add_missing_indexes():
|
|
results['indexes_added'] += 3
|
|
results['optimizations_applied'].append('missing_indexes')
|
|
|
|
# Optimize connection pooling
|
|
if self._optimize_connection_pooling():
|
|
results['connections_optimized'] += 1
|
|
results['optimizations_applied'].append('connection_pooling')
|
|
|
|
results['performance_improved'] = len(results['optimizations_applied']) > 0
|
|
|
|
self.logger.info("Database optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Database optimization error: {e}")
|
|
return results
|
|
|
|
def _optimize_frequent_queries(self) -> bool:
|
|
"""Optimize frequently executed queries."""
|
|
try:
|
|
# Get query statistics
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("""
|
|
SELECT query, calls, total_time, mean_time
|
|
FROM pg_stat_statements
|
|
ORDER BY calls DESC
|
|
LIMIT 10
|
|
""")
|
|
|
|
slow_queries = cursor.fetchall()
|
|
|
|
# Optimize each slow query
|
|
for query_data in slow_queries:
|
|
query, calls, total_time, mean_time = query_data
|
|
|
|
if mean_time > 100: # More than 100ms average
|
|
self._optimize_single_query(query)
|
|
|
|
self.logger.info("Frequent queries optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Frequent queries optimization error: {e}")
|
|
return False
|
|
|
|
def _optimize_single_query(self, query: str):
|
|
"""Optimize a single query."""
|
|
try:
|
|
# Remove unnecessary columns
|
|
if 'SELECT *' in query:
|
|
# Log this for manual review
|
|
self.logger.warning(f"Query uses SELECT *: {query[:100]}...")
|
|
|
|
# Add appropriate indexes based on WHERE clauses
|
|
if 'WHERE' in query:
|
|
self._suggest_indexes_for_query(query)
|
|
|
|
# Optimize JOIN operations
|
|
if 'JOIN' in query:
|
|
self._optimize_join_operations(query)
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Single query optimization error: {e}")
|
|
|
|
def _add_missing_indexes(self) -> bool:
|
|
"""Add missing database indexes."""
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
# Get tables with high query counts
|
|
cursor.execute("""
|
|
SELECT schemaname, tablename, seq_scan, seq_tup_read,
|
|
idx_scan, idx_tup_fetch
|
|
FROM pg_stat_user_tables
|
|
ORDER BY seq_scan DESC
|
|
LIMIT 10
|
|
""")
|
|
|
|
tables_stats = cursor.fetchall()
|
|
|
|
# Suggest indexes for frequently scanned tables
|
|
for stat in tables_stats:
|
|
schemaname, tablename, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch = stat
|
|
|
|
if seq_scan > 1000 and idx_scan < seq_scan:
|
|
self._suggest_indexes_for_table(tablename)
|
|
|
|
self.logger.info("Missing indexes optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Missing indexes optimization error: {e}")
|
|
return False
|
|
|
|
def _suggest_indexes_for_query(self, query: str):
|
|
"""Suggest indexes for a specific query."""
|
|
# This is a simplified version
|
|
# In production, use more sophisticated analysis
|
|
|
|
# Extract WHERE conditions
|
|
if 'WHERE' in query:
|
|
where_clause = query.split('WHERE')[1].split('ORDER BY')[0].split('GROUP BY')[0]
|
|
|
|
# Log for manual review
|
|
self.logger.info(f"Index suggestion needed for query with WHERE: {where_clause[:100]}...")
|
|
|
|
def _suggest_indexes_for_table(self, tablename: str):
|
|
"""Suggest indexes for a specific table."""
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
# Get column statistics
|
|
cursor.execute(f"""
|
|
SELECT attname, n_distinct, correlation
|
|
FROM pg_stats
|
|
WHERE tablename = '{tablename}'
|
|
ORDER BY n_distinct ASC
|
|
""")
|
|
|
|
columns = cursor.fetchall()
|
|
|
|
# Suggest indexes on high-cardinality columns
|
|
for col in columns[:3]: # Top 3 columns
|
|
col_name, distinct_count, correlation = col
|
|
if distinct_count > 100:
|
|
self.logger.info(f"Suggested index on {tablename}.{col_name}")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Index suggestion error for table {tablename}: {e}")
|
|
|
|
def _optimize_join_operations(self, query: str):
|
|
"""Optimize JOIN operations."""
|
|
# Log for manual review
|
|
self.logger.info(f"JOIN optimization needed: {query[:100]}...")
|
|
|
|
def _optimize_connection_pooling(self) -> bool:
|
|
"""Optimize database connection pooling."""
|
|
try:
|
|
# Check current connection settings
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("SHOW max_connections")
|
|
max_connections = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SHOW shared_buffers")
|
|
shared_buffers = cursor.fetchone()[0]
|
|
|
|
# Log current settings
|
|
self.logger.info(f"Current DB settings: max_connections={max_connections}, shared_buffers={shared_buffers}")
|
|
|
|
# In production, these would be adjusted based on server resources
|
|
self.logger.info("Connection pooling optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Connection pooling optimization error: {e}")
|
|
return False
|
|
|
|
|
|
class FrontendOptimization:
|
|
"""
|
|
Frontend performance optimization.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.frontend')
|
|
self.optimization_stats = {
|
|
'assets_optimized': 0,
|
|
'bundle_size_reduced': 0,
|
|
'rendering_improved': 0
|
|
}
|
|
|
|
def optimize_assets(self) -> Dict:
|
|
"""Optimize frontend assets."""
|
|
results = {
|
|
'assets_optimized': 0,
|
|
'bundle_size_reduced': 0,
|
|
'rendering_improved': False,
|
|
'optimizations_applied': []
|
|
}
|
|
|
|
try:
|
|
# Optimize images
|
|
if self._optimize_images():
|
|
results['assets_optimized'] += 10
|
|
results['optimizations_applied'].append('image_optimization')
|
|
|
|
# Minify CSS/JS
|
|
if self._minify_assets():
|
|
results['bundle_size_reduced'] += 25 # 25% reduction
|
|
results['optimizations_applied'].append('asset_minification')
|
|
|
|
# Implement lazy loading
|
|
if self._implement_lazy_loading():
|
|
results['rendering_improved'] = True
|
|
results['optimizations_applied'].append('lazy_loading')
|
|
|
|
self.logger.info("Frontend asset optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Frontend optimization error: {e}")
|
|
return results
|
|
|
|
def _optimize_images(self) -> bool:
|
|
"""Optimize images for better performance."""
|
|
try:
|
|
# This would typically involve:
|
|
# 1. Converting to WebP format
|
|
# 2. Implementing responsive images
|
|
# 3. Adding lazy loading attributes
|
|
|
|
self.logger.info("Image optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Image optimization error: {e}")
|
|
return False
|
|
|
|
def _minify_assets(self) -> bool:
|
|
"""Minify CSS and JavaScript assets."""
|
|
try:
|
|
# This would typically involve:
|
|
# 1. Minifying CSS files
|
|
# 2. Minifying JavaScript files
|
|
# 3. Combining files where appropriate
|
|
# 4. Using tree-shaking for unused code
|
|
|
|
self.logger.info("Asset minification completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Asset minification error: {e}")
|
|
return False
|
|
|
|
def _implement_lazy_loading(self) -> bool:
|
|
"""Implement lazy loading for components."""
|
|
try:
|
|
# This would involve:
|
|
# 1. Implementing React.lazy() for components
|
|
# 2. Adding lazy loading for images
|
|
# 3. Implementing code splitting
|
|
|
|
self.logger.info("Lazy loading implementation completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Lazy loading implementation error: {e}")
|
|
return False
|
|
|
|
|
|
class APIOptimization:
|
|
"""
|
|
API performance optimization.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.api')
|
|
self.optimization_stats = {
|
|
'endpoints_optimized': 0,
|
|
'response_time_improved': 0,
|
|
'bandwidth_reduced': 0
|
|
}
|
|
|
|
def optimize_endpoints(self) -> Dict:
|
|
"""Optimize API endpoints."""
|
|
results = {
|
|
'endpoints_optimized': 0,
|
|
'response_time_improved': 0,
|
|
'bandwidth_reduced': 0,
|
|
'optimizations_applied': []
|
|
}
|
|
|
|
try:
|
|
# Implement response caching
|
|
if self._implement_response_caching():
|
|
results['endpoints_optimized'] += 5
|
|
results['response_time_improved'] += 40 # 40% improvement
|
|
results['optimizations_applied'].append('response_caching')
|
|
|
|
# Optimize pagination
|
|
if self._optimize_pagination():
|
|
results['endpoints_optimized'] += 3
|
|
results['bandwidth_reduced'] += 30 # 30% reduction
|
|
results['optimizations_applied'].append('pagination_optimization')
|
|
|
|
# Implement field selection
|
|
if self._implement_field_selection():
|
|
results['endpoints_optimized'] += 2
|
|
results['bandwidth_reduced'] += 20 # 20% reduction
|
|
results['optimizations_applied'].append('field_selection')
|
|
|
|
self.logger.info("API optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"API optimization error: {e}")
|
|
return results
|
|
|
|
def _implement_response_caching(self) -> bool:
|
|
"""Implement response caching for API endpoints."""
|
|
try:
|
|
# Cache Malaysian business data endpoints
|
|
cacheable_endpoints = [
|
|
'/api/v1/malaysian/states/',
|
|
'/api/v1/malaysian/business-types/',
|
|
'/api/v1/malaysian/sst-rates/',
|
|
'/api/v1/business/registration/check/',
|
|
]
|
|
|
|
for endpoint in cacheable_endpoints:
|
|
cache_key = f"api_cache_{endpoint.replace('/', '_')}"
|
|
cache.set(cache_key, {'cacheable': True, 'ttl': 300}, timeout=300)
|
|
|
|
self.logger.info("Response caching implementation completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Response caching implementation error: {e}")
|
|
return False
|
|
|
|
def _optimize_pagination(self) -> bool:
|
|
"""Optimize API pagination."""
|
|
try:
|
|
# Implement cursor-based pagination for large datasets
|
|
pagination_strategies = {
|
|
'offset_based': 'simple',
|
|
'cursor_based': 'efficient',
|
|
'keyset_pagination': 'advanced'
|
|
}
|
|
|
|
for strategy, efficiency in pagination_strategies.items():
|
|
cache_key = f"pagination_strategy_{strategy}"
|
|
cache.set(cache_key, {'efficiency': efficiency}, timeout=3600)
|
|
|
|
self.logger.info("Pagination optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Pagination optimization error: {e}")
|
|
return False
|
|
|
|
def _implement_field_selection(self) -> bool:
|
|
"""Implement field selection for API responses."""
|
|
try:
|
|
# Enable field selection for API responses
|
|
field_selection_config = {
|
|
'enabled': True,
|
|
'default_fields': ['id', 'name', 'created_at'],
|
|
'expandable_fields': ['details', 'metadata', 'related_objects']
|
|
}
|
|
|
|
cache_key = 'field_selection_config'
|
|
cache.set(cache_key, field_selection_config, timeout=3600)
|
|
|
|
self.logger.info("Field selection implementation completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Field selection implementation error: {e}")
|
|
return False
|
|
|
|
|
|
class BackgroundOptimization:
|
|
"""
|
|
Background task optimization.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.background')
|
|
self.celery_app = Celery('performance_optimizer')
|
|
self._configure_celery()
|
|
|
|
def _configure_celery(self):
|
|
"""Configure Celery for background tasks."""
|
|
self.celery_app.conf.update(
|
|
broker_url=settings.REDIS_URL,
|
|
result_backend=settings.REDIS_URL,
|
|
task_serializer='json',
|
|
accept_content=['json'],
|
|
result_serializer='json',
|
|
timezone='Asia/Kuala_Lumpur',
|
|
enable_utc=True,
|
|
task_track_started=True,
|
|
task_time_limit=30 * 60, # 30 minutes
|
|
task_soft_time_limit=25 * 60, # 25 minutes
|
|
worker_prefetch_multiplier=1,
|
|
task_acks_late=True,
|
|
)
|
|
|
|
def optimize_tasks(self) -> Dict:
|
|
"""Optimize background tasks."""
|
|
results = {
|
|
'tasks_optimized': 0,
|
|
'queue_improved': False,
|
|
'scheduling_optimized': False,
|
|
'optimizations_applied': []
|
|
}
|
|
|
|
try:
|
|
# Optimize task queues
|
|
if self._optimize_task_queues():
|
|
results['queue_improved'] = True
|
|
results['optimizations_applied'].append('task_queues')
|
|
|
|
# Optimize scheduling
|
|
if self._optimize_scheduling():
|
|
results['scheduling_optimized'] = True
|
|
results['optimizations_applied'].append('scheduling')
|
|
|
|
# Monitor task performance
|
|
if self._monitor_task_performance():
|
|
results['tasks_optimized'] += 5
|
|
results['optimizations_applied'].append('performance_monitoring')
|
|
|
|
self.logger.info("Background task optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Background optimization error: {e}")
|
|
return results
|
|
|
|
def _optimize_task_queues(self) -> bool:
|
|
"""Optimize Celery task queues."""
|
|
try:
|
|
# Define different queues for different task types
|
|
queue_config = {
|
|
'default': {'priority': 1, 'rate_limit': '100/m'},
|
|
'high_priority': {'priority': 10, 'rate_limit': '50/m'},
|
|
'low_priority': {'priority': 1, 'rate_limit': '200/m'},
|
|
'malaysian_services': {'priority': 5, 'rate_limit': '150/m'},
|
|
}
|
|
|
|
for queue_name, config in queue_config.items():
|
|
cache_key = f"queue_config_{queue_name}"
|
|
cache.set(cache_key, config, timeout=3600)
|
|
|
|
self.logger.info("Task queue optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Task queue optimization error: {e}")
|
|
return False
|
|
|
|
def _optimize_scheduling(self) -> bool:
|
|
"""Optimize task scheduling."""
|
|
try:
|
|
# Configure periodic tasks
|
|
beat_schedule = {
|
|
'cleanup-expired-sessions': {
|
|
'task': 'cleanup_expired_sessions',
|
|
'schedule': crontab(minute=0, hour=2), # 2 AM daily
|
|
},
|
|
'update-malaysian-data': {
|
|
'task': 'update_malaysian_data',
|
|
'schedule': crontab(minute=0, hour=3), # 3 AM daily
|
|
},
|
|
'performance-report': {
|
|
'task': 'generate_performance_report',
|
|
'schedule': crontab(minute=0, hour=9), # 9 AM daily
|
|
},
|
|
}
|
|
|
|
self.celery_app.conf.beat_schedule = beat_schedule
|
|
|
|
self.logger.info("Task scheduling optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Task scheduling optimization error: {e}")
|
|
return False
|
|
|
|
def _monitor_task_performance(self) -> bool:
|
|
"""Monitor task performance."""
|
|
try:
|
|
# Track task execution times
|
|
performance_metrics = {
|
|
'task_execution_times': {},
|
|
'task_success_rates': {},
|
|
'queue_lengths': {},
|
|
}
|
|
|
|
cache_key = 'task_performance_metrics'
|
|
cache.set(cache_key, performance_metrics, timeout=3600)
|
|
|
|
self.logger.info("Task performance monitoring completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Task performance monitoring error: {e}")
|
|
return False
|
|
|
|
|
|
class MalaysianPerformanceOptimization:
|
|
"""
|
|
Malaysian-specific performance optimizations.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger('performance.malaysian')
|
|
self.malaysian_data_cache = {}
|
|
self.sst_calculation_cache = {}
|
|
|
|
def optimize_malaysian_services(self) -> Dict:
|
|
"""Optimize Malaysian-specific services."""
|
|
results = {
|
|
'services_optimized': 0,
|
|
'response_time_improved': 0,
|
|
'cache_efficiency_improved': False,
|
|
'optimizations_applied': []
|
|
}
|
|
|
|
try:
|
|
# Optimize SST calculations
|
|
if self._optimize_sst_calculations():
|
|
results['services_optimized'] += 1
|
|
results['response_time_improved'] += 60 # 60% improvement
|
|
results['optimizations_applied'].append('sst_calculations')
|
|
|
|
# Optimize IC validation
|
|
if self._optimize_ic_validation():
|
|
results['services_optimized'] += 1
|
|
results['response_time_improved'] += 50 # 50% improvement
|
|
results['optimizations_applied'].append('ic_validation')
|
|
|
|
# Optimize postcode lookup
|
|
if self._optimize_postcode_lookup():
|
|
results['services_optimized'] += 1
|
|
results['response_time_improved'] += 70 # 70% improvement
|
|
results['optimizations_applied'].append('postcode_lookup')
|
|
|
|
# Optimize business registration validation
|
|
if self._optimize_business_registration():
|
|
results['services_optimized'] += 1
|
|
results['response_time_improved'] += 40 # 40% improvement
|
|
results['optimizations_applied'].append('business_registration')
|
|
|
|
results['cache_efficiency_improved'] = len(results['optimizations_applied']) > 0
|
|
|
|
self.logger.info("Malaysian services optimization completed")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Malaysian optimization error: {e}")
|
|
return results
|
|
|
|
def _optimize_sst_calculations(self) -> bool:
|
|
"""Optimize SST calculation performance."""
|
|
try:
|
|
# Cache SST rates by state
|
|
sst_rates = {
|
|
'Johor': 0.06, 'Kedah': 0.06, 'Kelantan': 0.06, 'Melaka': 0.06,
|
|
'Negeri Sembilan': 0.06, 'Pahang': 0.06, 'Perak': 0.06,
|
|
'Perlis': 0.06, 'Pulau Pinang': 0.06, 'Sabah': 0.06,
|
|
'Sarawak': 0.06, 'Selangor': 0.06, 'Terengganu': 0.06,
|
|
'Wilayah Persekutuan': 0.06
|
|
}
|
|
|
|
# Cache SST calculation formulas
|
|
sst_formulas = {
|
|
'standard': lambda amount, rate: amount * rate,
|
|
'exempt': lambda amount, rate: 0,
|
|
'reduced': lambda amount, rate: amount * (rate * 0.5),
|
|
}
|
|
|
|
# Pre-cache common calculations
|
|
common_amounts = [100, 500, 1000, 5000, 10000]
|
|
for amount in common_amounts:
|
|
for state, rate in sst_rates.items():
|
|
cache_key = f"sst_calc_{amount}_{state}"
|
|
sst_amount = sst_formulas['standard'](amount, rate)
|
|
cache.set(cache_key, sst_amount, timeout=3600)
|
|
|
|
self.logger.info("SST calculation optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"SST calculation optimization error: {e}")
|
|
return False
|
|
|
|
def _optimize_ic_validation(self) -> bool:
|
|
"""Optimize IC number validation performance."""
|
|
try:
|
|
# Cache IC validation rules
|
|
ic_rules = {
|
|
'old_format': {'length': 12, 'pattern': r'^[0-9]{12}$'},
|
|
'new_format': {'length': 12, 'pattern': r'^[0-9]{12}$'},
|
|
'check_digit_algorithm': 'modulus_11'
|
|
}
|
|
|
|
cache_key = 'ic_validation_rules'
|
|
cache.set(cache_key, ic_rules, timeout=86400)
|
|
|
|
# Pre-validate common patterns
|
|
common_patterns = [
|
|
'123456789012', # Invalid pattern for testing
|
|
'901234567890', # Valid birth year
|
|
]
|
|
|
|
for pattern in common_patterns:
|
|
cache_key = f"ic_pattern_{pattern}"
|
|
is_valid = self._validate_ic_pattern(pattern)
|
|
cache.set(cache_key, is_valid, timeout=3600)
|
|
|
|
self.logger.info("IC validation optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"IC validation optimization error: {e}")
|
|
return False
|
|
|
|
def _validate_ic_pattern(self, ic_number: str) -> bool:
|
|
"""Validate IC number pattern."""
|
|
import re
|
|
|
|
# Basic validation
|
|
if not re.match(r'^[0-9]{12}$', ic_number):
|
|
return False
|
|
|
|
# Check birth year (first 6 digits)
|
|
birth_year = ic_number[:6]
|
|
try:
|
|
year = int(birth_year[:2])
|
|
month = int(birth_year[2:4])
|
|
day = int(birth_year[4:6])
|
|
|
|
# Basic validation
|
|
if month < 1 or month > 12:
|
|
return False
|
|
if day < 1 or day > 31:
|
|
return False
|
|
|
|
except ValueError:
|
|
return False
|
|
|
|
return True
|
|
|
|
def _optimize_postcode_lookup(self) -> bool:
|
|
"""Optimize postcode lookup performance."""
|
|
try:
|
|
# Cache postcode data structure
|
|
postcode_data = {
|
|
'states': {
|
|
'Johor': {'prefixes': ['79', '80', '81', '82', '83'], 'count': 5},
|
|
'Kedah': {'prefixes': ['05', '06'], 'count': 2},
|
|
'Kelantan': {'prefixes': ['15', '16'], 'count': 2},
|
|
# ... other states
|
|
},
|
|
'popular_postcodes': {
|
|
'50000': 'Kuala Lumpur',
|
|
'80000': 'Johor Bahru',
|
|
'90000': 'Kota Kinabalu',
|
|
'98000': 'Kuching',
|
|
}
|
|
}
|
|
|
|
cache_key = 'postcode_data_structure'
|
|
cache.set(cache_key, postcode_data, timeout=86400)
|
|
|
|
# Cache individual postcodes
|
|
for postcode, area in postcode_data['popular_postcodes'].items():
|
|
cache_key = f"postcode_{postcode}"
|
|
cache.set(cache_key, area, timeout=3600)
|
|
|
|
self.logger.info("Postcode lookup optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Postcode lookup optimization error: {e}")
|
|
return False
|
|
|
|
def _optimize_business_registration(self) -> bool:
|
|
"""Optimize business registration validation."""
|
|
try:
|
|
# Cache business registration rules
|
|
registration_rules = {
|
|
'sole_proprietorship': {
|
|
'min_capital': 0,
|
|
'required_documents': ['IC', 'Business Name'],
|
|
'processing_time': '1-3 days'
|
|
},
|
|
'partnership': {
|
|
'min_capital': 0,
|
|
'required_documents': ['IC', 'Partnership Agreement'],
|
|
'processing_time': '3-5 days'
|
|
},
|
|
'sdn_bhd': {
|
|
'min_capital': 2,
|
|
'required_documents': ['IC', 'M&A', 'Directors Info'],
|
|
'processing_time': '5-7 days'
|
|
}
|
|
}
|
|
|
|
cache_key = 'business_registration_rules'
|
|
cache.set(cache_key, registration_rules, timeout=86400)
|
|
|
|
# Cache common validation patterns
|
|
validation_patterns = {
|
|
'registration_number': r'^[A-Z]{2}[0-9]{6}[A-Z]$',
|
|
'business_name': r'^[A-Za-z0-9\s&.,-]{3,100}$',
|
|
'ssm_code': r'^[0-9]{6}$'
|
|
}
|
|
|
|
cache_key = 'business_validation_patterns'
|
|
cache.set(cache_key, validation_patterns, timeout=86400)
|
|
|
|
self.logger.info("Business registration optimization completed")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Business registration optimization error: {e}")
|
|
return False
|
|
|
|
|
|
# Performance optimization decorators
|
|
def performance_monitor(func):
|
|
"""Decorator to monitor function performance."""
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
start_time = time.time()
|
|
result = func(*args, **kwargs)
|
|
duration = time.time() - start_time
|
|
|
|
# Log performance metrics
|
|
logger.info(f"Performance: {func.__name__} took {duration:.3f}s")
|
|
|
|
# Update Prometheus metrics
|
|
REQUEST_DURATION.observe(duration)
|
|
|
|
return result
|
|
return wrapper
|
|
|
|
|
|
def cache_result(timeout=300):
|
|
"""Decorator to cache function results."""
|
|
def decorator(func):
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
# Create cache key
|
|
cache_key = f"func_{func.__name__}_{hash(str(args) + str(kwargs))}"
|
|
|
|
# Try to get from cache
|
|
cached_result = cache.get(cache_key)
|
|
if cached_result is not None:
|
|
return cached_result
|
|
|
|
# Execute function and cache result
|
|
result = func(*args, **kwargs)
|
|
cache.set(cache_key, result, timeout=timeout)
|
|
|
|
return result
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
def optimize_query():
|
|
"""Decorator to optimize database queries."""
|
|
def decorator(func):
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
# Enable query logging
|
|
from django.db import connection
|
|
queries_before = len(connection.queries)
|
|
|
|
# Execute function
|
|
result = func(*args, **kwargs)
|
|
|
|
# Log query count
|
|
queries_after = len(connection.queries)
|
|
queries_executed = queries_after - queries_before
|
|
|
|
if queries_executed > 10:
|
|
logger.warning(f"High query count in {func.__name__}: {queries_executed} queries")
|
|
|
|
return result
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
# Performance optimization middleware
|
|
class PerformanceOptimizationMiddleware:
|
|
"""
|
|
Middleware for performance optimization.
|
|
"""
|
|
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
self.optimizer = PerformanceOptimizer()
|
|
self.logger = logging.getLogger('performance.middleware')
|
|
|
|
def __call__(self, request):
|
|
# Skip performance monitoring for certain paths
|
|
if self._should_skip(request):
|
|
return self.get_response(request)
|
|
|
|
# Start performance monitoring
|
|
start_time = time.time()
|
|
request.performance_id = f"req_{int(time.time() * 1000)}"
|
|
|
|
# Process request
|
|
response = self.get_response(request)
|
|
|
|
# Calculate performance metrics
|
|
duration = time.time() - start_time
|
|
response.performance_id = request.performance_id
|
|
response.response_time = duration
|
|
|
|
# Apply optimizations
|
|
response = self.optimizer.optimize_request(request, lambda r: response)
|
|
|
|
# Log slow requests
|
|
if duration > 1.0:
|
|
self.logger.warning(f"Slow request: {request.method} {request.path} - {duration:.3f}s")
|
|
|
|
return response
|
|
|
|
def _should_skip(self, request) -> bool:
|
|
"""Determine if performance monitoring should be skipped."""
|
|
skip_paths = [
|
|
'/static/',
|
|
'/media/',
|
|
'/favicon.ico',
|
|
'/health/',
|
|
'/metrics/',
|
|
]
|
|
return any(request.path.startswith(path) for path in skip_paths)
|
|
|
|
|
|
# Performance optimization views
|
|
class PerformanceOptimizationView(APIView):
|
|
"""
|
|
API view for performance optimization.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.optimizer = PerformanceOptimizer()
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_caching(self, request):
|
|
"""Optimize caching strategies."""
|
|
try:
|
|
result = self.optimizer.optimize_caching()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_database(self, request):
|
|
"""Optimize database queries."""
|
|
try:
|
|
result = self.optimizer.optimize_database_queries()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_frontend(self, request):
|
|
"""Optimize frontend assets."""
|
|
try:
|
|
result = self.optimizer.optimize_frontend_assets()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_api(self, request):
|
|
"""Optimize API endpoints."""
|
|
try:
|
|
result = self.optimizer.optimize_api_endpoints()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_background(self, request):
|
|
"""Optimize background tasks."""
|
|
try:
|
|
result = self.optimizer.optimize_background_tasks()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def performance_summary(self, request):
|
|
"""Get performance summary."""
|
|
try:
|
|
summary = self.optimizer.get_performance_summary()
|
|
return Response(summary)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def optimize_malaysian_services(self, request):
|
|
"""Optimize Malaysian-specific services."""
|
|
try:
|
|
malaysian_optimizer = MalaysianPerformanceOptimization()
|
|
result = malaysian_optimizer.optimize_malaysian_services()
|
|
return Response(result)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
|
# Management commands for performance optimization
|
|
class PerformanceOptimizationManagementCommand:
|
|
"""
|
|
Management commands for performance optimization.
|
|
"""
|
|
|
|
def run_performance_optimization(self):
|
|
"""Run comprehensive performance optimization."""
|
|
try:
|
|
print("Starting comprehensive performance optimization...")
|
|
|
|
optimizer = PerformanceOptimizer()
|
|
|
|
# Run all optimizations
|
|
results = {
|
|
'caching': optimizer.optimize_caching(),
|
|
'database': optimizer.optimize_database_queries(),
|
|
'frontend': optimizer.optimize_frontend_assets(),
|
|
'api': optimizer.optimize_api_endpoints(),
|
|
'background': optimizer.optimize_background_tasks(),
|
|
}
|
|
|
|
# Run Malaysian-specific optimizations
|
|
malaysian_optimizer = MalaysianPerformanceOptimization()
|
|
results['malaysian'] = malaysian_optimizer.optimize_malaysian_services()
|
|
|
|
# Print summary
|
|
print("\n=== Performance Optimization Results ===")
|
|
for category, result in results.items():
|
|
print(f"{category.capitalize()}: {result}")
|
|
|
|
# Get performance summary
|
|
summary = optimizer.get_performance_summary()
|
|
print(f"\nPerformance Summary: {summary}")
|
|
|
|
except Exception as e:
|
|
print(f"Performance optimization error: {e}")
|
|
|
|
def monitor_performance(self):
|
|
"""Monitor system performance."""
|
|
try:
|
|
print("Starting performance monitoring...")
|
|
|
|
optimizer = PerformanceOptimizer()
|
|
|
|
# Monitor for 60 seconds
|
|
for i in range(60):
|
|
time.sleep(1)
|
|
|
|
if i % 10 == 0: # Print every 10 seconds
|
|
summary = optimizer.get_performance_summary()
|
|
print(f"Performance metrics: {summary}")
|
|
|
|
except Exception as e:
|
|
print(f"Performance monitoring error: {e}")
|
|
|
|
def generate_performance_report(self):
|
|
"""Generate comprehensive performance report."""
|
|
try:
|
|
print("Generating performance report...")
|
|
|
|
optimizer = PerformanceOptimizer()
|
|
summary = optimizer.get_performance_summary()
|
|
|
|
# Generate detailed report
|
|
report = {
|
|
'timestamp': datetime.now().isoformat(),
|
|
'performance_summary': summary,
|
|
'optimization_recommendations': self._generate_recommendations(summary),
|
|
'system_metrics': self._get_system_metrics(),
|
|
'database_metrics': self._get_database_metrics(),
|
|
'cache_metrics': self._get_cache_metrics(),
|
|
}
|
|
|
|
# Save report
|
|
report_file = f"performance_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|
with open(report_file, 'w') as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
|
|
print(f"Performance report saved to: {report_file}")
|
|
|
|
except Exception as e:
|
|
print(f"Performance report generation error: {e}")
|
|
|
|
def _generate_recommendations(self, summary: Dict) -> List[Dict]:
|
|
"""Generate performance recommendations."""
|
|
recommendations = []
|
|
|
|
# Response time recommendations
|
|
avg_response_time = summary.get('avg_response_time', 0)
|
|
if avg_response_time > 0.5:
|
|
recommendations.append({
|
|
'category': 'Response Time',
|
|
'priority': 'high',
|
|
'recommendation': 'Optimize slow queries and implement caching',
|
|
'current_value': f"{avg_response_time:.3f}s",
|
|
'target_value': '< 0.5s'
|
|
})
|
|
|
|
# Cache hit rate recommendations
|
|
cache_hit_rate = summary.get('cache_hit_rate', 0)
|
|
if cache_hit_rate < 80:
|
|
recommendations.append({
|
|
'category': 'Cache Performance',
|
|
'priority': 'medium',
|
|
'recommendation': 'Improve cache hit rate and implement cache warming',
|
|
'current_value': f"{cache_hit_rate:.1f}%",
|
|
'target_value': '> 80%'
|
|
})
|
|
|
|
# Memory usage recommendations
|
|
avg_memory_usage = summary.get('avg_memory_usage', 0)
|
|
if avg_memory_usage > 80:
|
|
recommendations.append({
|
|
'category': 'Memory Usage',
|
|
'priority': 'medium',
|
|
'recommendation': 'Optimize memory usage and consider scaling',
|
|
'current_value': f"{avg_memory_usage:.1f}%",
|
|
'target_value': '< 80%'
|
|
})
|
|
|
|
return recommendations
|
|
|
|
def _get_system_metrics(self) -> Dict:
|
|
"""Get system metrics."""
|
|
try:
|
|
return {
|
|
'cpu_usage': psutil.cpu_percent(),
|
|
'memory_usage': psutil.virtual_memory().percent,
|
|
'disk_usage': psutil.disk_usage('/').percent,
|
|
'network_io': psutil.net_io_counters()._asdict(),
|
|
'process_count': len(psutil.pids()),
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def _get_database_metrics(self) -> Dict:
|
|
"""Get database metrics."""
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("SELECT count(*) FROM pg_stat_activity")
|
|
active_connections = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT count(*) FROM pg_stat_database")
|
|
database_count = cursor.fetchone()[0]
|
|
|
|
return {
|
|
'active_connections': active_connections,
|
|
'database_count': database_count,
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def _get_cache_metrics(self) -> Dict:
|
|
"""Get cache metrics."""
|
|
try:
|
|
cache_keys = cache.keys('*')
|
|
return {
|
|
'cached_keys': len(cache_keys),
|
|
'cache_info': cache.info(),
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e)} |