- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
224 lines
7.6 KiB
PHP
224 lines
7.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\LiveComponents\Cache\CacheMetricsCollector;
|
|
use App\Framework\LiveComponents\Cache\CacheType;
|
|
|
|
describe('CacheMetricsCollector', function () {
|
|
it('initializes with empty metrics for all cache types', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$stateMetrics = $collector->getMetrics(CacheType::STATE);
|
|
$slotMetrics = $collector->getMetrics(CacheType::SLOT);
|
|
$templateMetrics = $collector->getMetrics(CacheType::TEMPLATE);
|
|
|
|
expect($stateMetrics->hits)->toBe(0)
|
|
->and($slotMetrics->hits)->toBe(0)
|
|
->and($templateMetrics->hits)->toBe(0);
|
|
});
|
|
|
|
it('records hits for specific cache type', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordHit(CacheType::STATE, 0.6);
|
|
|
|
$metrics = $collector->getMetrics(CacheType::STATE);
|
|
|
|
expect($metrics->hits)->toBe(2)
|
|
->and($metrics->misses)->toBe(0);
|
|
});
|
|
|
|
it('records misses for specific cache type', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordMiss(CacheType::SLOT, 1.0);
|
|
$collector->recordMiss(CacheType::SLOT, 1.2);
|
|
|
|
$metrics = $collector->getMetrics(CacheType::SLOT);
|
|
|
|
expect($metrics->hits)->toBe(0)
|
|
->and($metrics->misses)->toBe(2);
|
|
});
|
|
|
|
it('records invalidations for specific cache type', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordInvalidation(CacheType::TEMPLATE);
|
|
$collector->recordInvalidation(CacheType::TEMPLATE);
|
|
|
|
$metrics = $collector->getMetrics(CacheType::TEMPLATE);
|
|
|
|
expect($metrics->invalidations)->toBe(2);
|
|
});
|
|
|
|
it('updates cache size for specific cache type', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->updateSize(CacheType::STATE, 150);
|
|
|
|
$metrics = $collector->getMetrics(CacheType::STATE);
|
|
|
|
expect($metrics->totalSize)->toBe(150);
|
|
});
|
|
|
|
it('maintains separate metrics for different cache types', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordMiss(CacheType::SLOT, 1.0);
|
|
$collector->recordInvalidation(CacheType::TEMPLATE);
|
|
|
|
expect($collector->getMetrics(CacheType::STATE)->hits)->toBe(1)
|
|
->and($collector->getMetrics(CacheType::SLOT)->misses)->toBe(1)
|
|
->and($collector->getMetrics(CacheType::TEMPLATE)->invalidations)->toBe(1);
|
|
});
|
|
|
|
it('returns all metrics', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordHit(CacheType::SLOT, 0.6);
|
|
|
|
$allMetrics = $collector->getAllMetrics();
|
|
|
|
expect($allMetrics)->toBeArray()
|
|
->and($allMetrics)->toHaveKey('state')
|
|
->and($allMetrics)->toHaveKey('slot')
|
|
->and($allMetrics)->toHaveKey('template');
|
|
});
|
|
|
|
it('calculates aggregate metrics across all caches', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
// State: 2 hits
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordHit(CacheType::STATE, 0.6);
|
|
|
|
// Slot: 1 hit, 1 miss
|
|
$collector->recordHit(CacheType::SLOT, 0.7);
|
|
$collector->recordMiss(CacheType::SLOT, 1.0);
|
|
|
|
// Template: 1 miss
|
|
$collector->recordMiss(CacheType::TEMPLATE, 1.2);
|
|
|
|
$aggregate = $collector->getAggregateMetrics();
|
|
|
|
expect($aggregate->cacheType)->toBe(CacheType::MERGED)
|
|
->and($aggregate->hits)->toBe(3)
|
|
->and($aggregate->misses)->toBe(2)
|
|
->and($aggregate->getTotalOperations())->toBe(5);
|
|
});
|
|
|
|
it('generates comprehensive summary', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordMiss(CacheType::SLOT, 1.0);
|
|
|
|
$summary = $collector->getSummary();
|
|
|
|
expect($summary)->toHaveKey('overall')
|
|
->and($summary)->toHaveKey('by_type')
|
|
->and($summary)->toHaveKey('performance_assessment')
|
|
->and($summary['by_type'])->toHaveKey('state')
|
|
->and($summary['by_type'])->toHaveKey('slot')
|
|
->and($summary['by_type'])->toHaveKey('template');
|
|
});
|
|
|
|
it('assesses performance against targets', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
// State: 80% hit rate (target: 70%)
|
|
for ($i = 0; $i < 8; $i++) {
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
}
|
|
for ($i = 0; $i < 2; $i++) {
|
|
$collector->recordMiss(CacheType::STATE, 1.0);
|
|
}
|
|
|
|
$assessment = $collector->assessPerformance();
|
|
|
|
expect($assessment['state_cache']['meets_target'])->toBeTrue()
|
|
->and($assessment['state_cache']['grade'])->toBe('B');
|
|
});
|
|
|
|
it('detects performance issues when targets not met', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
// State: 50% hit rate (below 70% target)
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordMiss(CacheType::STATE, 1.0);
|
|
|
|
expect($collector->hasPerformanceIssues())->toBeTrue();
|
|
});
|
|
|
|
it('generates performance warnings for underperforming caches', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
// State: 50% hit rate (below 70% target)
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordMiss(CacheType::STATE, 1.0);
|
|
|
|
$warnings = $collector->getPerformanceWarnings();
|
|
|
|
expect($warnings)->toBeArray()
|
|
->and($warnings)->not->toBeEmpty()
|
|
->and($warnings[0])->toContain('State cache hit rate');
|
|
});
|
|
|
|
it('returns no warnings when all caches meet targets', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
// State: 90% hit rate (exceeds 70% target)
|
|
for ($i = 0; $i < 9; $i++) {
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
}
|
|
$collector->recordMiss(CacheType::STATE, 1.0);
|
|
|
|
// Slot: 80% hit rate (exceeds 60% target)
|
|
for ($i = 0; $i < 8; $i++) {
|
|
$collector->recordHit(CacheType::SLOT, 0.5);
|
|
}
|
|
for ($i = 0; $i < 2; $i++) {
|
|
$collector->recordMiss(CacheType::SLOT, 1.0);
|
|
}
|
|
|
|
// Template: 90% hit rate (exceeds 80% target)
|
|
for ($i = 0; $i < 9; $i++) {
|
|
$collector->recordHit(CacheType::TEMPLATE, 0.5);
|
|
}
|
|
$collector->recordMiss(CacheType::TEMPLATE, 1.0);
|
|
|
|
expect($collector->hasPerformanceIssues())->toBeFalse()
|
|
->and($collector->getPerformanceWarnings())->toBeEmpty();
|
|
});
|
|
|
|
it('exports metrics with timestamp', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
|
|
$export = $collector->export();
|
|
|
|
expect($export)->toHaveKey('timestamp')
|
|
->and($export)->toHaveKey('metrics')
|
|
->and($export['timestamp'])->toBeInt();
|
|
});
|
|
|
|
it('resets all metrics', function () {
|
|
$collector = new CacheMetricsCollector();
|
|
|
|
$collector->recordHit(CacheType::STATE, 0.5);
|
|
$collector->recordHit(CacheType::SLOT, 0.6);
|
|
$collector->recordHit(CacheType::TEMPLATE, 0.7);
|
|
|
|
$collector->reset();
|
|
|
|
expect($collector->getMetrics(CacheType::STATE)->hits)->toBe(0)
|
|
->and($collector->getMetrics(CacheType::SLOT)->hits)->toBe(0)
|
|
->and($collector->getMetrics(CacheType::TEMPLATE)->hits)->toBe(0);
|
|
});
|
|
});
|