docs: consolidate documentation into organized structure

- 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
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,176 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Memory;
use App\Framework\Core\ValueObjects\Byte;
/**
* Memory-efficient circular buffer for tracking memory usage history
*
* Replaces inefficient array operations with fixed-size circular buffer
* for constant O(1) memory usage regardless of operation count.
*/
final class CircularMemoryBuffer
{
private array $buffer;
private int $head = 0;
private int $count = 0;
public function __construct(
private readonly int $maxSize = 50
) {
$this->buffer = array_fill(0, $maxSize, null);
}
/**
* Add memory usage sample to buffer
*/
public function add(Byte $usage): void
{
$this->buffer[$this->head] = $usage;
$this->head = ($this->head + 1) % $this->maxSize;
if ($this->count < $this->maxSize) {
$this->count++;
}
}
/**
* Get all current samples in chronological order
* @return Byte[]
*/
public function getSamples(): array
{
if ($this->count === 0) {
return [];
}
$samples = [];
// If buffer isn't full yet, return from start to head
if ($this->count < $this->maxSize) {
for ($i = 0; $i < $this->count; $i++) {
$samples[] = $this->buffer[$i];
}
} else {
// Buffer is full, return in correct chronological order
for ($i = 0; $i < $this->maxSize; $i++) {
$index = ($this->head + $i) % $this->maxSize;
$samples[] = $this->buffer[$index];
}
}
return $samples;
}
/**
* Get most recent N samples
* @return Byte[]
*/
public function getRecentSamples(int $n): array
{
if ($n <= 0 || $this->count === 0) {
return [];
}
$samples = [];
$samplesToGet = min($n, $this->count);
for ($i = 0; $i < $samplesToGet; $i++) {
// Calculate index for the i-th most recent sample
$index = ($this->head - 1 - $i + $this->maxSize) % $this->maxSize;
$samples[] = $this->buffer[$index];
}
// Reverse to get chronological order (oldest first)
return array_reverse($samples);
}
/**
* Get latest (most recent) sample
*/
public function getLatest(): ?Byte
{
if ($this->count === 0) {
return null;
}
$latestIndex = ($this->head - 1 + $this->maxSize) % $this->maxSize;
return $this->buffer[$latestIndex];
}
/**
* Check if buffer has minimum number of samples for analysis
*/
public function hasMinimumSamples(int $minimum): bool
{
return $this->count >= $minimum;
}
/**
* Get current number of samples in buffer
*/
public function getCount(): int
{
return $this->count;
}
/**
* Get maximum buffer size
*/
public function getMaxSize(): int
{
return $this->maxSize;
}
/**
* Check if buffer is full
*/
public function isFull(): bool
{
return $this->count >= $this->maxSize;
}
/**
* Clear all samples from buffer
*/
public function clear(): void
{
$this->buffer = array_fill(0, $this->maxSize, null);
$this->head = 0;
$this->count = 0;
}
/**
* Get buffer statistics for debugging
*/
public function getStatistics(): array
{
if ($this->count === 0) {
return [
'count' => 0,
'max_size' => $this->maxSize,
'is_full' => false,
'memory_usage_bytes' => $this->maxSize * 8, // Rough estimate
];
}
$samples = $this->getSamples();
$usageBytes = array_map(fn (Byte $b) => $b->toBytes(), $samples);
return [
'count' => $this->count,
'max_size' => $this->maxSize,
'is_full' => $this->isFull(),
'min_usage' => min($usageBytes),
'max_usage' => max($usageBytes),
'avg_usage' => (int) (array_sum($usageBytes) / count($usageBytes)),
'memory_usage_bytes' => $this->maxSize * 8, // Fixed size
];
}
}

View File

