Performance & Optimierung
Last Updated: Juli 2025
Version: 1.0 - Performance-Optimierungen und Best Practices
Status: ✅ Aktuell - Basierend auf aktueller Implementierung
Database Performance
Primary Key Strategy
UUID Primary Keys: Verwendet für bessere Skalierbarkeit
- Verhindert ID-Collision bei verteilten Systemen
- Bessere Sicherheit (nicht vorhersagbar)
- Laravel HasUuids Trait für automatische Generierung
class Order extends Model
{
use HasUuids;
protected $keyType = 'string';
public $incrementing = false;
}
Strategische Indizierung
Optimiert für häufige Abfragen:
-- OrderRequest Matching Queries
CREATE INDEX order_requests_interpreter_id_state_index
ON order_requests (interpreter_id, state);
CREATE INDEX order_requests_timeout_at_index
ON order_requests (timeout_at);
CREATE INDEX order_requests_matching_score_index
ON order_requests (matching_score);
-- Order Queries
CREATE INDEX orders_state_created_at_index
ON orders (state, created_at);
CREATE INDEX orders_requester_id_state_index
ON orders (requester_id, state);
-- Language Skills Lookup
CREATE INDEX interpreter_languages_profile_language_index
ON interpreter_languages (interpreter_profile_id, language_code);
Query Optimierung
Eager Loading: Verhindert N+1-Query-Probleme
// ❌ Schlecht: N+1 Problem
$orders = Order::all();
foreach ($orders as $order) {
echo $order->requester->name; // Separate query für jeden Order
}
// ✅ Gut: Eager Loading
$orders = Order::with(['requester', 'assignments.interpreter'])->get();
foreach ($orders as $order) {
echo $order->requester->name; // Bereits geladen
}
Select Optimization: Nur benötigte Felder laden
// Für API Listen
$orders = Order::select(['id', 'order_number', 'state', 'created_at'])
->with(['requester:id,first_name,last_name'])
->get();
// Für Dashboards
$orderCounts = Order::selectRaw('state, COUNT(*) as count')
->groupBy('state')
->pluck('count', 'state');
Caching-Strategien
Redis Cache Implementation
Sprachmittler-Verfügbarkeit: Redis Cache (5 Min TTL)
// Verfügbarkeit cachen
$availableInterpreters = Cache::remember(
"interpreters.available.{$languageCode}.{$date}",
now()->addMinutes(5),
fn() => InterpreterProfile::availableFor($languageCode, $date)->get()
);
// Cache invalidieren bei Änderungen
class InterpreterProfile extends Model
{
protected static function booted()
{
static::saved(function ($profile) {
Cache::forget("interpreters.available.*");
});
}
}
Matching-Scores: Background-Jobs für Vorberechnung
// Matching Scores vorberechnen
class PreCalculateMatchingScoresJob implements ShouldQueue
{
public function handle()
{
$upcomingOrders = Order::whereState('state', Open::class)
->where('appointment_date', '>=', now()->addHours(24))
->get();
foreach ($upcomingOrders as $order) {
$this->calculateAndCacheScores($order);
}
}
private function calculateAndCacheScores(Order $order)
{
$scores = MatchingService::calculateScores($order);
Cache::put(
"matching.scores.{$order->id}",
$scores,
now()->addHours(12)
);
}
}
Model Caching
Language-Skills: Eager Loading mit Relationships
// InterpreterProfile Model
public function languages()
{
return $this->hasMany(InterpreterLanguage::class)
->select(['interpreter_profile_id', 'language_code', 'proficiency_level', 'hourly_rate'])
->orderBy('proficiency_level', 'desc');
}
// Usage mit Caching
$interpretersWithLanguages = Cache::remember(
'interpreters.with.languages',
now()->addMinutes(30),
fn() => InterpreterProfile::with('languages')->active()->get()
);
Queue Processing
Job Priorisierung
OrderRequest-Jobs werden priorisiert verarbeitet:
// High Priority: Sprachmittler-Benachrichtigungen
Queue::push(new SendOrderRequestNotification($orderRequest), 'high');
// Normal Priority: Standard Notifications
Queue::push(new SendOrderCompletedNotification($order), 'default');
// Low Priority: Cleanup & Timeouts
Queue::later(60, new HandleOrderRequestTimeout($orderRequest), 'low');
Batch Processing
Bulk Operations für bessere Performance:
// Batch-Update für expired OrderRequests
OrderRequest::where('timeout_at', '<', now())
->where('state', 'Requested')
->update(['state' => 'Expired', 'updated_at' => now()]);
// Batch-Insert für neue OrderRequests
$orderRequests = collect($topCandidates)->map(function ($interpreter) use ($order) {
return [
'id' => (string) Str::uuid(),
'order_id' => $order->id,
'interpreter_id' => $interpreter->id,
'matching_score' => $interpreter->score,
'state' => 'Requested',
'created_at' => now(),
'updated_at' => now(),
];
});
OrderRequest::insert($orderRequests->toArray());
Memory Management
Chunk Processing
Für große Datenmengen:
// ❌ Schlecht: Alle auf einmal laden
$allOrders = Order::all(); // Kann Memory Limit erreichen
// ✅ Gut: Chunk Processing
Order::chunk(100, function ($orders) {
foreach ($orders as $order) {
// Verarbeitung pro Chunk
$this->processOrder($order);
}
});
// ✅ Noch besser: Lazy Collections für Streams
Order::lazy()->each(function ($order) {
$this->processOrder($order);
});
Model Optimization
Soft Deletes: Geschäftskritische Daten bleiben erhalten
// Cleanup alte soft-deleted Records
$cutoffDate = now()->subMonths(12);
// Permanent delete nach 12 Monaten
Order::onlyTrashed()
->where('deleted_at', '<', $cutoffDate)
->forceDelete();
Monitoring & Analytics
Performance Metrics
Key Performance Indicators:
// Database Query Monitoring
class DatabaseQueryMonitor
{
public static function trackSlowQueries()
{
DB::listen(function ($query) {
if ($query->time > 1000) { // > 1 second
Log::warning('Slow Query Detected', [
'sql' => $query->sql,
'time' => $query->time,
'bindings' => $query->bindings
]);
}
});
}
}
// Response Time Tracking
class ResponseTimeMiddleware
{
public function handle($request, Closure $next)
{
$start = microtime(true);
$response = $next($request);
$duration = microtime(true) - $start;
if ($duration > 2.0) { // > 2 seconds
Log::info('Slow Response', [
'url' => $request->url(),
'method' => $request->method(),
'duration' => $duration
]);
}
return $response;
}
}
Health Checks
System Health Monitoring:
// Health Check Endpoint
class HealthController extends Controller
{
public function check()
{
$checks = [
'database' => $this->checkDatabase(),
'redis' => $this->checkRedis(),
'queue' => $this->checkQueue(),
'storage' => $this->checkStorage(),
];
$allHealthy = collect($checks)->every(fn($check) => $check['healthy']);
return response()->json([
'status' => $allHealthy ? 'healthy' : 'unhealthy',
'checks' => $checks,
'timestamp' => now()
], $allHealthy ? 200 : 503);
}
private function checkDatabase(): array
{
try {
DB::select('SELECT 1');
return ['healthy' => true, 'message' => 'Connected'];
} catch (Exception $e) {
return ['healthy' => false, 'message' => $e->getMessage()];
}
}
}
DSGVO & Compliance
Deutsche Compliance Features
- DSGVO-konforme Felder: Salutation, Birth Date, Gender
- German Localization: Deutsche Feldnamen und Werte
- Data Retention: Automatische Löschung nach gesetzlichen Fristen
- Audit Logging: Vollständige Nachverfolgbarkeit aller Änderungen
Data Privacy
// DSGVO Löschung nach Retention Period
class GdprCleanupJob implements ShouldQueue
{
public function handle()
{
$retentionDate = now()->subYears(7); // 7 Jahre Aufbewahrung
// Anonymisiere alte User-Daten
User::where('created_at', '<', $retentionDate)
->whereNull('last_login_at')
->update([
'email' => 'anonymized@example.com',
'first_name' => 'Anonymized',
'last_name' => 'User',
'phone' => null,
'birth_date' => null,
]);
}
}
Tags: #performance #optimization #caching #database #monitoring #gdpr