Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
145
backups/docs-backup-20250731125004/database/change-tracking.md
Normal file
145
backups/docs-backup-20250731125004/database/change-tracking.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# EntityManager Change Tracking
|
||||
|
||||
Das EntityManager Change Tracking System bietet detaillierte Informationen über Änderungen an Entities und optimiert gleichzeitig die Performance durch Vermeidung unnötiger UPDATE-Queries.
|
||||
|
||||
## Features
|
||||
|
||||
### ✅ Automatische Änderungserkennung
|
||||
- Vergleicht automatisch alte vs. neue Werte für alle Entity-Properties
|
||||
- Ignoriert Relations und ID-Properties bei der Änderungserkennung
|
||||
- Behandelt `null`-Werte und Typ-sensitive Vergleiche korrekt
|
||||
|
||||
### ✅ Performance-Optimierung
|
||||
- **Keine unnötigen DB-Queries**: UPDATE wird nur ausgeführt wenn Änderungen erkannt werden
|
||||
- **Selective Updates**: Nur geänderte Felder werden in der SET-Clause verwendet
|
||||
- **IdentityMap Integration**: Nutzt bereits geladene Entities für Vergleiche
|
||||
|
||||
### ✅ Event-System Integration
|
||||
- Liefert detaillierte Informationen für `EntityUpdatedEvent`
|
||||
- Ermöglicht Audit-Logging und Change-History
|
||||
- Unterstützt Domain Events und Event-Sourcing
|
||||
|
||||
## Usage Example
|
||||
|
||||
```php
|
||||
// Original Entity in IdentityMap laden
|
||||
$user = $entityManager->find(User::class, 1);
|
||||
|
||||
// Entity modifizieren
|
||||
$user->name = 'New Name';
|
||||
$user->age = 25;
|
||||
|
||||
// Update mit automatischem Change Tracking
|
||||
$entityManager->update($user);
|
||||
|
||||
// Das EntityUpdatedEvent enthält:
|
||||
// - changes: ['name', 'age']
|
||||
// - oldValues: ['name' => 'Old Name', 'age' => 24]
|
||||
// - newValues: ['name' => 'New Name', 'age' => 25]
|
||||
```
|
||||
|
||||
## Event Data Structure
|
||||
|
||||
```php
|
||||
class EntityUpdatedEvent
|
||||
{
|
||||
public readonly array $changes; // Geänderte Property-Namen
|
||||
public readonly array $oldValues; // Alte Werte [property => value]
|
||||
public readonly array $newValues; // Neue Werte [property => value]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Benefits
|
||||
|
||||
### UPDATE Query Optimierung
|
||||
```sql
|
||||
-- Vorher: Alle Felder werden immer aktualisiert
|
||||
UPDATE users SET name = ?, email = ?, age = ?, status = ? WHERE id = ?
|
||||
|
||||
-- Nachher: Nur geänderte Felder werden aktualisiert
|
||||
UPDATE users SET name = ?, age = ? WHERE id = ?
|
||||
```
|
||||
|
||||
### Query-Vermeidung
|
||||
```php
|
||||
// Keine Änderungen erkannt → Kein UPDATE ausgeführt
|
||||
$user = $entityManager->find(User::class, 1);
|
||||
$identicalUser = new User(id: 1, name: $user->name, email: $user->email);
|
||||
$result = $entityManager->update($identicalUser); // Kein DB-Query!
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Change Detection Algorithm
|
||||
1. **IdentityMap Lookup**: Original Entity aus IdentityMap laden (falls vorhanden)
|
||||
2. **Property Comparison**: Reflection-basierter Vergleich aller Non-Relation Properties
|
||||
3. **Change Collection**: Sammlung von geänderten Feldern, alten und neuen Werten
|
||||
4. **Query Building**: SET-Clause nur für geänderte Properties
|
||||
5. **Event Dispatch**: EntityUpdatedEvent mit vollständigen Change-Informationen
|
||||
|
||||
### Edge Cases Handling
|
||||
- **Neue Entity ohne Original**: Alle Properties werden als "geändert" behandelt
|
||||
- **Keine Änderungen**: UPDATE wird komplett übersprungen
|
||||
- **Type-sensitive Vergleiche**: `0 !== '0'` wird korrekt erkannt
|
||||
- **Null-Werte**: `null` vs. Wert-Änderungen werden korrekt verarbeitet
|
||||
|
||||
## Testing
|
||||
|
||||
Das Change Tracking System ist durch umfassende Tests abgedeckt:
|
||||
|
||||
```bash
|
||||
# Change Tracking Logic Tests
|
||||
docker exec php ./vendor/bin/pest tests/Framework/Database/ChangeTrackingLogicTest.php
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Audit Logging
|
||||
```php
|
||||
// Event Listener für Audit Trail
|
||||
class AuditLogger
|
||||
{
|
||||
public function handle(EntityUpdatedEvent $event): void
|
||||
{
|
||||
foreach ($event->changes as $property) {
|
||||
$this->logChange([
|
||||
'entity' => $event->entityClass,
|
||||
'id' => $event->entityId,
|
||||
'property' => $property,
|
||||
'old_value' => $event->oldValues[$property],
|
||||
'new_value' => $event->newValues[$property],
|
||||
'timestamp' => $event->timestamp
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Change History
|
||||
```php
|
||||
// Automatische Versionierung
|
||||
class EntityVersioning
|
||||
{
|
||||
public function handle(EntityUpdatedEvent $event): void
|
||||
{
|
||||
if (!empty($event->changes)) {
|
||||
$this->createVersion([
|
||||
'entity_type' => $event->entityClass,
|
||||
'entity_id' => $event->entityId,
|
||||
'changes' => $event->changes,
|
||||
'data' => $event->newValues,
|
||||
'previous_data' => $event->oldValues
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Performance**: Bis zu 50% weniger DB-Queries durch intelligente Änderungserkennung
|
||||
2. **Audit-fähig**: Vollständige Change-History für Compliance und Debugging
|
||||
3. **Event-driven**: Ermöglicht reactive Programmierung und Domain Events
|
||||
4. **Type-safe**: Korrekte Behandlung aller PHP-Datentypen und Edge Cases
|
||||
5. **Zero-Config**: Funktioniert automatisch ohne zusätzliche Konfiguration
|
||||
243
backups/docs-backup-20250731125004/database/eager-loading.md
Normal file
243
backups/docs-backup-20250731125004/database/eager-loading.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# Hydrator Eager Loading System
|
||||
|
||||
Das Eager Loading System löst das **N+1 Query Problem** durch intelligente Batch-Queries und bietet massive Performance-Verbesserungen für Relations-intensive Anwendungen.
|
||||
|
||||
## Problem: N+1 Query Problem
|
||||
|
||||
### Vorher (Lazy Loading)
|
||||
```php
|
||||
// 1 Query für Users
|
||||
$users = $repository->findAll(); // SELECT * FROM users
|
||||
|
||||
// N Queries für Relations (1 pro User)
|
||||
foreach($users as $user) {
|
||||
$user->posts; // SELECT * FROM posts WHERE user_id = ?
|
||||
$user->profile; // SELECT * FROM profiles WHERE user_id = ?
|
||||
}
|
||||
// Total: 1 + (N × 2) = 201 Queries für 100 Users!
|
||||
```
|
||||
|
||||
### Nachher (Eager Loading)
|
||||
```php
|
||||
// 3 Queries total durch Batch-Loading
|
||||
$users = $hydrator->hydrateManyWithRelations($metadata, $userData, ['posts', 'profile']);
|
||||
// 1. SELECT * FROM users
|
||||
// 2. SELECT * FROM posts WHERE user_id IN (1,2,3,...,100)
|
||||
// 3. SELECT * FROM profiles WHERE user_id IN (1,2,3,...,100)
|
||||
// Total: 3 Queries für 100 Users = 98.5% Reduktion!
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### ✅ Selective Eager Loading
|
||||
```php
|
||||
// Lade nur spezifizierte Relations
|
||||
$user = $hydrator->hydrateWithRelations($metadata, $data, ['posts', 'comments']);
|
||||
|
||||
// Ohne Relations = normale Hydration
|
||||
$user = $hydrator->hydrateWithRelations($metadata, $data, []);
|
||||
```
|
||||
|
||||
### ✅ Batch Processing für Collections
|
||||
```php
|
||||
// N+1 Problem gelöst für Collections
|
||||
$users = $hydrator->hydrateManyWithRelations($metadata, $dataRows, ['posts', 'profile']);
|
||||
|
||||
// Intelligente Gruppierung nach Relation-Types
|
||||
// - belongsTo: IN-Queries für Foreign Keys
|
||||
// - hasMany: Gruppierung nach Local Keys
|
||||
// - one-to-one: Eindeutige Zuordnung
|
||||
```
|
||||
|
||||
### ✅ Relation-Type Support
|
||||
- **belongsTo**: Foreign Key Lookups mit IN-Queries
|
||||
- **hasMany**: Reverse Foreign Key Lookups mit Gruppierung
|
||||
- **one-to-one**: Eindeutige Relations-Zuordnung
|
||||
|
||||
## API Usage
|
||||
|
||||
### Single Entity mit Relations
|
||||
```php
|
||||
$user = $hydrator->hydrateWithRelations(
|
||||
$userMetadata,
|
||||
$userData,
|
||||
['posts', 'profile', 'roles']
|
||||
);
|
||||
|
||||
// Relations sind sofort verfügbar, keine zusätzlichen Queries
|
||||
echo count($user->posts); // Kein Query
|
||||
echo $user->profile->bio; // Kein Query
|
||||
```
|
||||
|
||||
### Multiple Entities (Batch Loading)
|
||||
```php
|
||||
$users = $hydrator->hydrateManyWithRelations(
|
||||
$userMetadata,
|
||||
$userDataRows,
|
||||
['posts', 'comments', 'profile']
|
||||
);
|
||||
|
||||
// Alle Relations wurden mit nur 4 Queries geladen:
|
||||
// 1x Users, 1x Posts, 1x Comments, 1x Profiles
|
||||
```
|
||||
|
||||
### Performance-kritische Scenarios
|
||||
```php
|
||||
// Blog-System: Posts mit Comments, Tags, Categories
|
||||
$posts = $hydrator->hydrateManyWithRelations(
|
||||
$postMetadata,
|
||||
$postDataRows,
|
||||
['comments', 'tags', 'category', 'author']
|
||||
);
|
||||
|
||||
// Ohne Eager Loading: 1 + (50 × 4) = 201 Queries
|
||||
// Mit Eager Loading: 5 Queries = 97.5% Reduktion
|
||||
```
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
### Real-World Scenarios
|
||||
|
||||
| Scenario | Entities | Relations | Lazy Queries | Eager Queries | Reduction |
|
||||
|----------|----------|-----------|--------------|---------------|-----------|
|
||||
| User Dashboard | 50 users | posts, profile | 101 | 3 | **97.0%** |
|
||||
| Blog Listing | 20 posts | comments, tags, author | 81 | 4 | **95.1%** |
|
||||
| E-Commerce | 100 products | category, reviews, images | 301 | 4 | **98.7%** |
|
||||
| Social Feed | 30 posts | author, comments, likes | 91 | 4 | **95.6%** |
|
||||
|
||||
### Database Load Reduction
|
||||
```php
|
||||
// Blog System Beispiel (50 Posts)
|
||||
$lazyQueries = 1 + (50 × 3); // 151 Queries
|
||||
$eagerQueries = 4; // 4 Queries
|
||||
$loadReduction = 151 / 4; // 37.75x weniger DB-Load!
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Batch Loading Algorithm
|
||||
1. **Entity Creation**: Erstelle alle Entities ohne Relations
|
||||
2. **Key Collection**: Sammle alle IDs/Foreign Keys für Batch-Queries
|
||||
3. **Batch Queries**: Ein Query pro Relation-Type mit IN-Clauses
|
||||
4. **Grouping**: Gruppiere Related Entities nach Keys
|
||||
5. **Assignment**: Weise Relations den entsprechenden Entities zu
|
||||
|
||||
### BelongsTo Relations (Foreign Key Lookup)
|
||||
```php
|
||||
// Sammle alle Foreign Keys
|
||||
$foreignKeys = array_unique(array_column($dataRows, 'category_id'));
|
||||
|
||||
// Ein Batch-Query für alle Categories
|
||||
$categories = $entityLoader->findBy(Category::class, ['id' => $foreignKeys]);
|
||||
|
||||
// Gruppiere nach ID für schnelle Zuordnung
|
||||
$categoriesById = array_column($categories, null, 'id');
|
||||
```
|
||||
|
||||
### HasMany Relations (Reverse Lookup)
|
||||
```php
|
||||
// Sammle alle Entity IDs
|
||||
$userIds = array_column($userDataRows, 'id');
|
||||
|
||||
// Ein Batch-Query für alle Posts
|
||||
$posts = $entityLoader->findBy(Post::class, ['user_id' => $userIds]);
|
||||
|
||||
// Gruppiere Posts nach user_id
|
||||
$postsByUserId = [];
|
||||
foreach($posts as $post) {
|
||||
$postsByUserId[$post->user_id][] = $post;
|
||||
}
|
||||
```
|
||||
|
||||
### One-to-One Relations
|
||||
```php
|
||||
// Ähnlich wie hasMany, aber nur eine Relation pro Entity
|
||||
$profiles = $entityLoader->findBy(Profile::class, ['user_id' => $userIds]);
|
||||
$profileByUserId = array_column($profiles, null, 'user_id');
|
||||
```
|
||||
|
||||
## Error Handling & Edge Cases
|
||||
|
||||
### Missing Relations
|
||||
```php
|
||||
// Entities ohne Relations erhalten Default-Werte
|
||||
$userPosts = $postsByUserId[$userId] ?? []; // Empty array für hasMany
|
||||
$userProfile = $profilesByUserId[$userId] ?? null; // null für belongsTo/one-to-one
|
||||
```
|
||||
|
||||
### Empty Data Sets
|
||||
```php
|
||||
$result = $hydrator->hydrateManyWithRelations($metadata, [], ['posts']);
|
||||
// Returns: [] (empty array, keine Fehler)
|
||||
```
|
||||
|
||||
### Invalid Relations
|
||||
```php
|
||||
// Nicht-existierende oder Nicht-Relations werden übersprungen
|
||||
$user = $hydrator->hydrateWithRelations($metadata, $data, ['invalid_relation']);
|
||||
// Kein Fehler, Relation wird ignoriert
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Selective Loading
|
||||
```php
|
||||
// ✅ Gut: Nur benötigte Relations laden
|
||||
$posts = $hydrator->hydrateManyWithRelations($metadata, $data, ['author', 'comments']);
|
||||
|
||||
// ❌ Schlecht: Alle Relations laden
|
||||
$posts = $hydrator->hydrateManyWithRelations($metadata, $data, ['author', 'comments', 'tags', 'category', 'ratings']);
|
||||
```
|
||||
|
||||
### 2. Batch Processing priorisieren
|
||||
```php
|
||||
// ✅ Gut: Batch Loading für Collections
|
||||
$users = $hydrator->hydrateManyWithRelations($metadata, $dataRows, ['posts']);
|
||||
|
||||
// ❌ Schlecht: Einzelne Hydration in Loop
|
||||
foreach($dataRows as $data) {
|
||||
$users[] = $hydrator->hydrateWithRelations($metadata, $data, ['posts']);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Relation Depth begrenzen
|
||||
```php
|
||||
// ✅ Gut: Direkte Relations
|
||||
$posts = $hydrator->hydrateManyWithRelations($metadata, $data, ['author', 'comments']);
|
||||
|
||||
// ⚠️ Consideration: Nested Relations
|
||||
// (Aktuell nicht unterstützt, würde zusätzliche Implementierung benötigen)
|
||||
$posts = $hydrator->hydrateManyWithRelations($metadata, $data, ['author.profile', 'comments.author']);
|
||||
```
|
||||
|
||||
## Benefits Summary
|
||||
|
||||
1. **Performance**: 95-98% Reduktion der Database-Queries
|
||||
2. **Scalability**: Lineare statt exponentieller Query-Wachstum
|
||||
3. **Resource Efficiency**: Drastisch reduzierte DB-Connection Usage
|
||||
4. **User Experience**: Faster page loads and API responses
|
||||
5. **Server Stability**: Reduzierter Memory- und CPU-Verbrauch
|
||||
6. **Database Health**: Weniger Lock-Contention und Connection-Pool-Pressure
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Bestehende Code-Base
|
||||
```php
|
||||
// Vorher: Lazy Loading mit N+1 Problem
|
||||
$users = $repository->findAll();
|
||||
foreach($users as $user) {
|
||||
// Implicit queries triggered
|
||||
$user->posts;
|
||||
$user->profile;
|
||||
}
|
||||
|
||||
// Nachher: Explicit Eager Loading
|
||||
$userData = $repository->findAllAsData(); // Raw data query
|
||||
$users = $hydrator->hydrateManyWithRelations($metadata, $userData, ['posts', 'profile']);
|
||||
```
|
||||
|
||||
### Performance Monitoring
|
||||
- Monitor Query Count vor/nach Migration
|
||||
- Database Profiling für Query-Performance
|
||||
- Application Response Time Tracking
|
||||
- Memory Usage Monitoring bei Large Collections
|
||||
@@ -0,0 +1,204 @@
|
||||
# Master/Slave Router with Load Balancing
|
||||
|
||||
Der Master/Slave Router bietet fortgeschrittene Load Balancing und Monitoring Funktionen für optimale Database-Performance in High-Availability Setups.
|
||||
|
||||
## ✅ Implemented Features
|
||||
|
||||
### Weighted Selection Algorithm
|
||||
- **Dynamic Weight Adjustment**: Gewichte basierend auf aktueller Connection-Last und Response Time
|
||||
- **Load Factor**: Reduziert Gewichtung bei hoher Connection-Auslastung (min. 10% Gewichtung)
|
||||
- **Response Time Factor**: Bevorzugt schnelle Replicas basierend auf Moving Average
|
||||
- **Minimum Weight Protection**: Alle Replicas behalten mindestens 1% Gewichtung
|
||||
|
||||
### Connection Metrics & Monitoring
|
||||
- **Real-time Connection Counting**: Tracking aktiver Connections pro Replica
|
||||
- **Response Time History**: Moving Average der letzten 100 Response Times pro Replica
|
||||
- **Health Monitoring**: Automatische Health Checks mit konfigurierbaren Intervallen
|
||||
- **Comprehensive Statistics**: Detaillierte Routing-Statistiken für Monitoring
|
||||
|
||||
### Load Balancing Strategies
|
||||
- **WEIGHTED**: Intelligente gewichtete Auswahl mit Performance-Adjustierung
|
||||
- **LEAST_CONNECTIONS**: Auswahl der Replica mit wenigsten aktiven Connections
|
||||
- **RESPONSE_TIME**: Auswahl basierend auf bester durchschnittlicher Response Time
|
||||
- **ROUND_ROBIN**: Traditionelle zyklische Auswahl
|
||||
- **RANDOM**: Zufällige Auswahl für gleichmäßige Verteilung
|
||||
|
||||
## Configuration
|
||||
|
||||
### DriverConfig Extension
|
||||
```php
|
||||
public readonly class DriverConfig
|
||||
{
|
||||
public function __construct(
|
||||
// ... existing parameters ...
|
||||
public int $weight = 100, // Load balancing weight
|
||||
public int $maxConnections = 100 // Max concurrent connections
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
### ReadWriteConfig Methods
|
||||
```php
|
||||
$config->getConnectionWeight($index); // Get weight for replica
|
||||
$config->getMaxConnections($index); // Get max connections for replica
|
||||
$config->getAllWeights(); // Get all weights indexed by position
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Weighted Load Balancing Setup
|
||||
```php
|
||||
$readWriteConfig = new ReadWriteConfig(
|
||||
enabled: true,
|
||||
readConnections: [
|
||||
DriverConfig::fromArray([
|
||||
'host' => 'replica1.db.local',
|
||||
'weight' => 100, // Normal weight
|
||||
'max_connections' => 50
|
||||
]),
|
||||
DriverConfig::fromArray([
|
||||
'host' => 'replica2.db.local',
|
||||
'weight' => 200, // 2x higher capacity
|
||||
'max_connections' => 100
|
||||
]),
|
||||
DriverConfig::fromArray([
|
||||
'host' => 'replica3.db.local',
|
||||
'weight' => 50, // Lower capacity
|
||||
'max_connections' => 25
|
||||
])
|
||||
],
|
||||
loadBalancingStrategy: LoadBalancingStrategy::WEIGHTED
|
||||
);
|
||||
```
|
||||
|
||||
### Connection Tracking
|
||||
```php
|
||||
// Router tracks connections automatically
|
||||
$replica = $router->route($sql);
|
||||
|
||||
// Manual connection management (if needed)
|
||||
$router->incrementConnectionCount($replica);
|
||||
// ... use connection ...
|
||||
$router->decrementConnectionCount($replica);
|
||||
|
||||
// Track query performance
|
||||
$startTime = microtime(true);
|
||||
$result = $replica->query($sql);
|
||||
$responseTime = (microtime(true) - $startTime) * 1000;
|
||||
$router->recordResponseTime($replica, $responseTime);
|
||||
```
|
||||
|
||||
## Monitoring & Statistics
|
||||
|
||||
### Comprehensive Routing Statistics
|
||||
```php
|
||||
$stats = $router->getRoutingStatistics();
|
||||
// Returns:
|
||||
[
|
||||
'total_replicas' => 3,
|
||||
'healthy_replicas' => 2,
|
||||
'load_balancing_strategy' => 'WEIGHTED',
|
||||
'sticky_sessions' => false,
|
||||
'replica_details' => [
|
||||
0 => [
|
||||
'healthy' => true,
|
||||
'current_connections' => 15,
|
||||
'max_connections' => 50,
|
||||
'load_percentage' => 30.0,
|
||||
'config_weight' => 100,
|
||||
'adjusted_weight' => 85, // Reduced due to load
|
||||
'weight_adjustment_factor' => 0.85,
|
||||
'avg_response_time_ms' => 120.5,
|
||||
'total_queries' => 1540,
|
||||
'failed_queries' => 3,
|
||||
'success_rate' => 99.81,
|
||||
'recent_response_samples' => 100
|
||||
],
|
||||
// ... other replicas
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
### Weight Distribution Analysis
|
||||
```php
|
||||
$distribution = $router->getWeightDistribution();
|
||||
// Returns current weight distribution for healthy replicas:
|
||||
[
|
||||
0 => [
|
||||
'config_weight' => 100,
|
||||
'adjusted_weight' => 85,
|
||||
'current_connections' => 15,
|
||||
'avg_response_time' => 120.5,
|
||||
'weight_percentage' => 35.2 // % of total weight
|
||||
],
|
||||
// ... other healthy replicas
|
||||
]
|
||||
```
|
||||
|
||||
## Performance Benefits
|
||||
|
||||
### Intelligent Load Distribution
|
||||
- **Load-based Adjustment**: Überlastete Replicas erhalten weniger Traffic
|
||||
- **Performance-based Routing**: Schnelle Replicas werden bevorzugt
|
||||
- **Connection Pool Optimization**: Verhindert Connection-Überlastung
|
||||
- **Failover Protection**: Automatischer Fallback bei Replica-Ausfallen
|
||||
|
||||
### Monitoring Integration
|
||||
- **Real-time Metrics**: Live-Statistiken für Performance-Monitoring
|
||||
- **Health Tracking**: Continuous Health Checks mit Response Time Tracking
|
||||
- **Success Rate Monitoring**: Tracking von Query Success/Failure Rates
|
||||
- **Load Analysis**: Detaillierte Load-Verteilung für Capacity Planning
|
||||
|
||||
## Weight Calculation Algorithm
|
||||
|
||||
```php
|
||||
// Dynamic weight adjustment based on current performance
|
||||
$loadFactor = max(0.1, 1 - ($currentConnections / $maxConnections));
|
||||
$responseFactor = max(0.1, min(1.0, 100 / $avgResponseTime));
|
||||
$adjustedWeight = max(1, round($baseWeight * $loadFactor * $responseFactor));
|
||||
```
|
||||
|
||||
**Factors:**
|
||||
- **Load Factor**: 10-100% basierend auf Connection-Auslastung
|
||||
- **Response Factor**: 10-100% basierend auf Response Time (100ms = Baseline)
|
||||
- **Minimum Protection**: Jede Replica behält mindestens 10% Gewichtung
|
||||
|
||||
## Testing
|
||||
|
||||
Comprehensive test suite covering:
|
||||
- ✅ Weighted selection distribution accuracy
|
||||
- ✅ Load factor calculation with edge cases
|
||||
- ✅ Response time factor adjustment
|
||||
- ✅ Connection counting accuracy
|
||||
- ✅ Response time history window management
|
||||
- ✅ Load balancing strategy logic
|
||||
- ✅ Routing statistics generation
|
||||
|
||||
All tests pass with 78 assertions validating the complete implementation.
|
||||
|
||||
## Migration from Simple Round Robin
|
||||
|
||||
```php
|
||||
// Vorher: Simple Round Robin
|
||||
$router = new MasterSlaveRouter(
|
||||
$master,
|
||||
$replicas,
|
||||
new ReadWriteConfig(
|
||||
enabled: true,
|
||||
loadBalancingStrategy: LoadBalancingStrategy::ROUND_ROBIN
|
||||
)
|
||||
);
|
||||
|
||||
// Nachher: Intelligent Weighted Balancing
|
||||
$router = new MasterSlaveRouter(
|
||||
$master,
|
||||
$replicas,
|
||||
new ReadWriteConfig(
|
||||
enabled: true,
|
||||
readConnections: $configsWithWeights,
|
||||
loadBalancingStrategy: LoadBalancingStrategy::WEIGHTED
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Die Implementierung bietet vollständige Backward-Compatibility mit bestehenden Setups.
|
||||
Reference in New Issue
Block a user