- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
204 lines
6.5 KiB
PHP
204 lines
6.5 KiB
PHP
<?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 CircularMemoryBuffer $memoryHistory;
|
|
|
|
private int $checkCounter = 0;
|
|
|
|
private bool $emergencyMode = false;
|
|
|
|
private mixed $emergencyCallback = null;
|
|
|
|
public function __construct(
|
|
private readonly DiscoveryMemoryManager $memoryManager,
|
|
?callable $emergencyCallback = null
|
|
) {
|
|
$this->memoryHistory = new CircularMemoryBuffer(maxSize: 100);
|
|
$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 using CircularMemoryBuffer
|
|
$this->memoryHistory->add($memoryStatus->currentUsage);
|
|
|
|
$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 using CircularMemoryBuffer
|
|
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();
|
|
|
|
$samples = $this->memoryHistory->getSamples();
|
|
$historyCount = $this->memoryHistory->getCount();
|
|
|
|
$averageUsage = $historyCount > 0
|
|
? Byte::fromBytes((int) (array_sum(array_map(fn (Byte $byte) => $byte->toBytes(), $samples)) / $historyCount))
|
|
: Byte::zero();
|
|
|
|
$peakUsage = $historyCount > 0
|
|
? array_reduce($samples, 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->clear();
|
|
$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 using CircularMemoryBuffer
|
|
if ($this->memoryHistory->getCount() > 10) {
|
|
$leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
|
|
if ($leakInfo !== null) {
|
|
$recommendations[] = "Memory leak detected ({$leakInfo->severity->value}): Review object retention";
|
|
}
|
|
}
|
|
|
|
return $recommendations;
|
|
}
|
|
}
|