- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
218 lines
5.8 KiB
PHP
218 lines
5.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Database\Cache;
|
|
|
|
use App\Framework\Cache\Cache;
|
|
|
|
final class CacheAdapterStrategy implements CacheStrategy
|
|
{
|
|
private array $stats;
|
|
|
|
public function __construct(
|
|
private readonly Cache $cache,
|
|
private readonly string $keyPrefix = 'db_query:'
|
|
) {
|
|
$this->stats = [
|
|
'hits' => 0,
|
|
'misses' => 0,
|
|
'sets' => 0,
|
|
'deletes' => 0,
|
|
'invalidations' => 0,
|
|
];
|
|
}
|
|
|
|
public function set(QueryCacheKey $key, array $value, int $ttlSeconds): bool
|
|
{
|
|
$fullKey = $this->keyPrefix . $key->toString();
|
|
|
|
try {
|
|
$success = $this->cache->set($fullKey, $value, $ttlSeconds);
|
|
|
|
if ($success) {
|
|
$this->stats['sets']++;
|
|
}
|
|
|
|
return $success;
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function get(QueryCacheKey $key): ?array
|
|
{
|
|
$fullKey = $this->keyPrefix . $key->toString();
|
|
|
|
try {
|
|
$value = $this->cache->get($fullKey);
|
|
|
|
if ($value === null) {
|
|
$this->stats['misses']++;
|
|
|
|
return null;
|
|
}
|
|
|
|
$this->stats['hits']++;
|
|
|
|
return $value->value;
|
|
} catch (\Throwable) {
|
|
$this->stats['misses']++;
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function has(QueryCacheKey $key): bool
|
|
{
|
|
$fullKey = $this->keyPrefix . $key->toString();
|
|
|
|
try {
|
|
return $this->cache->has($fullKey);
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function delete(QueryCacheKey $key): bool
|
|
{
|
|
$fullKey = $this->keyPrefix . $key->toString();
|
|
|
|
try {
|
|
$success = $this->cache->forget($fullKey);
|
|
|
|
if ($success) {
|
|
$this->stats['deletes']++;
|
|
}
|
|
|
|
return $success;
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function invalidatePattern(string $pattern): int
|
|
{
|
|
// Fallback für Cache-Implementierungen ohne Pattern-Support
|
|
if (! method_exists($this->cache, 'deleteByPattern')) {
|
|
// Für einfache Caches: kompletter Clear bei Pattern-Invalidierung
|
|
$this->clear();
|
|
|
|
return 1; // Unbekannte Anzahl, also 1 als Indikator
|
|
}
|
|
|
|
try {
|
|
$searchPattern = $this->keyPrefix . '*' . $pattern . '*';
|
|
$deleted = $this->cache->deleteByPattern($searchPattern);
|
|
$this->stats['invalidations'] += $deleted;
|
|
|
|
return $deleted;
|
|
} catch (\Throwable) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public function clear(): void
|
|
{
|
|
try {
|
|
// Wenn Cache Tagged-Cache unterstützt, nutze Tags
|
|
if (method_exists($this->cache, 'deleteByTag')) {
|
|
$deleted = $this->cache->deleteByTag('database_query');
|
|
$this->stats['invalidations'] += $deleted;
|
|
|
|
return;
|
|
}
|
|
|
|
// Wenn Pattern-Deletion unterstützt wird
|
|
if (method_exists($this->cache, 'deleteByPattern')) {
|
|
$deleted = $this->cache->deleteByPattern($this->keyPrefix . '*');
|
|
$this->stats['invalidations'] += $deleted;
|
|
|
|
return;
|
|
}
|
|
|
|
// Fallback: Ganzer Cache wird geleert (nicht ideal)
|
|
if (method_exists($this->cache, 'clear')) {
|
|
$this->cache->clear();
|
|
$this->stats['invalidations']++;
|
|
}
|
|
} catch (\Throwable) {
|
|
// Ignoriere Fehler bei Cache-Clear
|
|
}
|
|
}
|
|
|
|
public function getStats(): array
|
|
{
|
|
$baseStats = $this->stats;
|
|
|
|
// Erweitere um Framework-Cache-spezifische Stats falls verfügbar
|
|
if (method_exists($this->cache, 'getStats')) {
|
|
try {
|
|
$cacheStats = $this->cache->getStats();
|
|
$baseStats = array_merge($baseStats, [
|
|
'cache_stats' => $cacheStats,
|
|
'cache_type' => get_class($this->cache),
|
|
]);
|
|
} catch (\Throwable) {
|
|
// Ignoriere Fehler bei Stats-Abruf
|
|
}
|
|
}
|
|
|
|
// Hit Ratio berechnen
|
|
$baseStats['hit_ratio'] = $this->calculateHitRatio();
|
|
|
|
return $baseStats;
|
|
}
|
|
|
|
private function calculateHitRatio(): float
|
|
{
|
|
$total = $this->stats['hits'] + $this->stats['misses'];
|
|
|
|
return $total > 0 ? ($this->stats['hits'] / $total) : 0.0;
|
|
}
|
|
|
|
/**
|
|
* Factory-Methode für TaggedCache wenn verfügbar
|
|
*/
|
|
public static function withTags(Cache $cache, array $tags = ['database_query'], string $keyPrefix = 'db_query:'): self
|
|
{
|
|
// Wenn TaggedCache verfügbar ist, nutze es
|
|
if (method_exists($cache, 'tags')) {
|
|
$taggedCache = $cache->tags($tags);
|
|
|
|
return new self($taggedCache, $keyPrefix);
|
|
}
|
|
|
|
return new self($cache, $keyPrefix);
|
|
}
|
|
|
|
/**
|
|
* Hilfsmethode für Cache-Tagging
|
|
*/
|
|
private function setWithTags(string $key, array $value, int $ttl, array $tags = []): bool
|
|
{
|
|
if (method_exists($this->cache, 'setWithTags')) {
|
|
return $this->cache->setWithTags($key, $value, $ttl, array_merge(['database_query'], $tags));
|
|
}
|
|
|
|
return $this->cache->set($key, $value, $ttl);
|
|
}
|
|
|
|
/**
|
|
* Invalidierung basierend auf Tabellen-Tags
|
|
*/
|
|
public function invalidateByTable(string $tableName): int
|
|
{
|
|
if (method_exists($this->cache, 'deleteByTag')) {
|
|
try {
|
|
return $this->cache->deleteByTag("table:{$tableName}");
|
|
} catch (\Throwable) {
|
|
// Fallback zu Pattern-basierter Invalidierung
|
|
}
|
|
}
|
|
|
|
// Fallback: Pattern-basierte Invalidierung
|
|
return $this->invalidatePattern("*{$tableName}*");
|
|
}
|
|
}
|