Scheduler + Cron — Setup OnnixConnect
Ticket Jira: FAC-83 — Configurar Laravel Scheduler + cron en servidor Estado: Demo en local funcionando 2026-04-09. Falta deploy en server.
Qué resuelve
Reemplazar todos los procesos manuales y los crons individuales por un único punto de entrada (schedule:run) que Laravel maneja internamente. La cron del SO solo tiene que dispararlo cada minuto.
Tareas programadas activas hoy:
| Comando / Job | Frecuencia | Definido en |
|---|---|---|
App\Jobs\Sifen\SincronizarEstadosSifenJob | cada 5 min | routes/console.php:15 |
A futuro se irán agregando más (reportes diarios, limpieza de logs, etc.) sin tocar el cron del servidor — solo agregando líneas en routes/console.php.
Cómo se prueba en local
1. Verificar lo que está programado
php artisan schedule:list
Debe mostrar:
*/5 * * * * App\Jobs\Sifen\SincronizarEstadosSifenJob Next Due: X minutes from now
2. Levantar el "cron simulado" + worker de cola
En dos terminales separadas (o background):
# Terminal 1 — simula el cron del servidor llamando schedule:run cada minuto
php artisan schedule:work
# Terminal 2 — procesa los jobs encolados (en producción esto lo hace Horizon)
php artisan queue:work --queue=default
3. Disparar manualmente sin esperar al boundary de 5 min
php artisan schedule:test --name="App\Jobs\Sifen\SincronizarEstadosSifenJob"
schedule:test bypaseа la expresión cron y encola el job inmediatamente. El worker de cola lo procesa en milisegundos.
4. Demo completo end-to-end (probado 2026-04-09)
1. Crear DTE → firmar → enviar como lote → quedar en 'sent'
2. Forzar last_sent_at a -10 min para pasar el filtro del job
3. schedule:test --name=SincronizarEstadosSifenJob
→ Running [SincronizarEstadosSifenJob] ........... 20.47ms DONE
4. Queue worker (background)
→ 2026-04-09 17:42:35 SincronizarEstadosSifenJob ...... RUNNING
→ 2026-04-09 17:42:35 SincronizarEstadosSifenJob 225.48ms DONE
5. SELECT estado, sifen_result_code FROM sifen_dtes WHERE id=93
→ estado=approved, cod=0260, msg=Aprobado ✅
Setup en servidor de desarrollo
Pre-requisitos
- PHP 8.2+ con
php artisanaccesible. - El proyecto desplegado en una ruta conocida (ej.
/var/www/onnixconnect). - Usuario del sistema con permisos sobre el proyecto (ej.
www-dataonestor). - Redis corriendo (la cola
defaultapunta a Redis). - Horizon (o
queue:workcomo fallback) corriendo como servicio supervisado.
Paso 1 — Agregar el cron del sistema
Editar el crontab del usuario que corre la aplicación:
sudo -u www-data crontab -e
Agregar una sola línea:
* * * * * cd /var/www/onnixconnect && php artisan schedule:run >> /dev/null 2>&1
Notas:
* * * * *= cada minuto (Laravel internamente decide qué tareas son due).cd /var/www/onnixconnectes OBLIGATORIO —schedule:runnecesita el cwd del proyecto.- Redirigir la salida a
/dev/nullevita que el crond mande mails. Si querés ver los logs cambialos a un archivo:>> /var/log/onnixconnect/schedule.log 2>&1.
Verificar que el crontab quedó guardado:
sudo -u www-data crontab -l
Paso 2 — Confirmar que crond está corriendo
systemctl status cron # Debian/Ubuntu
systemctl status crond # CentOS/RHEL
Si no está activo: sudo systemctl enable --now cron.
Paso 3 — Configurar Horizon como servicio (FAC-96 en el roadmap)
Hasta que FAC-96 esté hecho, se puede usar queue:work directo bajo supervisor. Archivo de ejemplo /etc/supervisor/conf.d/onnixconnect-worker.conf:
[program:onnixconnect-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/onnixconnect/artisan queue:work redis --queue=critica,lotes,default --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/onnixconnect/worker.log
stopwaitsecs=3600
Aplicar:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start onnixconnect-worker:*
Paso 4 — Smoke test en el server
# 1. Listar tareas programadas
sudo -u www-data php /var/www/onnixconnect/artisan schedule:list
# 2. Forzar ejecución manual
sudo -u www-data php /var/www/onnixconnect/artisan schedule:test --name="App\Jobs\Sifen\SincronizarEstadosSifenJob"
# 3. Ver logs del cron en vivo (cada minuto debe haber actividad)
tail -f /var/log/onnixconnect/schedule.log # si configuraste el log
# o
journalctl -u cron -f # logs del propio crond
# 4. Verificar que el worker está vivo
sudo supervisorctl status onnixconnect-worker:*
Paso 5 — Validación funcional
Con el sistema configurado:
- Enviar un lote real vía
POST /api/sifen/dtes/lote. - Verificar en BD que los DTEs quedan en
estado=sentconprot_lotepoblado. - Esperar 5-10 minutos.
- Re-consultar BD: deben transicionar automáticamente a
approvedorejected.
Si después de 10 min no hubo transición, debuggear en este orden:
- ¿El cron está disparando?
grep CRON /var/log/syslog | tail - ¿
schedule:runestá corriendo el job?php artisan schedule:listy verificar "Last Due" - ¿El worker está vivo?
supervisorctl status - ¿Hay jobs en la cola Redis?
redis-cli LLEN queues:default - ¿Hay jobs fallidos?
php artisan queue:failed
Cómo agregar más tareas programadas
Editar routes/console.php y agregar líneas como:
use App\Jobs\Reportes\GenerarReporteDiarioJob;
Schedule::job(new GenerarReporteDiarioJob())->dailyAt('06:00');
Schedule::command('horizon:snapshot')->everyFiveMinutes();
Schedule::command('telescope:prune --hours=48')->daily();
No hay que tocar el cron del sistema. Solo recargar la app si está cacheada (php artisan optimize:clear).
Troubleshooting
| Síntoma | Causa probable | Solución |
|---|---|---|
schedule:run no hace nada | El cron no está pasando por el cwd correcto | Verificar cd /var/www/onnixconnect && en el crontab |
schedule:list no muestra el job | routes/console.php no se está cargando | php artisan optimize:clear y reintentar |
| Job se encola pero no se procesa | Worker caído o cola incorrecta | supervisorctl status + verificar --queue=default |
WithoutOverlapping bloquea siempre | Lock viejo en cache | php artisan cache:clear |
| Logs vacíos | >> /dev/null 2>&1 está silenciando todo | Cambiar a >> /var/log/onnixconnect/schedule.log 2>&1 para debug |
Referencias
- Laravel 12 — Task Scheduling
- Laravel Horizon
routes/console.php— definición de tareas programadasapp/Jobs/Sifen/SincronizarEstadosSifenJob.php— el job que corre cada 5 mindocs/guias-tecnicas/SIFEN_LOTE_ASYNC_FUNCIONAMIENTO.md— pipeline completo de lotes async