@@ -13,6 +13,7 @@ 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\Logging\ValueObjects\LogContext;
use App\Framework\Performance\MemoryMonitor;
/**
@@ -197,7 +198,7 @@ final readonly class DiscoveryMemoryManager
));
}
$this->logger?->debug('Memory cleanup performed', [
$this->logger?->debug('Memory cleanup performed', LogContext::withData([
'before_usage' => $beforeUsage->toHumanReadable(),
'after_usage' => $afterUsage->toHumanReadable(),
'memory_freed' => $memoryFreed->toHumanReadable(),
@@ -205,22 +206,22 @@ final readonly class DiscoveryMemoryManager
'strategy' => $this->strategy->value,
'was_emergency' => $isEmergency,
'trigger_reason' => $triggerReason,
]);
]));
return $result;
}
/**
* Check for potential memory leaks and emit leak event if detected
* Check for potential memory leaks using CircularMemoryBuffer
*/
public function checkForMemoryLeaks(array $memoryHistory, ?string $context = null): ?MemoryLeakInfo
public function checkForMemoryLeaks(CircularMemoryBuffer $memoryBuffer, ?string $context = null): ?MemoryLeakInfo
{
if (count($memoryHistory) < $this->leakDetectionWindow) {
if (! $memoryBuffer->hasMinimumSamples($this->leakDetectionWindow)) {
return null;
}
// Take recent measurements
$recentHistory = array_slice($memoryHistory, -$this->leakDetectionWindow);
// Get recent measurements from circular buffer (O(1) operation)
$recentHistory = $memoryBuffer->getRecentSamples($this->leakDetectionWindow);
$usageValues = array_map(fn (Byte $usage) => $usage->toBytes(), $recentHistory);
// Calculate trend using linear regression
@@ -353,13 +354,13 @@ final readonly class DiscoveryMemoryManager
));
}
$this->logger?->info('Memory strategy changed', [
$this->logger?->info('Memory strategy changed', LogContext::withData([
'previous_strategy' => $previousStrategy->value,
'new_strategy' => $newStrategy->value,
'change_reason' => $changeReason,
'trigger_metrics' => $triggerMetrics,
'context' => $context,
]);
]));
}
/**

View File

@@ -14,7 +14,7 @@ use App\Framework\Core\ValueObjects\Byte;
*/
final class MemoryGuard
{
private array $memoryHistory = [];
private CircularMemoryBuffer $memoryHistory;
private int $checkCounter = 0;
@@ -26,6 +26,7 @@ final class MemoryGuard
private readonly DiscoveryMemoryManager $memoryManager,
?callable $emergencyCallback = null
) {
$this->memoryHistory = new CircularMemoryBuffer(maxSize: 100);
$this->emergencyCallback = $emergencyCallback;
}
@@ -37,13 +38,8 @@ final class MemoryGuard
$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);
}
// Track memory history for leak detection using CircularMemoryBuffer
$this->memoryHistory->add($memoryStatus->currentUsage);
$actions = [];
@@ -73,7 +69,7 @@ final class MemoryGuard
}
}
// Check for memory leaks periodically
// Check for memory leaks periodically using CircularMemoryBuffer
if ($this->checkCounter % 20 === 0) {
$leakInfo = $this->memoryManager->checkForMemoryLeaks($this->memoryHistory);
if ($leakInfo !== null) {
@@ -121,13 +117,15 @@ final class MemoryGuard
{
$memoryStatus = $this->memoryManager->getMemoryStatus();
$historyCount = count($this->memoryHistory);
$samples = $this->memoryHistory->getSamples();
$historyCount = $this->memoryHistory->getCount();
$averageUsage = $historyCount > 0
? Byte::fromBytes((int) (array_sum(array_map(fn ($byte) => $byte->toBytes(), $this->memoryHistory)) / $historyCount))
? Byte::fromBytes((int) (array_sum(array_map(fn (Byte $byte) => $byte->toBytes(), $samples)) / $historyCount))
: Byte::zero();
$peakUsage = $historyCount > 0
? array_reduce($this->memoryHistory, fn ($max, $current) => $current->greaterThan($max ?? Byte::zero()) ? $current : $max, Byte::zero())
? array_reduce($samples, fn ($max, $current) => $current->greaterThan($max ?? Byte::zero()) ? $current : $max, Byte::zero())
: Byte::zero();
return new GuardStatistics(
@@ -145,7 +143,7 @@ final class MemoryGuard
*/
public function reset(): void
{
$this->memoryHistory = [];
$this->memoryHistory->clear();
$this->checkCounter = 0;
$this->emergencyMode = false;
}
@@ -192,8 +190,8 @@ final class MemoryGuard
break;
}
// Check for potential leaks
if (count($this->memoryHistory) > 10) {
// 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";