Commit inicial con archivos existentes

This commit is contained in:
2026-01-17 16:14:00 -06:00
parent 48671dc88e
commit 4c48c279de
2539 changed files with 2412708 additions and 0 deletions

187
src/TranslationWorkerPool.php Executable file
View File

@@ -0,0 +1,187 @@
<?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;
}
}