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:
205
src/Framework/Discovery/Memory/MemoryGuard.php
Normal file
205
src/Framework/Discovery/Memory/MemoryGuard.php
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user