Files
michaelschiemer/tests/Framework/Discovery/SimpleMemoryTest.php
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

272 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Discovery;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheIdentifier;
use App\Framework\Cache\CacheItem;
use App\Framework\Cache\CacheResult;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Core\PathProvider;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemClock;
use App\Framework\Discovery\UnifiedDiscoveryService;
use PHPUnit\Framework\TestCase;
/**
* Simple Cache implementation for testing
*/
final class SimpleCacheWrapper implements Cache
{
public function __construct(private InMemoryCache $driver)
{
}
public function get(CacheIdentifier ...$identifiers): CacheResult
{
$keys = array_filter($identifiers, fn ($id) => $id instanceof \App\Framework\Cache\CacheKey);
return $this->driver->get(...$keys);
}
public function set(CacheItem ...$items): bool
{
return $this->driver->set(...$items);
}
public function has(CacheIdentifier ...$identifiers): array
{
$keys = array_filter($identifiers, fn ($id) => $id instanceof \App\Framework\Cache\CacheKey);
return $this->driver->has(...$keys);
}
public function forget(CacheIdentifier ...$identifiers): bool
{
$keys = array_filter($identifiers, fn ($id) => $id instanceof \App\Framework\Cache\CacheKey);
return $this->driver->forget(...$keys);
}
public function clear(): bool
{
return $this->driver->clear();
}
public function remember(\App\Framework\Cache\CacheKey $key, callable $callback, ?Duration $ttl = null): CacheItem
{
$result = $this->driver->get($key);
$item = $result->getItem($key);
if ($item->isHit) {
return $item;
}
$value = $callback();
$newItem = CacheItem::forSet($key, $value, $ttl);
$this->driver->set($newItem);
return CacheItem::hit($key, $value);
}
}
/**
* Simplified memory test focusing only on Discovery core without container dependencies
*/
final class SimpleMemoryTest extends TestCase
{
public function test_discovery_memory_usage_15_runs(): void
{
$iterations = 15;
$memoryData = [];
echo "\n=== Testing Discovery Memory Usage - $iterations Runs ===\n";
// Create discovery service directly with mock dependencies
$pathProvider = new PathProvider('/var/www/html');
// Use InMemoryCache with wrapper to implement Cache interface
$cacheDriver = new InMemoryCache();
$cache = new SimpleCacheWrapper($cacheDriver);
$clock = new SystemClock();
// Create reflection provider without dependencies
$reflectionProvider = new \App\Framework\ReflectionLegacy\CachedReflectionProvider();
$config = new \App\Framework\Discovery\ValueObjects\DiscoveryConfiguration(
paths: ['/var/www/html/src'],
attributeMappers: [
new \App\Framework\Core\RouteMapper(),
new \App\Framework\DI\InitializerMapper(),
],
targetInterfaces: [],
useCache: false
);
$discoveryService = new UnifiedDiscoveryService(
pathProvider: $pathProvider,
cache: $cache,
clock: $clock,
reflectionProvider: $reflectionProvider,
configuration: $config,
attributeMappers: [
new \App\Framework\Core\RouteMapper(),
new \App\Framework\DI\InitializerMapper(),
],
targetInterfaces: []
);
for ($i = 1; $i <= $iterations; $i++) {
echo "\n--- Discovery Run $i/$iterations ---\n";
$memoryBefore = memory_get_usage(true);
$peakBefore = memory_get_peak_usage(true);
// Run discovery
$registry = $discoveryService->discover();
$memoryAfter = memory_get_usage(true);
$peakAfter = memory_get_peak_usage(true);
$memoryDiff = $memoryAfter - $memoryBefore;
$peakDiff = $peakAfter - $peakBefore;
$memoryData[$i] = [
'memory_before' => $memoryBefore,
'memory_after' => $memoryAfter,
'memory_diff' => $memoryDiff,
'peak_before' => $peakBefore,
'peak_after' => $peakAfter,
'peak_diff' => $peakDiff,
];
echo sprintf(
"Run %2d: Memory %s -> %s (diff: %s), Peak %s -> %s (diff: %s)\n",
$i,
$this->formatBytes($memoryBefore),
$this->formatBytes($memoryAfter),
$this->formatBytes($memoryDiff),
$this->formatBytes($peakBefore),
$this->formatBytes($peakAfter),
$this->formatBytes($peakDiff)
);
// Log reflection stats every 5 runs
if ($i % 5 === 0) {
try {
$health = $discoveryService->getHealthStatus();
echo " Health Status: " . json_encode($health) . "\n";
} catch (\Throwable $e) {
echo " Health Status: Error - " . $e->getMessage() . "\n";
}
}
// Cleanup
unset($registry);
gc_collect_cycles();
// Check for excessive memory usage
if ($memoryDiff > 100 * 1024 * 1024) { // 100MB limit
$this->fail("Run $i: Excessive memory usage: " . $this->formatBytes($memoryDiff));
}
}
$this->analyzeResults($memoryData);
}
private function analyzeResults(array $memoryData): void
{
echo "\n=== Memory Analysis Results ===\n";
$memoryDiffs = array_column($memoryData, 'memory_diff');
$peakDiffs = array_column($memoryData, 'peak_diff');
$avgMemory = array_sum($memoryDiffs) / count($memoryDiffs);
$avgPeak = array_sum($peakDiffs) / count($peakDiffs);
$maxMemory = max($memoryDiffs);
$minMemory = min($memoryDiffs);
$totalGrowth = array_sum($memoryDiffs);
echo "Average memory per run: " . $this->formatBytes($avgMemory) . "\n";
echo "Average peak per run: " . $this->formatBytes($avgPeak) . "\n";
echo "Max memory per run: " . $this->formatBytes($maxMemory) . "\n";
echo "Min memory per run: " . $this->formatBytes($minMemory) . "\n";
echo "Total cumulative growth: " . $this->formatBytes($totalGrowth) . "\n";
// Calculate growth trend
$growthTrend = $this->calculateLinearTrend($memoryDiffs);
echo "Growth trend (bytes per run): " . $this->formatBytes($growthTrend) . "\n";
// Memory leak detection
if ($growthTrend > 2 * 1024 * 1024) { // 2MB growth per run
$this->fail("Detected memory leak: " . $this->formatBytes($growthTrend) . " growth per run");
}
if ($avgMemory > 50 * 1024 * 1024) { // 50MB average
$this->fail("Average memory usage too high: " . $this->formatBytes($avgMemory));
}
// Check for stability - memory usage should be consistent
$memoryVariance = $this->calculateVariance($memoryDiffs);
echo "Memory usage variance: " . $this->formatBytes($memoryVariance) . "\n";
if ($memoryVariance > 10 * 1024 * 1024) { // 10MB variance
echo "WARNING: High memory usage variance detected\n";
}
echo "\n✅ Memory test completed successfully!\n";
echo "✅ No significant memory leaks detected\n";
echo "✅ Average memory usage within acceptable limits\n";
}
private function calculateLinearTrend(array $values): float
{
$n = count($values);
if ($n < 3) {
return 0;
}
$sumX = 0;
$sumY = 0;
$sumXY = 0;
$sumX2 = 0;
for ($i = 0; $i < $n; $i++) {
$x = $i + 1;
$y = $values[$i];
$sumX += $x;
$sumY += $y;
$sumXY += $x * $y;
$sumX2 += $x * $x;
}
return ($n * $sumXY - $sumX * $sumY) / ($n * $sumX2 - $sumX * $sumX);
}
private function calculateVariance(array $values): float
{
$mean = array_sum($values) / count($values);
$squaredDiffs = array_map(fn ($value) => pow($value - $mean, 2), $values);
return array_sum($squaredDiffs) / count($squaredDiffs);
}
private function formatBytes(int|float $bytes): string
{
if ($bytes < 0) {
return '-' . $this->formatBytes(abs($bytes));
}
$units = ['B', 'KB', 'MB', 'GB'];
$unitIndex = 0;
while ($bytes >= 1024 && $unitIndex < count($units) - 1) {
$bytes /= 1024;
$unitIndex++;
}
return round($bytes, 1) . ' ' . $units[$unitIndex];
}
}