feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready
This commit is contained in:
@@ -7,6 +7,8 @@ namespace App\Framework\Cache;
|
||||
use App\Framework\Cache\Compression\NoCompression;
|
||||
use App\Framework\Cache\Contracts\DriverAccessible;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Logging\ValueObjects\LogContext;
|
||||
use App\Framework\Serializer\Serializer;
|
||||
|
||||
final readonly class GeneralCache implements Cache, DriverAccessible
|
||||
@@ -17,7 +19,8 @@ final readonly class GeneralCache implements Cache, DriverAccessible
|
||||
private CacheDriver $adapter,
|
||||
private Serializer $serializer,
|
||||
private CompressionAlgorithm $compressionAlgorithm = new NoCompression(),
|
||||
private bool $autoCompress = true
|
||||
private bool $autoCompress = true,
|
||||
private ?Logger $logger = null
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -255,48 +258,62 @@ final readonly class GeneralCache implements Cache, DriverAccessible
|
||||
}
|
||||
|
||||
/**
|
||||
* EMERGENCY: Monitor cache item sizes to identify memory explosion sources
|
||||
* Monitor cache item sizes to identify memory explosion sources
|
||||
*/
|
||||
private function monitorCacheSize(CacheItem $item): void
|
||||
{
|
||||
if ($this->logger === null) {
|
||||
return; // No logging if logger not provided
|
||||
}
|
||||
|
||||
try {
|
||||
// Get serialized size to understand cache impact
|
||||
$serializedValue = serialize($item->value);
|
||||
$sizeBytes = strlen($serializedValue);
|
||||
$sizeKB = round($sizeBytes / 1024, 2);
|
||||
$sizeMB = round($sizeBytes / 1024 / 1024, 2);
|
||||
|
||||
// Log large cache items
|
||||
if ($sizeBytes > 50 * 1024) { // >50KB for GeneralCache
|
||||
$keyString = $item->key->toString();
|
||||
$valueType = get_debug_type($item->value);
|
||||
$keyString = (string) $item->key;
|
||||
$pattern = $this->analyzeCacheKeyPattern($keyString);
|
||||
|
||||
error_log("⚠️ GENERAL CACHE SIZE WARNING: {$sizeMB}MB");
|
||||
error_log(" 📋 Cache Key: '{$keyString}'");
|
||||
error_log(" 🏷️ Value Type: {$valueType}");
|
||||
|
||||
// If it's an object, get class name
|
||||
if (is_object($item->value)) {
|
||||
$className = get_class($item->value);
|
||||
error_log(" 🔍 Object Class: {$className}");
|
||||
}
|
||||
|
||||
// Show cache key pattern analysis
|
||||
$keyAnalysis = $this->analyzeCacheKeyPattern($keyString);
|
||||
error_log(" 🔑 Key Pattern: {$keyAnalysis}");
|
||||
// Monitor large cache items (>50KB for GeneralCache)
|
||||
if ($sizeBytes > 50 * 1024) {
|
||||
$this->logger->warning(
|
||||
'Large cache item detected in GeneralCache',
|
||||
LogContext::withData([
|
||||
'key' => $keyString,
|
||||
'size_kb' => $sizeKB,
|
||||
'size_mb' => $sizeMB,
|
||||
'pattern' => $pattern,
|
||||
'threshold' => '50KB',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// EMERGENCY: Block extremely large cache items
|
||||
if ($sizeBytes > 5 * 1024 * 1024) { // >5MB for GeneralCache (more strict)
|
||||
error_log("🛑 GENERAL CACHE BLOCK: Refusing to cache {$sizeMB}MB item");
|
||||
error_log(" 📋 Blocked Key: '{$keyString}'");
|
||||
error_log(" 🚫 Reason: Exceeds 5MB limit");
|
||||
$this->logger->error(
|
||||
'GeneralCache BLOCK: Refusing to cache oversized item',
|
||||
LogContext::withData([
|
||||
'key' => $keyString,
|
||||
'size_mb' => $sizeMB,
|
||||
'limit_mb' => 5,
|
||||
'pattern' => $pattern,
|
||||
'action' => 'blocked',
|
||||
])
|
||||
);
|
||||
|
||||
throw new \RuntimeException("GeneralCache item too large: {$sizeMB}MB (max 5MB)");
|
||||
}
|
||||
|
||||
} catch (\RuntimeException $e) {
|
||||
// Re-throw RuntimeException for size limit violations
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
// Don't break caching, just log the monitoring error
|
||||
error_log("GeneralCache size monitoring failed: " . $e->getMessage());
|
||||
$this->logger?->error(
|
||||
'GeneralCache size monitoring failed',
|
||||
LogContext::withException($e)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ use App\Framework\Cache\Contracts\Scannable;
|
||||
use App\Framework\Cache\Strategies\CacheStrategyManager;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Logging\ValueObjects\LogContext;
|
||||
|
||||
/**
|
||||
* Smart cache implementation with composable strategy support
|
||||
@@ -36,7 +38,8 @@ final class SmartCache implements Cache, DriverAccessible
|
||||
private readonly ?AsyncService $asyncService = null,
|
||||
private readonly bool $asyncEnabled = true,
|
||||
?CacheStrategyManager $strategyManager = null,
|
||||
private readonly bool $enableDefaultStrategies = true
|
||||
private readonly bool $enableDefaultStrategies = true,
|
||||
private readonly ?Logger $logger = null
|
||||
) {
|
||||
// Initialize strategy manager with defaults if enabled and none provided
|
||||
if ($strategyManager === null && $this->enableDefaultStrategies) {
|
||||
@@ -913,50 +916,62 @@ final class SmartCache implements Cache, DriverAccessible
|
||||
}
|
||||
|
||||
/**
|
||||
* EMERGENCY: Monitor cache item sizes to identify memory explosion sources
|
||||
* Monitor cache item sizes to identify memory explosion sources
|
||||
*/
|
||||
private function monitorCacheItemSize(CacheItem $item): void
|
||||
{
|
||||
if ($this->logger === null) {
|
||||
return; // No logging if logger not provided
|
||||
}
|
||||
|
||||
try {
|
||||
// Get serialized size to understand cache impact
|
||||
$serializedValue = serialize($item->value);
|
||||
$sizeBytes = strlen($serializedValue);
|
||||
$sizeKB = round($sizeBytes / 1024, 2);
|
||||
$sizeMB = round($sizeBytes / 1024 / 1024, 2);
|
||||
|
||||
// Log large cache items
|
||||
if ($sizeBytes > 100 * 1024) { // >100KB
|
||||
$keyString = $item->key->toString();
|
||||
$valueType = get_debug_type($item->value);
|
||||
$keyString = (string) $item->key;
|
||||
$pattern = $this->analyzeCacheKey($keyString);
|
||||
|
||||
error_log("🚨 SMART CACHE SIZE WARNING: {$sizeMB}MB");
|
||||
error_log(" 📋 Cache Key: '{$keyString}'");
|
||||
error_log(" 🏷️ Value Type: {$valueType}");
|
||||
|
||||
// If it's an object, get class name and some properties
|
||||
if (is_object($item->value)) {
|
||||
$className = get_class($item->value);
|
||||
$objectInfo = $this->analyzeObject($item->value);
|
||||
error_log(" 🔍 Object Class: {$className}");
|
||||
error_log(" 📊 Object Analysis: {$objectInfo}");
|
||||
}
|
||||
|
||||
// Show cache key pattern analysis
|
||||
$keyAnalysis = $this->analyzeCacheKey($keyString);
|
||||
error_log(" 🔑 Key Pattern: {$keyAnalysis}");
|
||||
// Monitor large cache items (>100KB for SmartCache)
|
||||
if ($sizeBytes > 100 * 1024) {
|
||||
$this->logger->warning(
|
||||
'Large cache item detected in SmartCache',
|
||||
LogContext::withData([
|
||||
'key' => $keyString,
|
||||
'size_kb' => $sizeKB,
|
||||
'size_mb' => $sizeMB,
|
||||
'pattern' => $pattern,
|
||||
'threshold' => '100KB',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// EMERGENCY: Block extremely large cache items
|
||||
if ($sizeBytes > 10 * 1024 * 1024) { // >10MB
|
||||
error_log("🛑 SMART CACHE BLOCK: Refusing to cache {$sizeMB}MB item");
|
||||
error_log(" 📋 Blocked Key: '{$keyString}'");
|
||||
error_log(" 🚫 Reason: Exceeds 10MB limit");
|
||||
if ($sizeBytes > 10 * 1024 * 1024) { // >10MB for SmartCache (more lenient)
|
||||
$this->logger->error(
|
||||
'SmartCache BLOCK: Refusing to cache oversized item',
|
||||
LogContext::withData([
|
||||
'key' => $keyString,
|
||||
'size_mb' => $sizeMB,
|
||||
'limit_mb' => 10,
|
||||
'pattern' => $pattern,
|
||||
'action' => 'blocked',
|
||||
])
|
||||
);
|
||||
|
||||
throw new \RuntimeException("Cache item too large: {$sizeMB}MB (max 10MB)");
|
||||
throw new \RuntimeException("SmartCache item too large: {$sizeMB}MB (max 10MB)");
|
||||
}
|
||||
|
||||
} catch (\RuntimeException $e) {
|
||||
// Re-throw RuntimeException for size limit violations
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
// Don't break caching, just log the monitoring error
|
||||
error_log("Cache size monitoring failed: " . $e->getMessage());
|
||||
$this->logger?->error(
|
||||
'SmartCache size monitoring failed',
|
||||
LogContext::withException($e)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Access pattern tracking for adaptive TTL calculation
|
||||
*/
|
||||
|
||||
@@ -203,100 +203,3 @@ final class AdaptiveTtlCacheStrategy implements CacheStrategy
|
||||
return Duration::fromSeconds($boundedSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access pattern tracking for adaptive TTL calculation
|
||||
*/
|
||||
final class AccessPattern
|
||||
{
|
||||
/** @var array<int, Timestamp> */
|
||||
private array $accessTimes = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly int $windowSize
|
||||
) {
|
||||
}
|
||||
|
||||
public function recordAccess(): void
|
||||
{
|
||||
$this->accessTimes[] = Timestamp::now();
|
||||
|
||||
// Keep only recent accesses within the window
|
||||
if (count($this->accessTimes) > $this->windowSize) {
|
||||
array_shift($this->accessTimes);
|
||||
}
|
||||
}
|
||||
|
||||
public function getRecentAccessCount(): int
|
||||
{
|
||||
return count($this->accessTimes);
|
||||
}
|
||||
|
||||
public function getTotalAccesses(): int
|
||||
{
|
||||
return count($this->accessTimes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate access frequency (accesses per hour)
|
||||
*/
|
||||
public function getAccessFrequency(): float
|
||||
{
|
||||
if (count($this->accessTimes) < 2) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$oldest = $this->accessTimes[0];
|
||||
$newest = end($this->accessTimes);
|
||||
$timeSpan = $newest->diff($oldest);
|
||||
|
||||
if ($timeSpan->toSeconds() <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$hours = $timeSpan->toSeconds() / 3600.0;
|
||||
|
||||
return count($this->accessTimes) / $hours;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics for adaptive TTL optimization
|
||||
*/
|
||||
final class AdaptiveTtlStats
|
||||
{
|
||||
private int $hits = 0;
|
||||
|
||||
private int $misses = 0;
|
||||
|
||||
public function recordHitMiss(bool $isHit): void
|
||||
{
|
||||
if ($isHit) {
|
||||
$this->hits++;
|
||||
} else {
|
||||
$this->misses++;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHitRate(): float
|
||||
{
|
||||
$total = $this->hits + $this->misses;
|
||||
|
||||
return $total > 0 ? ($this->hits / $total) : 0.0;
|
||||
}
|
||||
|
||||
public function getTotalRequests(): int
|
||||
{
|
||||
return $this->hits + $this->misses;
|
||||
}
|
||||
|
||||
public function getHits(): int
|
||||
{
|
||||
return $this->hits;
|
||||
}
|
||||
|
||||
public function getMisses(): int
|
||||
{
|
||||
return $this->misses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,105 +335,3 @@ final class HeatMapCacheStrategy implements CacheStrategy
|
||||
return 'Monitor and analyze access patterns';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Heat map entry for tracking cache key usage
|
||||
*/
|
||||
final class HeatMapEntry
|
||||
{
|
||||
/** @var array<array{timestamp: Timestamp, is_hit: bool, retrieval_time: ?float}> */
|
||||
private array $accesses = [];
|
||||
|
||||
private int $totalHits = 0;
|
||||
|
||||
private int $totalMisses = 0;
|
||||
|
||||
private float $totalRetrievalTime = 0.0;
|
||||
|
||||
private int $retrievalTimeCount = 0;
|
||||
|
||||
public function __construct(
|
||||
private readonly CacheKey $key
|
||||
) {
|
||||
}
|
||||
|
||||
public function recordAccess(bool $isHit, ?Duration $retrievalTime = null): void
|
||||
{
|
||||
$this->accesses[] = [
|
||||
'timestamp' => Timestamp::now(),
|
||||
'is_hit' => $isHit,
|
||||
'retrieval_time' => $retrievalTime?->toSeconds(),
|
||||
];
|
||||
|
||||
if ($isHit) {
|
||||
$this->totalHits++;
|
||||
} else {
|
||||
$this->totalMisses++;
|
||||
}
|
||||
|
||||
if ($retrievalTime !== null) {
|
||||
$this->totalRetrievalTime += $retrievalTime->toSeconds();
|
||||
$this->retrievalTimeCount++;
|
||||
}
|
||||
|
||||
// Keep only recent data to prevent memory bloat
|
||||
$cutoff = Timestamp::now()->subtract(Duration::fromHours(48)); // Keep 48 hours
|
||||
$this->accesses = array_filter(
|
||||
$this->accesses,
|
||||
fn ($access) => $access['timestamp']->isAfter($cutoff)
|
||||
);
|
||||
}
|
||||
|
||||
public function getRecentAccesses(Timestamp $since): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->accesses,
|
||||
fn ($access) => $access['timestamp']->isAfter($since)
|
||||
);
|
||||
}
|
||||
|
||||
public function getHitRate(): float
|
||||
{
|
||||
$total = $this->totalHits + $this->totalMisses;
|
||||
|
||||
return $total > 0 ? ($this->totalHits / $total) : 0.0;
|
||||
}
|
||||
|
||||
public function getAverageRetrievalTime(): float
|
||||
{
|
||||
return $this->retrievalTimeCount > 0 ? ($this->totalRetrievalTime / $this->retrievalTimeCount) : 0.0;
|
||||
}
|
||||
|
||||
public function getTotalAccesses(): int
|
||||
{
|
||||
return count($this->accesses);
|
||||
}
|
||||
|
||||
public function getLastAccessTime(): ?Timestamp
|
||||
{
|
||||
if (empty($this->accesses)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return end($this->accesses)['timestamp'];
|
||||
}
|
||||
|
||||
public function getKey(): CacheKey
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write operation tracking
|
||||
*/
|
||||
final readonly class WriteOperation
|
||||
{
|
||||
public function __construct(
|
||||
public CacheKey $key,
|
||||
public int $valueSize,
|
||||
public Duration $writeTime,
|
||||
public Timestamp $timestamp
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Heat map entry for tracking cache key usage
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Prediction pattern for a cache key
|
||||
*/
|
||||
|
||||
@@ -450,100 +450,3 @@ final class PredictiveCacheStrategy implements CacheStrategy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prediction pattern for a cache key
|
||||
*/
|
||||
final class PredictionPattern
|
||||
{
|
||||
/** @var array<array{timestamp: Timestamp, context: array}> */
|
||||
private array $accesses = [];
|
||||
|
||||
/** @var array<CacheKey> */
|
||||
private array $dependencies = [];
|
||||
|
||||
private mixed $warmingCallback = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly CacheKey $key
|
||||
) {
|
||||
}
|
||||
|
||||
public function recordAccess(Timestamp $timestamp, array $context = []): void
|
||||
{
|
||||
$this->accesses[] = [
|
||||
'timestamp' => $timestamp,
|
||||
'context' => $context,
|
||||
];
|
||||
|
||||
// Keep only recent accesses to prevent memory bloat
|
||||
$cutoff = $timestamp->subtract(Duration::fromHours(168)); // 1 week
|
||||
$this->accesses = array_filter(
|
||||
$this->accesses,
|
||||
fn ($access) => $access['timestamp']->isAfter($cutoff)
|
||||
);
|
||||
}
|
||||
|
||||
public function addDependency(CacheKey $dependentKey): void
|
||||
{
|
||||
$this->dependencies[] = $dependentKey;
|
||||
}
|
||||
|
||||
public function setWarmingCallback(callable $callback): void
|
||||
{
|
||||
$this->warmingCallback = $callback;
|
||||
}
|
||||
|
||||
public function getKey(): CacheKey
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getRecentAccesses(int $hours): array
|
||||
{
|
||||
$cutoff = Timestamp::now()->subtract(Duration::fromHours($hours));
|
||||
|
||||
return array_filter(
|
||||
$this->accesses,
|
||||
fn ($access) => $access['timestamp']->isAfter($cutoff)
|
||||
);
|
||||
}
|
||||
|
||||
public function getDependencies(): array
|
||||
{
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
public function getWarmingCallback(): ?callable
|
||||
{
|
||||
return $this->warmingCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Active warming job
|
||||
*/
|
||||
final readonly class WarmingJob
|
||||
{
|
||||
public function __construct(
|
||||
public CacheKey $key,
|
||||
public mixed $callback,
|
||||
public string $reason,
|
||||
public Timestamp $startTime
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Warming operation result
|
||||
*/
|
||||
final readonly class WarmingResult
|
||||
{
|
||||
public function __construct(
|
||||
public CacheKey $key,
|
||||
public bool $successful,
|
||||
public Duration $duration,
|
||||
public string $reason
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Active warming job
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
|
||||
/**
|
||||
* Warming operation result
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace App\Framework\Cache\Strategies;
|
||||
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Write operation tracking
|
||||
*/
|
||||
|
||||
@@ -50,8 +50,8 @@ final readonly class CriticalPathWarmingStrategy extends BaseWarmupStrategy
|
||||
'ttl' => Duration::fromDays(7)
|
||||
],
|
||||
[
|
||||
'key' => 'routes_dynamic',
|
||||
'loader' => fn() => $this->compiledRoutes->getDynamicRoutes(),
|
||||
'key' => 'routes_stats',
|
||||
'loader' => fn() => $this->compiledRoutes->getStats(),
|
||||
'ttl' => Duration::fromDays(7)
|
||||
],
|
||||
[
|
||||
@@ -77,7 +77,7 @@ final readonly class CriticalPathWarmingStrategy extends BaseWarmupStrategy
|
||||
$data = $item['loader']();
|
||||
$ttl = $item['ttl'];
|
||||
|
||||
$cacheItem = CacheItem::forSetting(
|
||||
$cacheItem = CacheItem::forSet(
|
||||
key: $key,
|
||||
value: $data,
|
||||
ttl: $ttl
|
||||
@@ -110,9 +110,10 @@ final readonly class CriticalPathWarmingStrategy extends BaseWarmupStrategy
|
||||
|
||||
protected function getMetadata(): array
|
||||
{
|
||||
$stats = $this->compiledRoutes->getStats();
|
||||
|
||||
return [
|
||||
'routes_count' => count($this->compiledRoutes->getStaticRoutes()) +
|
||||
count($this->compiledRoutes->getDynamicRoutes()),
|
||||
'routes_count' => $stats['static_routes'] + $stats['dynamic_patterns'],
|
||||
'config_keys' => 5,
|
||||
'env_vars' => 4
|
||||
];
|
||||
|
||||
@@ -92,7 +92,7 @@ final readonly class PredictiveWarmingStrategy extends BaseWarmupStrategy
|
||||
$key = CacheKey::fromString(self::ACCESS_PATTERN_CACHE_KEY);
|
||||
$result = $this->cache->get($key);
|
||||
|
||||
if ($result->isHit()) {
|
||||
if ($result->isHit) { // isHit is a property, not a method
|
||||
return $result->value;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ final readonly class WarmupMetrics
|
||||
{
|
||||
$total = $this->totalItemsWarmed + $this->totalItemsFailed;
|
||||
if ($total === 0) {
|
||||
return 1.0;
|
||||
return 0.0;
|
||||
}
|
||||
return $this->totalItemsWarmed / $total;
|
||||
}
|
||||
@@ -75,7 +75,8 @@ final readonly class WarmupMetrics
|
||||
'total_strategies_executed' => $this->totalStrategiesExecuted,
|
||||
'total_items_warmed' => $this->totalItemsWarmed,
|
||||
'total_items_failed' => $this->totalItemsFailed,
|
||||
'total_duration_seconds' => round($this->totalDurationSeconds, 3),
|
||||
'total_duration_seconds' => $this->totalDurationSeconds,
|
||||
'total_memory_used_bytes' => $this->totalMemoryUsedBytes,
|
||||
'total_memory_used_mb' => round($this->getTotalMemoryUsedMB(), 2),
|
||||
'overall_success_rate' => round($this->getOverallSuccessRate() * 100, 2),
|
||||
'average_items_per_second' => round($this->getAverageItemsPerSecond(), 2),
|
||||
|
||||
@@ -41,7 +41,7 @@ final readonly class WarmupResult
|
||||
{
|
||||
$total = $this->itemsWarmed + $this->itemsFailed;
|
||||
if ($total === 0) {
|
||||
return 1.0;
|
||||
return 0.0;
|
||||
}
|
||||
return $this->itemsWarmed / $total;
|
||||
}
|
||||
@@ -65,7 +65,8 @@ final readonly class WarmupResult
|
||||
'strategy_name' => $this->strategyName,
|
||||
'items_warmed' => $this->itemsWarmed,
|
||||
'items_failed' => $this->itemsFailed,
|
||||
'duration_seconds' => round($this->durationSeconds, 3),
|
||||
'duration_seconds' => $this->durationSeconds,
|
||||
'memory_used_bytes' => $this->memoryUsedBytes,
|
||||
'memory_used_mb' => round($this->getMemoryUsedMB(), 2),
|
||||
'success_rate' => round($this->getSuccessRate() * 100, 2),
|
||||
'items_per_second' => round($this->getItemsPerSecond(), 2),
|
||||
|
||||
Reference in New Issue
Block a user