7.6 KiB
Discovery Cache Refactoring - Analyse und Vorschläge
Problem-Analyse
Identifiziertes Problem
Die Debug-Logs zeigen:
[DEBUG ComponentRegistry] LiveComponent: class=App\Application\LiveComponents\Popover\PopoverComponent, name=popover, arguments={"name":"popover"}
Befund:
- ✅ Die Datei enthält korrekt
popover-live(bestätigt durch direkte Reflection) - ❌ Der Discovery-Cache enthält noch
{"name":"popover"} - ❌ Die Discovery wird nicht neu durchgeführt - es erscheint "Cached registry loaded", aber kein "Performing fresh discovery"
- ❌ Der Cache wird sofort nach dem Löschen neu erstellt, bevor die Discovery-Registry die Attribute aus den Dateien liest
Root Cause
Der Discovery-Cache wird geladen, bevor die Discovery-Registry die Attribute aus den Dateien liest. Die Arguments werden aus dem Cache geladen, nicht direkt aus den Dateien.
Cache-Load-Reihenfolge:
DiscoveryCacheManager::get()wird aufgerufen- Cache wird geladen (enthält noch alte Arguments)
ComponentRegistry::buildNameMap()verwendet die gecachten Arguments- Kollision tritt auf, weil beide Komponenten als
popoverregistriert werden
Refactoring-Vorschläge
Vorschlag 1: Staleness-Prüfung verbessern
Problem: Die Staleness-Prüfung prüft nicht auf die spezifischen Dateien, die geändert wurden.
Lösung: Erweitere StalenessChecker um eine Methode, die prüft, ob bestimmte Dateien geändert wurden:
// In StalenessChecker.php
public function areFilesModifiedSince(array $filePaths, Timestamp $since): bool
{
foreach ($filePaths as $filePath) {
if ($this->fileSystemService->exists($filePath)) {
$modifiedTime = $this->fileSystemService->getModifiedTime($filePath);
if ($modifiedTime->toInt() > $since->toInt()) {
return true;
}
}
}
return false;
}
Vorteil: Präzise Prüfung auf geänderte Dateien statt allgemeine Staleness-Prüfung.
Vorschlag 2: Cache-Invalidierung bei Datei-Änderungen
Problem: Der Cache wird nicht automatisch invalidated, wenn Dateien geändert werden.
Lösung: Füge einen File-Watcher hinzu, der Datei-Änderungen erkennt und den Cache automatisch invalidated:
// In DiscoveryCacheManager.php
public function invalidateIfFilesChanged(array $filePaths): bool
{
$key = $this->buildCacheKey($context);
$entry = $this->getStandardCache($context);
if (!$entry->found || !$entry->entry) {
return false;
}
$cacheCreatedAt = $entry->entry->createdAt;
foreach ($filePaths as $filePath) {
if ($this->fileSystemService->exists($filePath)) {
$modifiedTime = $this->fileSystemService->getModifiedTime($filePath);
if ($modifiedTime->toInt() > $cacheCreatedAt->toInt()) {
$this->invalidate($context);
return true;
}
}
}
return false;
}
Vorteil: Automatische Cache-Invalidierung bei Datei-Änderungen.
Vorschlag 3: Arguments direkt aus Dateien lesen (Bypass Cache)
Problem: Die Arguments werden aus dem Cache geladen, nicht direkt aus den Dateien.
Lösung: Füge eine Option hinzu, die Arguments direkt aus den Dateien liest, wenn der Cache als stale erkannt wird:
// In ComponentRegistry.php, buildNameMap()
private function buildNameMap(): ComponentNameMap
{
$mappings = [];
$liveComponentClassNames = [];
// Get all LiveComponent attributes from DiscoveryRegistry
$liveComponents = $this->discoveryRegistry->attributes()->get(LiveComponent::class);
foreach ($liveComponents as $discoveredAttribute) {
/** @var LiveComponent|null $attribute */
$attribute = $discoveredAttribute->createAttributeInstance();
if ($attribute && ! empty($attribute->name)) {
$className = $discoveredAttribute->className->toString();
// DEBUG: Log für Popover-Komponenten
if (str_contains($className, 'Popover')) {
// Prüfe ob Datei geändert wurde seit Cache-Erstellung
$filePath = $discoveredAttribute->filePath?->toString();
if ($filePath && $this->isFileModifiedSinceCache($filePath)) {
// Lese Arguments direkt aus Datei
$attribute = $this->readAttributeFromFile($className, LiveComponent::class);
}
error_log(sprintf(
"[DEBUG ComponentRegistry] LiveComponent: class=%s, name=%s, arguments=%s, source=%s",
$className,
$attribute->name,
json_encode($discoveredAttribute->arguments),
$filePath ?? 'unknown'
));
}
$mappings[] = new ComponentMapping($attribute->name, $className);
$liveComponentClassNames[] = $className;
}
}
// ... rest of method
}
Vorteil: Umgeht den Cache, wenn Dateien geändert wurden.
Vorschlag 4: Cache-Versionierung
Problem: Der Cache hat keine Versionierung, die bei Änderungen der Cache-Struktur hilft.
Lösung: Füge eine Cache-Version hinzu, die bei Änderungen der Cache-Struktur erhöht wird:
// In CacheEntry.php
private const int CACHE_VERSION = 2; // Erhöhe bei Cache-Struktur-Änderungen
public function __construct(
public readonly DiscoveryRegistry|array $registry,
public readonly Timestamp $createdAt,
public readonly string $version = '',
public readonly CacheLevel $cacheLevel = CacheLevel::NORMAL,
public readonly CacheTier $cacheTier = CacheTier::HOT,
public readonly int $cacheVersion = self::CACHE_VERSION
) {
}
// In DiscoveryCacheManager.php
private function isCacheVersionValid(CacheEntry $entry): bool
{
return $entry->cacheVersion === CacheEntry::CACHE_VERSION;
}
Vorteil: Automatische Cache-Invalidierung bei Cache-Struktur-Änderungen.
Vorschlag 5: Discovery-Registry Refresh-Mechanismus
Problem: Die Discovery-Registry wird nicht automatisch aktualisiert, wenn Dateien geändert werden.
Lösung: Füge einen Refresh-Mechanismus hinzu, der die Discovery-Registry automatisch aktualisiert:
// In DiscoveryServiceBootstrapper.php
public function refreshIfNeeded(): void
{
$discoveryCacheManager = $this->container->get(DiscoveryCacheManager::class);
$context = $this->createDiscoveryContext();
$cachedRegistry = $discoveryCacheManager->get($context);
if ($cachedRegistry === null) {
// Cache ist leer, führe Discovery durch
$this->bootstrap();
return;
}
// Prüfe ob Registry stale ist
if ($this->isRegistryStale($cachedRegistry, $context)) {
// Registry ist stale, führe Discovery durch
$this->bootstrap();
}
}
Vorteil: Automatische Aktualisierung der Discovery-Registry bei Änderungen.
Empfohlene Implementierungsreihenfolge
- Vorschlag 1 (Staleness-Prüfung verbessern) - Sofortige Verbesserung
- Vorschlag 2 (Cache-Invalidierung bei Datei-Änderungen) - Langfristige Lösung
- Vorschlag 3 (Arguments direkt aus Dateien lesen) - Workaround für aktuelles Problem
- Vorschlag 4 (Cache-Versionierung) - Präventive Maßnahme
- Vorschlag 5 (Discovery-Registry Refresh-Mechanismus) - Automatisierung
Sofortige Lösung (Workaround)
Für das aktuelle Problem sollte Vorschlag 3 implementiert werden, da er das Problem direkt behebt, ohne die gesamte Cache-Architektur zu ändern.
Langfristige Lösung
Vorschlag 2 sollte langfristig implementiert werden, da er das Problem an der Wurzel löst und verhindert, dass ähnliche Probleme in Zukunft auftreten.