Zum Hauptinhalt springen

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