Files
michaelschiemer/src/Framework/Queue/Entities/Worker.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

280 lines
8.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Queue\Entities;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Queue\ValueObjects\WorkerId;
/**
* Worker Entity für Distributed Job Processing
*/
final readonly class Worker
{
public function __construct(
public WorkerId $id,
public string $hostname,
public int $processId,
public array $queues, // Array of QueueName objects
public int $maxJobs,
public \DateTimeImmutable $registeredAt,
public ?\DateTimeImmutable $lastHeartbeat = null,
public bool $isActive = true,
public Percentage $cpuUsage = new Percentage(0),
public Byte $memoryUsage = new Byte(0),
public int $currentJobs = 0,
public array $capabilities = [],
public string $version = '1.0.0'
) {
if (empty($this->queues)) {
throw new \InvalidArgumentException('Worker must handle at least one queue');
}
if ($this->maxJobs <= 0) {
throw new \InvalidArgumentException('Max jobs must be greater than 0');
}
if ($this->currentJobs < 0) {
throw new \InvalidArgumentException('Current jobs cannot be negative');
}
if ($this->currentJobs > $this->maxJobs) {
throw new \InvalidArgumentException('Current jobs cannot exceed max jobs');
}
}
/**
* Erstelle einen neuen Worker
*/
public static function register(
string $hostname,
int $processId,
array $queues,
int $maxJobs = 10,
array $capabilities = []
): self {
return new self(
id: WorkerId::forHost($hostname, $processId),
hostname: $hostname,
processId: $processId,
queues: $queues,
maxJobs: $maxJobs,
registeredAt: new \DateTimeImmutable(),
lastHeartbeat: new \DateTimeImmutable(),
isActive: true,
capabilities: $capabilities
);
}
/**
* Worker Heartbeat aktualisieren
*/
public function updateHeartbeat(
Percentage $cpuUsage,
Byte $memoryUsage,
int $currentJobs
): self {
return new self(
id: $this->id,
hostname: $this->hostname,
processId: $this->processId,
queues: $this->queues,
maxJobs: $this->maxJobs,
registeredAt: $this->registeredAt,
lastHeartbeat: new \DateTimeImmutable(),
isActive: true,
cpuUsage: $cpuUsage,
memoryUsage: $memoryUsage,
currentJobs: $currentJobs,
capabilities: $this->capabilities,
version: $this->version
);
}
/**
* Worker als inaktiv markieren
*/
public function markInactive(): self
{
return new self(
id: $this->id,
hostname: $this->hostname,
processId: $this->processId,
queues: $this->queues,
maxJobs: $this->maxJobs,
registeredAt: $this->registeredAt,
lastHeartbeat: $this->lastHeartbeat,
isActive: false,
cpuUsage: $this->cpuUsage,
memoryUsage: $this->memoryUsage,
currentJobs: $this->currentJobs,
capabilities: $this->capabilities,
version: $this->version
);
}
/**
* Prüfe ob Worker verfügbar für neue Jobs ist
*/
public function isAvailableForJobs(): bool
{
return $this->isActive
&& $this->currentJobs < $this->maxJobs
&& $this->isHealthy();
}
/**
* Prüfe ob Worker eine bestimmte Queue unterstützt
*/
public function handlesQueue(QueueName $queueName): bool
{
foreach ($this->queues as $queue) {
if ($queue instanceof QueueName && $queue->equals($queueName)) {
return true;
}
}
return false;
}
/**
* Prüfe ob Worker healthy ist
*/
public function isHealthy(): bool
{
if (! $this->isActive) {
return false;
}
// Heartbeat nicht älter als 60 Sekunden
if ($this->lastHeartbeat === null) {
return false;
}
$heartbeatAge = time() - $this->lastHeartbeat->getTimestamp();
if ($heartbeatAge > 60) {
return false;
}
// CPU und Memory Limits
if ($this->cpuUsage->getValue() > 90) {
return false;
}
// Memory Limit (2GB)
if ($this->memoryUsage->toBytes() > 2 * 1024 * 1024 * 1024) {
return false;
}
return true;
}
/**
* Berechne Worker Load (0-100%)
*/
public function getLoadPercentage(): Percentage
{
if ($this->maxJobs === 0) {
return new Percentage(100);
}
$jobLoad = ($this->currentJobs / $this->maxJobs) * 100;
$cpuLoad = $this->cpuUsage->getValue();
// Höchste Last zählt
return new Percentage(max($jobLoad, $cpuLoad));
}
/**
* Prüfe ob Worker eine Capability hat
*/
public function hasCapability(string $capability): bool
{
return in_array($capability, $this->capabilities, true);
}
/**
* Worker Informationen für Monitoring
*/
public function toMonitoringArray(): array
{
return [
'id' => $this->id->toString(),
'hostname' => $this->hostname,
'process_id' => $this->processId,
'queues' => array_map(fn (QueueName $queue) => $queue->toString(), $this->queues),
'max_jobs' => $this->maxJobs,
'current_jobs' => $this->currentJobs,
'is_active' => $this->isActive,
'is_healthy' => $this->isHealthy(),
'is_available' => $this->isAvailableForJobs(),
'load_percentage' => $this->getLoadPercentage()->getValue(),
'cpu_usage' => $this->cpuUsage->getValue(),
'memory_usage_mb' => round($this->memoryUsage->toBytes() / 1024 / 1024, 2),
'registered_at' => $this->registeredAt->format('Y-m-d H:i:s'),
'last_heartbeat' => $this->lastHeartbeat?->format('Y-m-d H:i:s'),
'capabilities' => $this->capabilities,
'version' => $this->version,
];
}
/**
* Array Repräsentation für Persistierung
*/
public function toArray(): array
{
return [
'id' => $this->id->toString(),
'hostname' => $this->hostname,
'process_id' => $this->processId,
'queues' => json_encode(array_map(fn (QueueName $queue) => $queue->toString(), $this->queues)),
'max_jobs' => $this->maxJobs,
'current_jobs' => $this->currentJobs,
'is_active' => $this->isActive,
'cpu_usage' => $this->cpuUsage->getValue(),
'memory_usage_bytes' => $this->memoryUsage->toBytes(),
'registered_at' => $this->registeredAt->format('Y-m-d H:i:s'),
'last_heartbeat' => $this->lastHeartbeat?->format('Y-m-d H:i:s'),
'capabilities' => json_encode($this->capabilities),
'version' => $this->version,
];
}
/**
* Worker aus Array erstellen
*/
public static function fromArray(array $data): self
{
$queueStrings = json_decode($data['queues'], true);
$queues = array_map(function (string $queueString) {
// Parse queue string zurück zu QueueName
// Annahme: Format ist "type.name" oder "tenant.type.name"
$parts = explode('.', $queueString);
if (count($parts) >= 2) {
return QueueName::default(); // Vereinfacht - könnte erweitert werden
}
return QueueName::default();
}, $queueStrings);
return new self(
id: WorkerId::fromString($data['id']),
hostname: $data['hostname'],
processId: $data['process_id'],
queues: $queues,
maxJobs: $data['max_jobs'],
registeredAt: new \DateTimeImmutable($data['registered_at']),
lastHeartbeat: $data['last_heartbeat'] ? new \DateTimeImmutable($data['last_heartbeat']) : null,
isActive: (bool) $data['is_active'],
cpuUsage: new Percentage($data['cpu_usage'] ?? 0),
memoryUsage: Byte::fromBytes($data['memory_usage_bytes'] ?? 0),
currentJobs: $data['current_jobs'] ?? 0,
capabilities: json_decode($data['capabilities'] ?? '[]', true),
version: $data['version'] ?? '1.0.0'
);
}
}