- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
6.2 KiB
6.2 KiB
LiveComponents Memory Management
Current Architecture (✅ Already Good)
No Instance Caching
- ComponentRegistry creates fresh instances on each
resolve() - No memory leaks from accumulated component instances
- Components are garbage-collected after request
TTL-Based Rendering Cache
- ComponentCacheManager caches rendered HTML only
- Automatic expiry via
Cacheable::getCacheTTL() - Tag-based invalidation for related components
- Delegates to Framework Cache (FileCache, ApcuCache, Redis)
Framework Cache Handles Memory
- FileCache: Disk-based, no memory issues
- ApcuCache: APCu's memory management
- Redis: Redis memory eviction policies
- All support TTL and automatic cleanup
Best Practices
Component Design
✅ DO:
final readonly class UserCardComponent implements LiveComponentContract
{
public function __construct(
private ComponentId $id,
private UserId $userId // ✅ Small Value Object
) {}
public function getRenderData(): ComponentRenderData
{
// ✅ Load data on-demand
$user = $this->userRepository->find($this->userId);
return new ComponentRenderData(
templatePath: 'user-card',
data: ['user' => $user->toArray()]
);
}
}
❌ DON'T:
final readonly class BadComponent implements LiveComponentContract
{
public function __construct(
private ComponentId $id,
private array $allUsers // ❌ Large dataset in constructor!
) {}
}
Caching Strategy
Cache Expensive Renders:
final readonly class StatsComponent implements LiveComponentContract, Cacheable
{
public function shouldCache(): bool
{
return true; // ✅ Expensive calculation
}
public function getCacheTTL(): Duration
{
return Duration::fromMinutes(5); // ✅ Reasonable TTL
}
public function getCacheKey(): string
{
return "stats:{$this->dateRange->format()}"; // ✅ Vary by parameters
}
}
Don't Cache Simple Renders:
final readonly class ButtonComponent implements LiveComponentContract
{
// ❌ Don't implement Cacheable for simple components
// Caching overhead > render cost
}
Memory Monitoring
Per-Request Component Limits:
// Future Enhancement: Request-level tracking
final readonly class ComponentRequestTracker
{
private int $componentsCreated = 0;
private const MAX_COMPONENTS_PER_REQUEST = 100;
public function track(ComponentId $id): void
{
$this->componentsCreated++;
if ($this->componentsCreated > self::MAX_COMPONENTS_PER_REQUEST) {
throw new TooManyComponentsException(
"Request exceeded component limit: {$this->componentsCreated}"
);
}
}
}
Troubleshooting
High Memory Usage
Symptoms:
- PHP memory limit errors
- Slow response times
- Cache growing too large
Diagnosis:
// Check cache size
$stats = $cacheManager->getStats($component);
// Look at 'age_seconds' - old caches not expiring?
// Monitor per-request component creation
error_log("Components created this request: " . count($componentIds));
Solutions:
- Reduce Cache TTL:
public function getCacheTTL(): Duration
{
return Duration::fromMinutes(1); // Shorter TTL
}
- Implement Cache Tags:
public function getCacheTags(): array
{
return ['user:' . $this->userId, 'dashboard'];
}
// Invalidate by tag when data changes
$registry->invalidateCacheByTag('user:123');
- Use Conditional Caching:
public function shouldCache(): bool
{
// Only cache for non-admin users
return !$this->user->isAdmin();
}
- Clear Old Caches:
# Cron job for cleanup
*/30 * * * * php console.php cache:clear --tags=livecomponents
Memory Leaks
Potential Issues:
- Event listeners not unsubscribed
- Database connections in lifecycle hooks
- Large closures in component state
Prevention:
final readonly class SafeComponent implements LiveComponentContract, LifecycleAware
{
public function onMount(): void
{
// ✅ Subscribe to events
$this->eventBus->subscribe('user.updated', $this->handleUserUpdate(...));
}
public function onDestroy(): void
{
// ✅ ALWAYS unsubscribe
$this->eventBus->unsubscribe('user.updated', $this->handleUserUpdate(...));
}
}
Monitoring
Recommended Metrics
Component Metrics:
livecomponent.created.count- Components created per requestlivecomponent.render.duration_ms- Render time distributionlivecomponent.cache.hit_rate- Cache effectiveness
Memory Metrics:
php.memory.usage_bytes- Current memory usagephp.memory.peak_bytes- Peak memory per requestcache.size_bytes- Total cache size
Alert Thresholds:
- Memory usage > 80% of limit
- Components per request > 50
- Cache hit rate < 50%
- Render time > 200ms (p95)
Production Configuration
// config/cache.php
return [
'livecomponents' => [
'default_ttl' => 300, // 5 minutes
'max_cache_size' => '100MB', // Per-driver limit
'cleanup_interval' => 3600, // 1 hour
'enable_metrics' => true
]
];
Future Enhancements
Planned Improvements
-
Request-Level Component Tracking
- Track component creation count
- Enforce per-request limits
- Alert on threshold violations
-
Memory Usage Profiling
- Component memory footprint tracking
- Automatic large component detection
- Performance regression alerts
-
Intelligent Cache Eviction
- LRU eviction when cache full
- Prioritize by component usage frequency
- Automatic TTL adjustment based on hit rate
-
Component Lifecycle Monitoring
- Track component lifetime
- Detect components never destroyed
- Memory leak detection
Summary
✅ Current State: Memory management is solid
- No instance caches
- TTL-based rendering cache
- Framework cache handles underlying memory
✅ No Immediate Action Required
- Architecture is already memory-safe
- Follow best practices above
- Implement monitoring for production
⚠️ Future Work: Request-level limits and enhanced monitoring (Phase 5 - Observability)