Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
/**
* Batch processing parameters
*/
final readonly class BatchParameters
{
public function __construct(
public int $chunkSize,
public int $batchCount,
public Byte $estimatedMemoryPerBatch,
public MemoryStrategy $strategy
) {
}
public function toArray(): array
{
return [
'chunk_size' => $this->chunkSize,
'batch_count' => $this->batchCount,
'estimated_memory_per_batch' => $this->estimatedMemoryPerBatch->toHumanReadable(),
'strategy' => $this->strategy->value,
];
}
}

View File

@@ -0,0 +1,387 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\DateTime\Clock;
use App\Framework\Discovery\Events\MemoryCleanupEvent;
use App\Framework\Discovery\Events\MemoryLeakDetectedEvent;
use App\Framework\Discovery\Events\MemoryPressureEvent;
use App\Framework\Discovery\Events\MemoryStrategyChangedEvent;
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
use App\Framework\Logging\Logger;
use App\Framework\Performance\MemoryMonitor;
/**
* Centralized memory management for Discovery operations
*
* Provides intelligent memory monitoring, adaptive chunking, and leak detection
* to ensure stable and efficient discovery processing regardless of codebase size.
*/
final readonly class DiscoveryMemoryManager
{
private Byte $memoryLimit;
private Byte $warningThreshold;
private Byte $criticalThreshold;
private int $leakDetectionWindow;
public function __construct(
private MemoryStrategy $strategy,
Byte $memoryLimit,
private float $memoryPressureThreshold = 0.8,
private ?MemoryMonitor $memoryMonitor = null,
private ?Logger $logger = null,
private ?EventDispatcher $eventDispatcher = null,
private ?Clock $clock = null
) {
$this->memoryLimit = $memoryLimit;
$this->warningThreshold = $memoryLimit->multiply($this->memoryPressureThreshold);
$this->criticalThreshold = $memoryLimit->multiply(0.95);
$this->leakDetectionWindow = 50; // Check for leaks every 50 operations
}
/**
* Create memory manager with auto-suggested strategy
*/
public static function createWithSuggestion(
Byte $memoryLimit,
int $estimatedFileCount,
?MemoryMonitor $memoryMonitor = null,
?Logger $logger = null
): self {
$strategy = MemoryStrategy::suggestForDiscovery(
$estimatedFileCount,
(int) $memoryLimit->toMegabytes(),
$memoryMonitor !== null
);
return new self($strategy, $memoryLimit, 0.8, $memoryMonitor, $logger);
}
/**
* Get current memory status and emit events if needed
*/
public function getMemoryStatus(?string $context = null): MemoryStatusInfo
{
$currentUsage = Byte::fromBytes(memory_get_usage(true));
$peakUsage = Byte::fromBytes(memory_get_peak_usage(true));
$availableMemory = $this->memoryLimit->subtract($currentUsage);
$memoryPressure = $currentUsage->percentOf($this->memoryLimit);
$status = match (true) {
$currentUsage->greaterThan($this->criticalThreshold) => MemoryStatus::CRITICAL,
$currentUsage->greaterThan($this->warningThreshold) => MemoryStatus::WARNING,
default => MemoryStatus::NORMAL
};
// Emit memory pressure event if not normal
if ($status !== MemoryStatus::NORMAL && $this->eventDispatcher && $this->clock) {
$this->eventDispatcher->dispatch(new MemoryPressureEvent(
status: $status,
currentUsage: $currentUsage,
memoryLimit: $this->memoryLimit,
memoryPressure: $memoryPressure,
strategy: $this->strategy,
context: $context,
timestamp: $this->clock->time()
));
}
return new MemoryStatusInfo(
status: $status,
currentUsage: $currentUsage,
peakUsage: $peakUsage,
memoryLimit: $this->memoryLimit,
availableMemory: $availableMemory,
memoryPressure: $memoryPressure,
strategy: $this->strategy
);
}
/**
* Calculate optimal chunk size based on current memory pressure
*/
public function calculateOptimalChunkSize(int $totalItems): int
{
$memoryInfo = $this->getMemoryStatus();
$baseChunkSize = $this->strategy->getDefaultChunkSize();
// Adaptive strategy adjusts chunk size based on memory pressure
if ($this->strategy->supportsDynamicAdjustment()) {
$pressureRatio = $memoryInfo->memoryPressure->toDecimal();
// Reduce chunk size as memory pressure increases
$adjustmentFactor = match (true) {
$pressureRatio > 0.9 => 0.25, // Critical: Very small chunks
$pressureRatio > 0.8 => 0.5, // High pressure: Half size
$pressureRatio > 0.6 => 0.75, // Medium pressure: 3/4 size
default => 1.0 // Normal: Full size
};
$adjustedChunkSize = (int) ($baseChunkSize * $adjustmentFactor);
} else {
$adjustedChunkSize = $baseChunkSize;
}
// Ensure chunk size is reasonable
$minChunkSize = 1;
$maxChunkSize = min($totalItems, 1000);
return max($minChunkSize, min($adjustedChunkSize, $maxChunkSize));
}
/**
* Check if memory cleanup is needed
*/
public function shouldCleanup(int $processedItems): bool
{
$cleanupFrequency = $this->strategy->getCleanupFrequency();
// Always cleanup if memory pressure is high
$memoryInfo = $this->getMemoryStatus();
if ($memoryInfo->status === MemoryStatus::WARNING || $memoryInfo->status === MemoryStatus::CRITICAL) {
return true;
}
// Regular cleanup based on strategy
return $processedItems > 0 && ($processedItems % $cleanupFrequency) === 0;
}
/**
* Perform memory cleanup and emit cleanup event
*/
public function performCleanup(bool $isEmergency = false, ?string $triggerReason = null, ?string $context = null): MemoryCleanupResult
{
$beforeUsage = Byte::fromBytes(memory_get_usage(true));
$beforePeak = Byte::fromBytes(memory_get_peak_usage(true));
// Force garbage collection
$collectedCycles = gc_collect_cycles();
// Clear any internal caches that might be holding references
if (function_exists('opcache_reset')) {
opcache_reset();
}
$afterUsage = Byte::fromBytes(memory_get_usage(true));
$afterPeak = Byte::fromBytes(memory_get_peak_usage(true));
$memoryFreed = $beforeUsage->greaterThan($afterUsage)
? $beforeUsage->subtract($afterUsage)
: Byte::zero();
$result = new MemoryCleanupResult(
beforeUsage: $beforeUsage,
afterUsage: $afterUsage,
memoryFreed: $memoryFreed,
collectedCycles: $collectedCycles
);
// Emit cleanup event
if ($this->eventDispatcher && $this->clock) {
$this->eventDispatcher->dispatch(new MemoryCleanupEvent(
beforeUsage: $beforeUsage,
afterUsage: $afterUsage,
memoryFreed: $memoryFreed,
collectedCycles: $collectedCycles,
wasEmergency: $isEmergency,
triggerReason: $triggerReason,
context: $context,
timestamp: $this->clock->time()
));
}
$this->logger?->debug('Memory cleanup performed', [
'before_usage' => $beforeUsage->toHumanReadable(),
'after_usage' => $afterUsage->toHumanReadable(),
'memory_freed' => $memoryFreed->toHumanReadable(),
'collected_cycles' => $collectedCycles,
'strategy' => $this->strategy->value,
'was_emergency' => $isEmergency,
'trigger_reason' => $triggerReason,
]);
return $result;
}
/**
* Check for potential memory leaks and emit leak event if detected
*/
public function checkForMemoryLeaks(array $memoryHistory, ?string $context = null): ?MemoryLeakInfo
{
if (count($memoryHistory) < $this->leakDetectionWindow) {
return null;
}
// Take recent measurements
$recentHistory = array_slice($memoryHistory, -$this->leakDetectionWindow);
$usageValues = array_map(fn (Byte $usage) => $usage->toBytes(), $recentHistory);
// Calculate trend using linear regression
$n = count($usageValues);
$sumX = array_sum(range(0, $n - 1));
$sumY = array_sum($usageValues);
$sumXY = 0;
$sumXX = 0;
foreach ($usageValues as $i => $y) {
$sumXY += $i * $y;
$sumXX += $i * $i;
}
$slope = ($n * $sumXY - $sumX * $sumY) / ($n * $sumXX - $sumX * $sumX);
$intercept = ($sumY - $slope * $sumX) / $n;
// If slope is significantly positive, we might have a leak
$averageUsage = array_sum($usageValues) / $n;
$leakThreshold = $averageUsage * 0.02; // 2% growth per measurement
if ($slope > $leakThreshold) {
$firstUsage = Byte::fromBytes($usageValues[0]);
$lastUsage = Byte::fromBytes($usageValues[$n - 1]);
$growthRate = Byte::fromBytes((int) $slope);
$severity = $this->calculateLeakSeverity($slope, $averageUsage);
$leakInfo = new MemoryLeakInfo(
detectedAt: $lastUsage,
growthRate: $growthRate,
windowSize: $this->leakDetectionWindow,
severity: $severity
);
// Emit memory leak event
if ($this->eventDispatcher && $this->clock) {
$this->eventDispatcher->dispatch(new MemoryLeakDetectedEvent(
detectedAt: $lastUsage,
growthRate: $growthRate,
severity: $severity,
windowSize: $this->leakDetectionWindow,
context: $context,
timestamp: $this->clock->time()
));
}
return $leakInfo;
}
return null;
}
/**
* Get memory guard for continuous monitoring
*/
public function createMemoryGuard(?callable $emergencyCallback = null): MemoryGuard
{
return new MemoryGuard(
memoryManager: $this,
emergencyCallback: $emergencyCallback ?? function () {
$this->logger?->critical('Emergency memory cleanup triggered');
$this->performCleanup();
}
);
}
/**
* Calculate adaptive batch parameters for streaming operations
*/
public function calculateBatchParameters(int $totalItems, Byte $itemSizeEstimate): BatchParameters
{
$memoryInfo = $this->getMemoryStatus();
$availableMemory = $memoryInfo->availableMemory;
// Reserve some memory for processing overhead
$usableMemory = $availableMemory->multiply(0.8);
// Calculate how many items can fit in available memory
$maxItemsInMemory = $itemSizeEstimate->isEmpty()
? $this->strategy->getDefaultChunkSize()
: (int) ($usableMemory->toBytes() / $itemSizeEstimate->toBytes());
$optimalChunkSize = $this->calculateOptimalChunkSize($totalItems);
$finalChunkSize = min($maxItemsInMemory, $optimalChunkSize);
// Calculate number of batches needed
$batchCount = (int) ceil($totalItems / $finalChunkSize);
return new BatchParameters(
chunkSize: max(1, $finalChunkSize),
batchCount: $batchCount,
estimatedMemoryPerBatch: $itemSizeEstimate->multiply($finalChunkSize),
strategy: $this->strategy
);
}
/**
* Get strategy description for debugging
*/
public function getStrategyDescription(): string
{
return $this->strategy->getDescription();
}
/**
* Change memory strategy and emit strategy changed event
*/
public function changeStrategy(MemoryStrategy $newStrategy, string $changeReason, ?array $triggerMetrics = null, ?string $context = null): void
{
if ($newStrategy === $this->strategy) {
return; // No change needed
}
$previousStrategy = $this->strategy;
$this->strategy = $newStrategy;
// Recalculate thresholds for new strategy
$this->warningThreshold = $this->memoryLimit->multiply($this->memoryPressureThreshold);
$this->criticalThreshold = $this->memoryLimit->multiply(0.95);
// Emit strategy changed event
if ($this->eventDispatcher && $this->clock) {
$this->eventDispatcher->dispatch(new MemoryStrategyChangedEvent(
previousStrategy: $previousStrategy,
newStrategy: $newStrategy,
changeReason: $changeReason,
triggerMetrics: $triggerMetrics,
context: $context,
timestamp: $this->clock->time()
));
}
$this->logger?->info('Memory strategy changed', [
'previous_strategy' => $previousStrategy->value,
'new_strategy' => $newStrategy->value,
'change_reason' => $changeReason,
'trigger_metrics' => $triggerMetrics,
'context' => $context,
]);
}
/**
* Get current memory strategy
*/
public function getCurrentStrategy(): MemoryStrategy
{
return $this->strategy;
}
/**
* Calculate memory leak severity
*/
private function calculateLeakSeverity(float $slope, float $averageUsage): LeakSeverity
{
$relativeGrowth = $slope / $averageUsage;
return match (true) {
$relativeGrowth > 0.05 => LeakSeverity::CRITICAL, // 5%+ growth
$relativeGrowth > 0.03 => LeakSeverity::HIGH, // 3-5% growth
$relativeGrowth > 0.02 => LeakSeverity::MEDIUM, // 2-3% growth
default => LeakSeverity::LOW // < 2% growth
};
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
/**
* Guard actions that can be taken
*/
enum GuardAction: string
{
case WARNING_ISSUED = 'warning_issued';
case EMERGENCY_CLEANUP = 'emergency_cleanup';
case CLEANUP_SUGGESTED = 'cleanup_suggested';
case CLEANUP_SUCCESSFUL = 'cleanup_successful';
case CLEANUP_FAILED = 'cleanup_failed';
case CRITICAL_LEAK_DETECTED = 'critical_leak_detected';
case HIGH_LEAK_DETECTED = 'high_leak_detected';
case MEDIUM_LEAK_DETECTED = 'medium_leak_detected';
case LOW_LEAK_DETECTED = 'low_leak_detected';
case EMERGENCY_MODE_RESET = 'emergency_mode_reset';
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
/**
* Result of a guard check
*/
final readonly class GuardResult
{
public function __construct(
public MemoryStatusInfo $memoryStatus,
public array $actions,
public int $checkNumber,
public bool $emergencyMode
) {
}
public function hasAction(GuardAction $action): bool
{
return in_array($action, $this->actions, true);
}
public function hasAnyLeakDetection(): bool
{
return $this->hasAction(GuardAction::CRITICAL_LEAK_DETECTED) ||
$this->hasAction(GuardAction::HIGH_LEAK_DETECTED) ||
$this->hasAction(GuardAction::MEDIUM_LEAK_DETECTED) ||
$this->hasAction(GuardAction::LOW_LEAK_DETECTED);
}
public function requiresImmediateAction(): bool
{
return $this->hasAction(GuardAction::EMERGENCY_CLEANUP) ||
$this->hasAction(GuardAction::CRITICAL_LEAK_DETECTED);
}
public function toArray(): array
{
return [
'memory_status' => $this->memoryStatus->toArray(),
'actions' => array_map(fn ($action) => $action->value, $this->actions),
'check_number' => $this->checkNumber,
'emergency_mode' => $this->emergencyMode,
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
/**
* Guard statistics
*/
final readonly class GuardStatistics
{
public function __construct(
public int $totalChecks,
public MemoryStatusInfo $currentStatus,
public Byte $averageUsage,
public Byte $peakUsage,
public int $historySize,
public bool $emergencyMode
) {
}
public function toArray(): array
{
return [
'total_checks' => $this->totalChecks,
'current_status' => $this->currentStatus->toArray(),
'average_usage' => $this->averageUsage->toHumanReadable(),
'peak_usage' => $this->peakUsage->toHumanReadable(),
'history_size' => $this->historySize,
'emergency_mode' => $this->emergencyMode,
];
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
/**
* Memory leak severity levels
*/
enum LeakSeverity: string
{
case LOW = 'low';
case MEDIUM = 'medium';
case HIGH = 'high';
case CRITICAL = 'critical';
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
/**
* Memory cleanup result
*/
final readonly class MemoryCleanupResult
{
public function __construct(
public Byte $beforeUsage,
public Byte $afterUsage,
public Byte $memoryFreed,
public int $collectedCycles
) {
}
public function wasEffective(): bool
{
return $this->memoryFreed->greaterThan(Byte::zero()) || $this->collectedCycles > 0;
}
}

View File

@@ -0,0 +1,205 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
/**
* Memory guard for continuous memory monitoring during discovery operations
*
* Provides real-time memory monitoring with automatic emergency handling
* to prevent out-of-memory conditions during long-running operations.
*/
final class MemoryGuard
{
private array $memoryHistory = [];
private int $checkCounter = 0;
private bool $emergencyMode = false;
private mixed $emergencyCallback = null;
public function __construct(
private readonly DiscoveryMemoryManager $memoryManager,
?callable $emergencyCallback = null
) {
$this->emergencyCallback = $emergencyCallback;
}
/**
* Check memory status and take action if needed
*/
public function check(): GuardResult
{
$this->checkCounter++;
$memoryStatus = $this->memoryManager->getMemoryStatus();
// Track memory history for leak detection
$this->memoryHistory[] = $memoryStatus->currentUsage;
// Keep only recent history to prevent memory growth
if (count($this->memoryHistory) > 100) {
$this->memoryHistory = array_slice($this->memoryHistory, -50);
}
$actions = [];
// Handle critical memory situations
if ($memoryStatus->status === MemoryStatus::CRITICAL && ! $this->emergencyMode) {
$this->emergencyMode = true;
$actions[] = GuardAction::EMERGENCY_CLEANUP;
if ($this->emergencyCallback) {
($this->emergencyCallback)();
}
// Perform immediate cleanup
$cleanupResult = $this->memoryManager->performCleanup();
$actions[] = $cleanupResult->wasEffective()
? GuardAction::CLEANUP_SUCCESSFUL
: GuardAction::CLEANUP_FAILED;
}
// Handle warning conditions
if ($memoryStatus->status === MemoryStatus::WARNING) {
$actions[] = GuardAction::WARNING_ISSUED;
// Suggest cleanup if it's time
if ($this->memoryManager->shouldCleanup($this->checkCounter)) {
$actions[] = GuardAction::CLEANUP_SUGGESTED;
}
}
// Check for memory leaks periodically
if ($this->checkCounter % 20 === 0) {
$leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
if ($leakInfo !== null) {
$actions[] = match ($leakInfo->severity) {
LeakSeverity::CRITICAL => GuardAction::CRITICAL_LEAK_DETECTED,
LeakSeverity::HIGH => GuardAction::HIGH_LEAK_DETECTED,
LeakSeverity::MEDIUM => GuardAction::MEDIUM_LEAK_DETECTED,
LeakSeverity::LOW => GuardAction::LOW_LEAK_DETECTED
};
}
}
// Reset emergency mode if memory is back to normal
if ($this->emergencyMode && $memoryStatus->status === MemoryStatus::NORMAL) {
$this->emergencyMode = false;
$actions[] = GuardAction::EMERGENCY_MODE_RESET;
}
return new GuardResult(
memoryStatus: $memoryStatus,
actions: $actions,
checkNumber: $this->checkCounter,
emergencyMode: $this->emergencyMode
);
}
/**
* Force emergency cleanup
*/
public function forceEmergencyCleanup(): MemoryCleanupResult
{
$this->emergencyMode = true;
if ($this->emergencyCallback) {
($this->emergencyCallback)();
}
return $this->memoryManager->performCleanup();
}
/**
* Get current memory statistics
*/
public function getStatistics(): GuardStatistics
{
$memoryStatus = $this->memoryManager->getMemoryStatus();
$historyCount = count($this->memoryHistory);
$averageUsage = $historyCount > 0
? Byte::fromBytes((int) (array_sum(array_map(fn ($byte) => $byte->toBytes(), $this->memoryHistory)) / $historyCount))
: Byte::zero();
$peakUsage = $historyCount > 0
? array_reduce($this->memoryHistory, fn ($max, $current) => $current->greaterThan($max ?? Byte::zero()) ? $current : $max, Byte::zero())
: Byte::zero();
return new GuardStatistics(
totalChecks: $this->checkCounter,
currentStatus: $memoryStatus,
averageUsage: $averageUsage,
peakUsage: $peakUsage,
historySize: $historyCount,
emergencyMode: $this->emergencyMode
);
}
/**
* Reset guard state
*/
public function reset(): void
{
$this->memoryHistory = [];
$this->checkCounter = 0;
$this->emergencyMode = false;
}
/**
* Check if it's safe to continue processing
*/
public function isSafeToProcess(): bool
{
$memoryStatus = $this->memoryManager->getMemoryStatus();
return $memoryStatus->status !== MemoryStatus::CRITICAL;
}
/**
* Get recommendations for current memory state
* @return string[]
*/
public function getRecommendations(): array
{
$memoryStatus = $this->memoryManager->getMemoryStatus();
$recommendations = [];
switch ($memoryStatus->status) {
case MemoryStatus::CRITICAL:
$recommendations[] = 'Immediate action required: Reduce batch size or stop processing';
$recommendations[] = 'Consider switching to STREAMING memory strategy';
$recommendations[] = 'Force garbage collection and cleanup caches';
break;
case MemoryStatus::WARNING:
$recommendations[] = 'Consider reducing batch size';
$recommendations[] = 'Enable more frequent cleanup cycles';
$recommendations[] = 'Monitor for memory leaks';
break;
case MemoryStatus::NORMAL:
if ($memoryStatus->memoryPressure->toDecimal() > 0.5) {
$recommendations[] = 'Memory usage is moderate - consider optimization';
}
break;
}
// Check for potential leaks
if (count($this->memoryHistory) > 10) {
$leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
if ($leakInfo !== null) {
$recommendations[] = "Memory leak detected ({$leakInfo->severity->value}): Review object retention";
}
}
return $recommendations;
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
/**
* Memory leak detection information
*/
final readonly class MemoryLeakInfo
{
public function __construct(
public Byte $detectedAt,
public Byte $growthRate,
public int $windowSize,
public LeakSeverity $severity
) {
}
public function toArray(): array
{
return [
'detected_at' => $this->detectedAt->toHumanReadable(),
'growth_rate' => $this->growthRate->toHumanReadable(),
'window_size' => $this->windowSize,
'severity' => $this->severity->value,
];
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
/**
* Memory status enumeration
*/
enum MemoryStatus: string
{
case NORMAL = 'normal';
case WARNING = 'warning';
case CRITICAL = 'critical';
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Discovery\ValueObjects\MemoryStrategy;
/**
* Memory status information
*/
final readonly class MemoryStatusInfo
{
public function __construct(
public MemoryStatus $status,
public Byte $currentUsage,
public Byte $peakUsage,
public Byte $memoryLimit,
public Byte $availableMemory,
public Percentage $memoryPressure,
public MemoryStrategy $strategy
) {
}
public function toArray(): array
{
return [
'status' => $this->status->value,
'current_usage' => $this->currentUsage->toHumanReadable(),
'peak_usage' => $this->peakUsage->toHumanReadable(),
'memory_limit' => $this->memoryLimit->toHumanReadable(),
'available_memory' => $this->availableMemory->toHumanReadable(),
'memory_pressure' => $this->memoryPressure->toDecimal(),
'strategy' => $this->strategy->value,
];
}
}