188 lines
6.9 KiB
PHP
Executable File
188 lines
6.9 KiB
PHP
Executable File
<?php
|
|
|
|
class TranslationWorkerPool {
|
|
private $workers = [];
|
|
private $maxWorkers = 4;
|
|
private $pdo;
|
|
private $environment;
|
|
private $running = true;
|
|
|
|
public function __construct($pdo, $maxWorkers = null) {
|
|
$this->pdo = $pdo;
|
|
|
|
// Configurar número de workers desde environment
|
|
if ($maxWorkers !== null) {
|
|
$this->maxWorkers = $maxWorkers;
|
|
} elseif (isset($_ENV['TRANSLATION_WORKERS'])) {
|
|
$this->maxWorkers = (int)$_ENV['TRANSLATION_WORKERS'];
|
|
}
|
|
|
|
// Capturar el environment del proceso padre
|
|
$this->environment = getenv('APP_ENVIRONMENT') ?: 'pruebas';
|
|
|
|
custom_log("[WORKER_POOL] Inicializando con {$this->maxWorkers} workers");
|
|
custom_log("[WORKER_POOL] Environment: {$this->environment}");
|
|
}
|
|
|
|
public function start() {
|
|
// Configurar manejadores de señales para el pool
|
|
pcntl_async_signals(true);
|
|
pcntl_signal(SIGINT, [$this, 'handleShutdown']);
|
|
pcntl_signal(SIGTERM, [$this, 'handleShutdown']);
|
|
|
|
custom_log("[WORKER_POOL] Iniciando {$this->maxWorkers} workers...");
|
|
|
|
// Iniciar workers
|
|
for ($i = 0; $i < $this->maxWorkers; $i++) {
|
|
$this->spawnWorker($i);
|
|
}
|
|
|
|
custom_log("[WORKER_POOL] Todos los workers iniciados");
|
|
|
|
// Supervisar workers
|
|
$this->supervise();
|
|
}
|
|
|
|
private function spawnWorker($id) {
|
|
$pid = pcntl_fork();
|
|
|
|
if ($pid == -1) {
|
|
// Error al hacer fork
|
|
custom_log("[WORKER_POOL] ERROR: No se pudo crear worker {$id}");
|
|
return false;
|
|
} elseif ($pid == 0) {
|
|
// Proceso hijo - ejecutar worker
|
|
try {
|
|
// Heredar environment del padre
|
|
putenv('APP_ENVIRONMENT=' . $this->environment);
|
|
|
|
custom_log("[WORKER_{$id}] Iniciado con PID " . getmypid() . ", APP_ENVIRONMENT={$this->environment}");
|
|
|
|
// IMPORTANTE: Crear nueva conexión PDO para este worker
|
|
// No usar la conexión del padre porque se cierra por inactividad
|
|
$maxRetries = 3;
|
|
$retryDelay = 2;
|
|
$pdo = null;
|
|
|
|
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
|
|
try {
|
|
require_once __DIR__ . '/../includes/db.php';
|
|
// $pdo se crea en db.php
|
|
if (isset($pdo) && $pdo !== null) {
|
|
custom_log("[WORKER_{$id}] Conexión a BD establecida");
|
|
break;
|
|
}
|
|
} catch (Exception $dbError) {
|
|
custom_log("[WORKER_{$id}] Intento {$attempt}/{$maxRetries} de conexión a BD falló: " . $dbError->getMessage());
|
|
if ($attempt < $maxRetries) {
|
|
sleep($retryDelay);
|
|
} else {
|
|
throw new Exception("No se pudo conectar a la BD después de {$maxRetries} intentos");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cargar dependencias necesarias
|
|
require_once __DIR__ . '/Translate.php';
|
|
require_once __DIR__ . '/TranslationWorker.php';
|
|
|
|
// Crear y ejecutar worker con su propia conexión
|
|
$worker = new TranslationWorker($id, $pdo);
|
|
$worker->run();
|
|
|
|
} catch (Exception $e) {
|
|
custom_log("[WORKER_{$id}] ERROR FATAL: " . $e->getMessage());
|
|
custom_log("[WORKER_{$id}] Stack trace: " . $e->getTraceAsString());
|
|
}
|
|
|
|
exit(0);
|
|
} else {
|
|
// Proceso padre - registrar worker
|
|
$this->workers[$id] = [
|
|
'pid' => $pid,
|
|
'started' => time(),
|
|
'restarts' => 0
|
|
];
|
|
|
|
custom_log("[WORKER_POOL] Worker {$id} spawned con PID {$pid}");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function supervise() {
|
|
custom_log("[WORKER_POOL] Iniciando supervisión de workers");
|
|
|
|
while ($this->running) {
|
|
// Verificar estado de cada worker
|
|
foreach ($this->workers as $id => $info) {
|
|
$status = pcntl_waitpid($info['pid'], $exitStatus, WNOHANG);
|
|
|
|
if ($status > 0) {
|
|
// Worker terminó
|
|
$exitCode = pcntl_wexitstatus($exitStatus);
|
|
custom_log("[WORKER_POOL] Worker {$id} (PID {$info['pid']}) terminó con código {$exitCode}");
|
|
|
|
// Reiniciar worker si el pool sigue corriendo
|
|
if ($this->running) {
|
|
custom_log("[WORKER_POOL] Reiniciando worker {$id}...");
|
|
$this->workers[$id]['restarts']++;
|
|
|
|
// Esperar un poco antes de reiniciar
|
|
sleep(1);
|
|
|
|
$this->spawnWorker($id);
|
|
}
|
|
} elseif ($status < 0) {
|
|
// Error al verificar estado
|
|
custom_log("[WORKER_POOL] Error al verificar worker {$id}");
|
|
}
|
|
}
|
|
|
|
// Procesar señales
|
|
pcntl_signal_dispatch();
|
|
|
|
// Esperar antes de la siguiente verificación
|
|
sleep(5);
|
|
}
|
|
|
|
custom_log("[WORKER_POOL] Supervisión detenida");
|
|
}
|
|
|
|
public function handleShutdown($signal) {
|
|
custom_log("[WORKER_POOL] Señal {$signal} recibida, deteniendo pool...");
|
|
$this->running = false;
|
|
|
|
// Enviar señal de terminación a todos los workers
|
|
foreach ($this->workers as $id => $info) {
|
|
custom_log("[WORKER_POOL] Enviando SIGTERM a worker {$id} (PID {$info['pid']})");
|
|
posix_kill($info['pid'], SIGTERM);
|
|
}
|
|
|
|
// Esperar a que todos los workers terminen
|
|
custom_log("[WORKER_POOL] Esperando a que los workers terminen...");
|
|
foreach ($this->workers as $id => $info) {
|
|
pcntl_waitpid($info['pid'], $status);
|
|
custom_log("[WORKER_POOL] Worker {$id} terminado");
|
|
}
|
|
|
|
custom_log("[WORKER_POOL] Pool detenido completamente");
|
|
}
|
|
|
|
public function getStats() {
|
|
$stats = [
|
|
'total_workers' => $this->maxWorkers,
|
|
'workers' => []
|
|
];
|
|
|
|
foreach ($this->workers as $id => $info) {
|
|
$stats['workers'][$id] = [
|
|
'pid' => $info['pid'],
|
|
'uptime' => time() - $info['started'],
|
|
'restarts' => $info['restarts']
|
|
];
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
}
|