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,295 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Queue\Performance;
use App\Framework\Queue\Jobs\Job;
use App\Framework\Queue\Jobs\JobRequest;
use App\Framework\Queue\Jobs\JobResult;
use App\Framework\Queue\Jobs\JobStatus;
use App\Framework\Queue\Workers\Worker;
use App\Framework\Queue\Workers\WorkerCapacity;
use App\Framework\Queue\Workers\WorkerId;
use App\Framework\Queue\Workers\WorkerStatus;
use App\Framework\Queue\Queue\QueueName;
use App\Framework\Queue\Queue\JobPriority;
final readonly class PerformanceTestHelper
{
public static function createTestWorker(
string $id = null,
int $capacity = 10,
WorkerStatus $status = WorkerStatus::AVAILABLE
): Worker {
return new Worker(
id: new WorkerId($id ?? uniqid('worker_')),
queueNames: [new QueueName('test_queue')],
capacity: new WorkerCapacity($capacity),
status: $status,
lastHeartbeat: new \DateTimeImmutable(),
metadata: []
);
}
public static function createTestJob(
string $id = null,
JobPriority $priority = JobPriority::NORMAL,
array $payload = []
): Job {
return new Job(
id: $id ?? uniqid('job_'),
request: new JobRequest(
type: 'test_job',
payload: $payload ?: ['test' => 'data'],
queue: new QueueName('test_queue'),
priority: $priority
),
status: JobStatus::PENDING,
createdAt: new \DateTimeImmutable(),
attempts: 0
);
}
public static function createBulkJobs(int $count, JobPriority $priority = JobPriority::NORMAL): array
{
$jobs = [];
for ($i = 0; $i < $count; $i++) {
$jobs[] = self::createTestJob(
id: "job_{$i}",
priority: $priority,
payload: ['batch_id' => $i, 'data' => str_repeat('x', 100)]
);
}
return $jobs;
}
public static function measureTime(callable $operation): float
{
$start = microtime(true);
$operation();
$end = microtime(true);
return ($end - $start) * 1000; // Return milliseconds
}
public static function measureTimeWithResult(callable $operation): array
{
$start = microtime(true);
$result = $operation();
$end = microtime(true);
return [
'result' => $result,
'time_ms' => ($end - $start) * 1000
];
}
public static function calculateStatistics(array $measurements): array
{
if (empty($measurements)) {
return [
'count' => 0,
'min' => 0,
'max' => 0,
'avg' => 0,
'median' => 0,
'p95' => 0,
'p99' => 0,
'stddev' => 0
];
}
sort($measurements);
$count = count($measurements);
$min = $measurements[0];
$max = $measurements[$count - 1];
$avg = array_sum($measurements) / $count;
$median = $count % 2 === 0
? ($measurements[$count / 2 - 1] + $measurements[$count / 2]) / 2
: $measurements[intval($count / 2)];
$p95Index = intval($count * 0.95) - 1;
$p99Index = intval($count * 0.99) - 1;
$p95 = $measurements[max(0, $p95Index)];
$p99 = $measurements[max(0, $p99Index)];
// Calculate standard deviation
$sumSquaredDiff = 0;
foreach ($measurements as $value) {
$sumSquaredDiff += pow($value - $avg, 2);
}
$stddev = sqrt($sumSquaredDiff / $count);
return [
'count' => $count,
'min' => round($min, 3),
'max' => round($max, 3),
'avg' => round($avg, 3),
'median' => round($median, 3),
'p95' => round($p95, 3),
'p99' => round($p99, 3),
'stddev' => round($stddev, 3)
];
}
public static function formatStatistics(array $stats, string $unit = 'ms'): string
{
return sprintf(
"Count: %d, Min: %.3f%s, Max: %.3f%s, Avg: %.3f%s, P95: %.3f%s, P99: %.3f%s, StdDev: %.3f%s",
$stats['count'],
$stats['min'], $unit,
$stats['max'], $unit,
$stats['avg'], $unit,
$stats['p95'], $unit,
$stats['p99'], $unit,
$stats['stddev'], $unit
);
}
public static function assertPerformance(
array $measurements,
float $expectedAvg,
float $expectedP95,
string $operation
): void {
$stats = self::calculateStatistics($measurements);
if ($stats['avg'] > $expectedAvg) {
throw new \AssertionError(
sprintf(
"%s average performance exceeded: expected ≤%.3fms, got %.3fms",
$operation,
$expectedAvg,
$stats['avg']
)
);
}
if ($stats['p95'] > $expectedP95) {
throw new \AssertionError(
sprintf(
"%s P95 performance exceeded: expected ≤%.3fms, got %.3fms",
$operation,
$expectedP95,
$stats['p95']
)
);
}
}
public static function getMemoryUsage(): array
{
return [
'current_mb' => round(memory_get_usage(true) / 1024 / 1024, 2),
'peak_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2),
'current_real_mb' => round(memory_get_usage(false) / 1024 / 1024, 2),
'peak_real_mb' => round(memory_get_peak_usage(false) / 1024 / 1024, 2)
];
}
public static function warmupDatabase(\PDO $pdo): void
{
// Execute simple queries to warm up connections
$pdo->query('SELECT 1');
$pdo->query('SELECT COUNT(*) FROM workers');
$pdo->query('SELECT COUNT(*) FROM jobs');
}
public static function createConcurrentOperation(callable $operation, int $concurrency): \Generator
{
$operations = [];
for ($i = 0; $i < $concurrency; $i++) {
$operations[] = function() use ($operation, $i) {
return $operation($i);
};
}
foreach ($operations as $op) {
yield $op;
}
}
public static function simulateLoad(
callable $operation,
int $totalOperations,
int $concurrency,
float $durationSeconds = null
): array {
$results = [];
$startTime = microtime(true);
$endTime = $durationSeconds ? $startTime + $durationSeconds : PHP_FLOAT_MAX;
$operationsCompleted = 0;
$batch = 0;
while ($operationsCompleted < $totalOperations && microtime(true) < $endTime) {
$batchSize = min($concurrency, $totalOperations - $operationsCompleted);
$batchResults = [];
// Execute concurrent operations
for ($i = 0; $i < $batchSize; $i++) {
$result = self::measureTimeWithResult(function() use ($operation, $batch, $i) {
return $operation($batch * $concurrency + $i);
});
$batchResults[] = $result;
}
$results = array_merge($results, $batchResults);
$operationsCompleted += $batchSize;
$batch++;
// Small delay to prevent overwhelming the system
if (microtime(true) < $endTime) {
usleep(1000); // 1ms
}
}
return [
'results' => $results,
'operations_completed' => $operationsCompleted,
'duration_seconds' => microtime(true) - $startTime,
'throughput_ops_per_sec' => $operationsCompleted / (microtime(true) - $startTime)
];
}
public static function generatePerformanceReport(array $testResults): string
{
$report = "\n" . str_repeat("=", 80) . "\n";
$report .= "PERFORMANCE TEST REPORT\n";
$report .= str_repeat("=", 80) . "\n\n";
foreach ($testResults as $testName => $results) {
$report .= "Test: {$testName}\n";
$report .= str_repeat("-", 40) . "\n";
if (isset($results['statistics'])) {
$report .= "Statistics: " . self::formatStatistics($results['statistics']) . "\n";
}
if (isset($results['throughput'])) {
$report .= "Throughput: {$results['throughput']} ops/sec\n";
}
if (isset($results['memory'])) {
$report .= sprintf(
"Memory: Current: %.2fMB, Peak: %.2fMB\n",
$results['memory']['current_mb'],
$results['memory']['peak_mb']
);
}
if (isset($results['notes'])) {
$report .= "Notes: {$results['notes']}\n";
}
$report .= "\n";
}
return $report;
}
}