Files
michaelschiemer/docs/livecomponents/troubleshooting.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

979 lines
20 KiB
Markdown

# LiveComponents Troubleshooting Guide
**Common Issues and Solutions**
This guide helps diagnose and resolve common LiveComponents problems.
---
## Table of Contents
1. [Component Not Responding](#component-not-responding)
2. [State Synchronization Issues](#state-synchronization-issues)
3. [CSRF Token Errors](#csrf-token-errors)
4. [Rate Limiting Issues](#rate-limiting-issues)
5. [SSE Connection Problems](#sse-connection-problems)
6. [Performance Issues](#performance-issues)
7. [File Upload Problems](#file-upload-problems)
8. [Fragment Rendering Issues](#fragment-rendering-issues)
9. [Browser Compatibility](#browser-compatibility)
10. [Debugging Tools](#debugging-tools)
---
## Component Not Responding
### Symptoms
- Clicking buttons does nothing
- Forms don't submit
- No network requests in DevTools
- Component appears static
### Diagnostic Steps
**1. Check Component Initialization**
```javascript
// Open browser console
console.log(LiveComponent.components);
// Should show active components
// If empty, component not initialized
```
**2. Verify Component Markup**
```html
<!-- Required attributes -->
<div data-component-id="{component_id}" data-component-name="ComponentName">
<!-- Content -->
</div>
```
**3. Check JavaScript Loading**
```javascript
// In browser console
typeof LiveComponent !== 'undefined' // Should be true
```
**4. Inspect Browser Console**
```
F12 → Console Tab
Look for JavaScript errors
```
### Common Causes & Solutions
#### Missing `data-component-id`
```html
<!-- ❌ Wrong - missing required attribute -->
<div class="component">
<button data-lc-action="save">Save</button>
</div>
<!-- ✅ Correct -->
<div data-component-id="{component_id}" data-component-name="MyComponent">
<button data-lc-action="save">Save</button>
</div>
```
#### JavaScript Not Loaded
```bash
# Rebuild JavaScript assets
npm run build
# Check if file exists
ls public/assets/js/livecomponent.js
# Development mode
npm run dev
```
#### HTTPS Not Enabled
```bash
# LiveComponents requires HTTPS
# Start with HTTPS
make up
# Access via
https://localhost # ✅ Correct
http://localhost # ❌ Wrong - will fail
```
#### Component Class Not Found
```php
// Verify class exists and namespace is correct
namespace App\Application\LiveComponents;
final class MyComponent extends LiveComponent
{
// Implementation
}
// In controller
LiveComponent::mount(MyComponent::class) // ✅
LiveComponent::mount('MyComponent') // ❌ Wrong
```
---
## State Synchronization Issues
### Symptoms
- Property changes don't reflect in UI
- UI updates but server state wrong
- State resets unexpectedly
### Diagnostic Steps
**1. Check LiveProp Attribute**
```php
// ❌ Wrong - missing attribute
public string $name = '';
// ✅ Correct
#[LiveProp]
public string $name = '';
```
**2. Verify Property Type Compatibility**
```php
// ✅ Supported types
#[LiveProp] public string $text;
#[LiveProp] public int $count;
#[LiveProp] public float $price;
#[LiveProp] public bool $active;
#[LiveProp] public array $items;
// ❌ Not supported - can't serialize
#[LiveProp] public \Closure $callback;
#[LiveProp] public Resource $handle;
#[LiveProp] public PDO $connection;
```
**3. Inspect Current State**
```javascript
// Get component state
const component = LiveComponent.getComponent('component-id');
console.log('Current state:', component.state);
```
### Common Causes & Solutions
#### Property Not Marked as LiveProp
```php
// Problem: Property changes not synced
public string $searchTerm = ''; // ❌ Missing #[LiveProp]
#[LiveAction]
public function search(): void
{
// $this->searchTerm not synced with client
$this->results = $this->searchService->search($this->searchTerm);
}
// Solution: Add attribute
#[LiveProp]
public string $searchTerm = ''; // ✅ Now syncs
```
#### Complex Object Serialization
```php
// Problem: Object can't be serialized
#[LiveProp]
public User $user; // ❌ Complex object
// Solution: Use Value Object or primitives
#[LiveProp]
public string $userId;
private User $user; // Not serialized
public function hydrate(array $state): void
{
parent::hydrate($state);
// Reload user from ID
$this->user = $this->userRepository->find($this->userId);
}
```
#### State Checksum Mismatch
```javascript
// Monitor checksum errors
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'CHECKSUM_MISMATCH') {
console.error('State corrupted - refreshing component');
LiveComponent.refresh(e.detail.componentId);
}
});
```
---
## CSRF Token Errors
### Symptoms
- "CSRF token mismatch" error
- 403 Forbidden responses
- Actions fail silently
### Diagnostic Steps
**1. Check CSRF Meta Tag**
```html
<!-- Must be in <head> -->
<meta name="csrf-token" content="{csrf_token}">
```
**2. Verify Token in Requests**
```
F12 → Network Tab → Select request → Headers
Should have: X-CSRF-Token: {token}
```
**3. Check Token Expiry**
```php
// .env configuration
CSRF_TOKEN_LIFETIME=7200 // 2 hours (default)
```
### Common Causes & Solutions
#### Missing CSRF Meta Tag
```html
<!-- ❌ Wrong - no CSRF token -->
<head>
<title>My App</title>
</head>
<!-- ✅ Correct - token included -->
<head>
<title>My App</title>
<meta name="csrf-token" content="{csrf_token}">
</head>
```
#### Token Expired
```javascript
// Automatic token refresh
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'CSRF_TOKEN_EXPIRED') {
// Refresh token
LiveComponent.refreshCsrfToken().then(() => {
// Retry failed action
LiveComponent.retryLastAction();
});
}
});
```
#### Session Cleared
```php
// Problem: Session expired/cleared
// Solution: Redirect to login
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'SESSION_EXPIRED') {
window.location.href = '/login?redirect=' + window.location.pathname;
}
});
```
#### Development vs Production
```env
# Development - lenient
CSRF_TOKEN_LIFETIME=86400 # 24 hours
# Production - strict
CSRF_TOKEN_LIFETIME=3600 # 1 hour
CSRF_REGENERATE_ON_ACTION=true
```
---
## Rate Limiting Issues
### Symptoms
- "Too many requests" error (429)
- Actions blocked after rapid clicks
- "Retry-After" headers in response
### Diagnostic Steps
**1. Check Rate Limit Configuration**
```env
LIVECOMPONENT_RATE_LIMIT=60 # Default: 60/minute
LIVECOMPONENT_RATE_LIMIT_WINDOW=60 # Window in seconds
```
**2. Monitor Rate Limit Events**
```javascript
window.addEventListener('livecomponent:rate-limited', (e) => {
console.warn('Rate limited:', {
action: e.detail.action,
retryAfter: e.detail.retryAfter
});
});
```
**3. Check Component-Specific Limits**
```php
#[RateLimit(requests: 10, window: 60)]
final class SearchComponent extends LiveComponent
{
// More restrictive than global limit
}
```
### Common Causes & Solutions
#### Too Aggressive Rate Limit
```env
# Problem: Rate limit too low
LIVECOMPONENT_RATE_LIMIT=10 # Too restrictive
# Solution: Increase for normal usage
LIVECOMPONENT_RATE_LIMIT=60 # More reasonable
```
#### Rapid User Actions
```php
// Problem: User clicking too fast
#[LiveAction]
public function search(): void
{
// Triggered on every keystroke
}
// Solution: Use debouncing
```
```html
<input
type="text"
data-lc-model.debounce.500="searchTerm"
value="{searchTerm}"
/>
```
#### Bot/Scraper Activity
```php
use App\Framework\Http\Middlewares\RateLimitMiddleware;
// Implement stricter limits for suspicious patterns
#[RateLimit(requests: 5, window: 300)] // 5 per 5 minutes
final class PublicApiComponent extends LiveComponent
{
// Public-facing, needs protection
}
```
---
## SSE Connection Problems
### Symptoms
- Real-time updates not working
- Connection drops frequently
- "SSE disconnected" in console
### Diagnostic Steps
**1. Verify HTTPS**
```
SSE requires HTTPS in production
https://localhost ✅
http://localhost ❌
```
**2. Check SSE Configuration**
```env
LIVECOMPONENT_SSE_ENABLED=true
LIVECOMPONENT_SSE_HEARTBEAT=15 # Heartbeat interval
```
**3. Monitor SSE Connection**
```javascript
window.addEventListener('livecomponent:sse-connected', () => {
console.log('SSE connected');
});
window.addEventListener('livecomponent:sse-disconnected', (e) => {
console.error('SSE disconnected:', e.detail.reason);
});
```
**4. Check Server Logs**
```bash
docker logs php | grep SSE
```
### Common Causes & Solutions
#### HTTPS Not Enabled
```bash
# Problem: HTTP used in production
# Solution: Enable HTTPS
# Development
make up # Automatically uses HTTPS
# Production
# Configure SSL certificates in nginx/apache
```
#### Firewall Blocking SSE
```nginx
# nginx configuration
location /livecomponent/sse {
proxy_pass http://php-fpm;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s; # 24 hours
}
```
#### Connection Timeout
```php
// Increase SSE timeout
set_time_limit(0); // No timeout
ignore_user_abort(true);
```
#### Auto-Reconnection Not Working
```javascript
// Manual reconnection logic
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
window.addEventListener('livecomponent:sse-disconnected', (e) => {
if (reconnectAttempts < maxReconnectAttempts) {
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
setTimeout(() => {
console.log(`Reconnecting SSE (attempt ${reconnectAttempts + 1})`);
LiveComponent.reconnectSse(e.detail.componentId);
reconnectAttempts++;
}, delay);
}
});
window.addEventListener('livecomponent:sse-connected', () => {
reconnectAttempts = 0; // Reset on successful connection
});
```
---
## Performance Issues
### Symptoms
- Slow action responses (>500ms)
- UI freezes during updates
- High memory usage
- Browser lag
### Diagnostic Steps
**1. Enable Performance Profiling**
```javascript
localStorage.setItem('livecomponent_devtools', 'true');
localStorage.setItem('livecomponent_profiling', 'true');
location.reload();
```
**2. Check Action Latency**
```javascript
window.addEventListener('livecomponent:action-executed', (e) => {
if (e.detail.duration > 200) {
console.warn('Slow action:', {
action: e.detail.action,
duration: e.detail.duration
});
}
});
```
**3. Monitor Memory Usage**
```
F12 → Performance Tab → Memory
Record session and analyze heap snapshots
```
**4. Analyze Network Requests**
```
F12 → Network Tab
Check:
- Request count (should be low with batching)
- Payload sizes
- Response times
```
### Common Causes & Solutions
#### Large Component State
```php
// Problem: Serializing too much data
#[LiveProp]
public array $allProducts = []; // ❌ 10,000 products
// Solution: Pagination
#[LiveProp]
public int $page = 1;
#[LiveProp]
public int $perPage = 50;
private array $products = []; // ✅ Not serialized
public function mount(): void
{
$this->loadPage();
}
private function loadPage(): void
{
$this->products = $this->productRepository->paginate(
page: $this->page,
perPage: $this->perPage
);
}
```
#### No Fragment Rendering
```php
// Problem: Re-rendering entire component
#[LiveAction]
public function updateStats(): void
{
$this->stats = $this->calculateStats();
// Full component re-render ❌
}
// Solution: Use fragments
#[LiveAction]
#[Fragment('stats-section')]
public function updateStats(): void
{
$this->stats = $this->calculateStats();
// Only stats fragment re-renders ✅
}
```
#### N+1 Query Problem
```php
// Problem: N+1 queries
public function render(): string
{
foreach ($this->orders as $order) {
$customer = $this->customerRepository->find($order->customerId);
// ❌ Query per order
}
}
// Solution: Eager loading
public function mount(): void
{
$this->orders = $this->orderRepository->findWithCustomers();
// ✅ Single query with JOIN
}
```
#### Too Many Components
```
Problem: >100 components on page
Solution:
1. Lazy load components
2. Use virtual scrolling
3. Implement pagination
```
See [Performance Guide](performance-guide.md) for comprehensive optimization strategies.
---
## File Upload Problems
### Symptoms
- Upload fails silently
- Progress stuck at 0%
- Chunk upload errors
- File size exceeded errors
### Diagnostic Steps
**1. Check Upload Configuration**
```html
<input
type="file"
data-lc-upload="handleUpload"
data-chunk-size="1048576"
data-max-file-size="104857600"
/>
```
**2. Monitor Upload Events**
```javascript
window.addEventListener('livecomponent:upload-progress', (e) => {
console.log(`Upload: ${e.detail.progress}%`);
});
window.addEventListener('livecomponent:upload-error', (e) => {
console.error('Upload failed:', e.detail.error);
});
```
**3. Check Server Limits**
```php
// php.ini
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
```
### Common Causes & Solutions
#### File Size Exceeds Limit
```html
<!-- Problem: No validation -->
<input type="file" data-lc-upload="handleUpload" />
<!-- Solution: Set max size -->
<input
type="file"
data-lc-upload="handleUpload"
data-max-file-size="104857600"
accept=".pdf,.jpg,.png"
/>
```
```javascript
// Client-side validation
window.addEventListener('livecomponent:upload-error', (e) => {
if (e.detail.code === 'FILE_TOO_LARGE') {
alert(`File too large. Max size: ${e.detail.maxSize / 1024 / 1024}MB`);
}
});
```
#### Chunk Upload Failure
```php
// Server-side: Implement retry logic
final class ChunkUploadHandler
{
public function storeChunk(
string $uploadId,
int $chunkIndex,
string $data
): void {
$attempts = 0;
$maxAttempts = 3;
while ($attempts < $maxAttempts) {
try {
$this->storage->put(
"uploads/{$uploadId}/chunk_{$chunkIndex}",
$data
);
return;
} catch (\Exception $e) {
$attempts++;
if ($attempts >= $maxAttempts) {
throw $e;
}
usleep(100000 * $attempts); // Exponential backoff
}
}
}
}
```
#### Upload State Lost
```php
// Problem: Upload state not persisted
// Solution: Store upload state
final class UploadStateRepository
{
public function saveState(UploadState $state): void
{
$this->cache->set(
"upload_state:{$state->uploadId}",
serialize($state),
Duration::fromHours(24)
);
}
public function restoreState(string $uploadId): ?UploadState
{
$data = $this->cache->get("upload_state:{$uploadId}");
return $data ? unserialize($data) : null;
}
}
```
---
## Fragment Rendering Issues
### Symptoms
- Fragment not updating
- Entire component re-renders
- Focus lost after update
- Nested fragments broken
### Diagnostic Steps
**1. Verify Fragment Markup**
```html
<!-- Check fragment attribute -->
<div data-lc-fragment="section-name">
<!-- Content -->
</div>
```
**2. Check Action Attribute**
```php
#[LiveAction]
#[Fragment('section-name')] // Must match HTML
public function updateSection(): void
{
// Update logic
}
```
**3. Monitor Fragment Updates**
```javascript
window.addEventListener('livecomponent:fragment-updated', (e) => {
console.log('Fragment updated:', {
name: e.detail.fragmentName,
duration: e.detail.duration
});
});
```
### Common Causes & Solutions
#### Fragment Name Mismatch
```php
// Problem: Names don't match
#[Fragment('user-stats')] // ❌ kebab-case in PHP
public function updateStats(): void { }
```
```html
<!-- userStats in HTML ❌ -->
<div data-lc-fragment="userStats">
```
```php
// Solution: Use consistent naming
#[Fragment('user-stats')] // ✅ kebab-case
```
```html
<div data-lc-fragment="user-stats"> <!-- ✅ Matches -->
```
#### Missing Fragment Attribute
```php
// Problem: Fragment specified but no HTML marker
#[LiveAction]
#[Fragment('results')]
public function search(): void { }
// Template missing fragment marker
<div>
<!-- No data-lc-fragment="results" -->
<for items="results" as="result">
<div>{result.title}</div>
</for>
</div>
// Solution: Add fragment marker
<div data-lc-fragment="results"> <!-- Added -->
<for items="results" as="result">
<div>{result.title}</div>
</for>
</div>
```
#### Focus Lost After Update
```javascript
// Problem: Input focus lost during fragment update
// Solution: Framework preserves focus automatically
// If not working, check:
// 1. Is input inside fragment?
<div data-lc-fragment="search">
<input type="text" id="search-input" />
</div>
// 2. Does input have stable ID?
<input type="text" id="search-input" /> Has ID
<input type="text" /> No ID
```
---
## Browser Compatibility
### Symptoms
- Works in Chrome but not Safari
- Mobile browser issues
- Older browser errors
### Supported Browsers
- Chrome/Edge: 90+
- Firefox: 88+
- Safari: 14+
- Mobile Safari: 14+
- Mobile Chrome: 90+
### Common Issues
#### ES2020 Features Not Supported
```javascript
// Problem: Older browser lacks ES2020
// Solution: Build with polyfills
// vite.config.js
export default defineConfig({
build: {
target: 'es2015', // Broader compatibility
polyfillModulePreload: true
}
});
```
#### EventSource Not Supported
```javascript
// Check SSE support
if (typeof EventSource === 'undefined') {
console.warn('SSE not supported - real-time updates disabled');
// Fallback to polling
}
```
#### iOS Safari Issues
```javascript
// iOS Safari has stricter CORS requirements
// Ensure proper CORS headers
// PHP
header('Access-Control-Allow-Origin: https://yourdomain.com');
header('Access-Control-Allow-Credentials: true');
```
---
## Debugging Tools
### DevTools Panel
**Enable DevTools**:
```env
LIVECOMPONENT_DEVTOOLS_ENABLED=true
```
Or via localStorage:
```javascript
localStorage.setItem('livecomponent_devtools', 'true');
location.reload();
```
**Features**:
- Component tree inspection
- Action log with timing
- Event log
- Performance profiling
- Network monitoring
- DOM badges
### Console Commands
```javascript
// Get all components
LiveComponent.components
// Get specific component
const component = LiveComponent.getComponent('component-id');
// Inspect state
console.log(component.state);
// Execute action manually
LiveComponent.executeAction('component-id', 'actionName', {
param: 'value'
});
// Refresh component
LiveComponent.refresh('component-id');
// Get performance metrics
LiveComponent.getMetrics('component-id');
```
### Server-Side Debugging
```php
// Log component state
$this->logger->debug('Component state', [
'component' => static::class,
'state' => $this->getState()
]);
// Performance tracking
$start = hrtime(true);
$result = $this->expensiveOperation();
$duration = (hrtime(true) - $start) / 1e6;
$this->logger->info('Operation completed', [
'duration_ms' => $duration
]);
```
### Network Debugging
```
F12 → Network Tab → Filter: livecomponent
Check:
- Request payloads
- Response times
- Error responses
- Batching efficiency
```
---
## Getting Help
### Before Asking for Help
1. **Check this guide** for common solutions
2. **Enable DevTools** and check console
3. **Reproduce in isolation** (minimal example)
4. **Gather error messages** (browser console + server logs)
### Reporting Issues
Include:
- Browser + version
- PHP version
- Framework version
- Component code (minimal reproduction)
- Error messages
- Steps to reproduce
### Resources
- **Documentation**: [Getting Started](01-getting-started.md), [API Reference](api-reference.md)
- **Examples**: `src/Application/LiveComponents/`
- **Tests**: `tests/e2e/livecomponents-*.spec.js`
- **Security**: security@example.com
- **Bugs**: GitHub Issues
---
**Next**: [FAQ](faq.md) →