v13.39.0 y se aplican al desarrollo de paquetes las operaciones básicas de Programación de tareas. Para la carga de providers, consulta Estructura interna de la detección automática de paquetes; para la publicación de la configuración, consulta Fusión y caché de la configuración de paquetes.
Registrar tras la resolución de Schedule
En una aplicación Laravel normal,FoundationServiceProvider registra Schedule::class como singleton. Su creación llama a resolveConsoleSchedule() del Console Kernel, que aplica la zona horaria de la aplicación y el store de caché del scheduler.
El paquete debe extender este Schedule compartido, y no limitarse a crear su propio new Schedule(). El siguiente provider supone una estructura en la que Acme\Courier\ corresponde a src/ mediante PSR-4 de Composer y el paquete incluye config/courier.php.
callAfterResolving() espera a la resolución futura si el servicio aún no se ha resuelto y, si ya se resolvió, lo obtiene y llama al callback en ese momento. Así se puede registrar aunque otro provider haya usado Schedule antes. Para más detalles, consulta Hooks de resolución de servicios en paquetes.
runningInConsole() no identifica un comando Artisan concreto. En este ejemplo se excluyen los usos de Schedule a través de HTTP. Además, el provider que registra el hook no debe ser un provider diferido: si boot() no se ejecuta antes del momento necesario, el hook tampoco se registra.
Registrar un evento y ejecutar una tarea son cosas distintas. Cargar este provider no ejecuta el heartbeat en ese momento. El registro por sí solo tampoco inicia cron ni
schedule:work.La activación explícita corresponde a quien usa el paquete
El archivoconfig/courier.php del paquete deja el estado inicial desactivado.
schedule.enabled a true. En este ejemplo, los valores se leen en el momento del registro. La zona horaria tiene UTC como valor inicial; si eliges otra, ten en cuenta también las ejecuciones duplicadas u omitidas por el horario de verano.
Como la configuración se fusiona de forma superficial, el registrador también especifica el valor predeterminado de cada clave. Así, aunque una sobrescritura parcial elimine otras claves del array schedule, se puede registrar con la configuración básica.
Evitar el registro duplicado en el mismo Schedule
El siguientesrc/CourierSchedule.php registra un heartbeat mínimo que escribe una entrada de log. Con WeakMap, este registrador añade la tarea al mismo objeto Schedule una sola vez.
Schedule::call(), exec() y job() añaden eventos a un array. No son APIs que busquen un evento con el mismo nombre para reemplazarlo ni que eliminen duplicados.
El nombre del evento también afecta a la compatibilidad de los bloqueos
name() es un alias de description(), pero su efecto en el nombre del bloqueo depende del tipo de evento. A continuación se muestran los nombres de mutex predeterminados.
En un CallbackEvent, asigna un nombre no vacío antes de
withoutOverlapping() u onOneServer(). Si no se ha definido un nombre, estos métodos lanzan una excepción.
job() normalmente usa como nombre inicial el nombre de la clase del job, entre otros. Sin embargo, si registras el mismo job con distintos argumentos o destinos, especifica explícitamente un nombre para cada destino. Cambiar solo los argumentos no hace que el nombre inicial pase a ser distinto automáticamente.
SyncCatalogJob es un ejemplo de clase de job que proporciona el paquete. Para implementar jobs, consulta Colas y jobs.
Si cambias el nombre, el comando o la expresión cron, según el tipo de evento también cambia el identificador del bloqueo. En despliegues donde conviven procesos de la versión anterior y la nueva, el mismo proceso puede adquirir bloqueos distintos. No lo trates como un simple cambio de texto visible: gestiona el nombre de la tarea como una especificación pública estable.
Distinguir onOneServer y withoutOverlapping
onOneServer()elige el servidor que se encarga de la ejecución en ese momento. ElCacheSchedulingMutexrevisado añade la hora y el minuto al nombre de mutex del evento y lo mantiene con una caducidad de una hora. Es distinto del bloqueo contra solapamientos, que se libera al terminar la tarea.withoutOverlapping()evita las ejecuciones solapadas mientras continúa la anterior. La caducidad indicada se expresa en minutos y su valor predeterminado es de 1.440 minutos. Con una caducidad demasiado corta, el bloqueo expira antes de que termine el proceso.- Cuando
job()envía el trabajo a la cola, la finalización del evento programado no es la finalización del procesamiento del job. El control de duplicados en los workers se diseña por separado en el lado del job, por ejemplo con jobs únicos o con el middlewareWithoutOverlapping.
onOneServer(), indícales como requisito una configuración de caché compartida con database, memcached, dynamodb o redis, tal como especifica la documentación oficial. Los bloqueos no garantizan una ejecución exactly-once de la lógica de negocio, así que también hace falta un proceso que no se rompa si se vuelve a ejecutar.
Resolver las dependencias pesadas en tiempo de ejecución
ElLoggerInterface del heartbeat anterior lo resuelve CallbackEvent mediante call() del contenedor cuando se ejecuta el evento. Con esta estructura, no se escriben logs ni se crean clientes al registrar el hook.
No colocar conexiones externas ni lógica de negocio en el constructor también es importante en Registro de comandos Artisan de paquetes y resolución de dependencias.
Además, como
call() y job() son CallbackEvent, no se puede usar runInBackground(). Delega los procesos largos a la cola o, si es necesario, elige la ejecución en segundo plano de command().
No apropiarse de la configuración global de la aplicación
Schedule::useCache() cambia el store del objeto mutex que tiene Schedule. No es una configuración que afecte solo a los eventos del paquete: también afecta a los eventos que comparten el mismo mutex. No lo llames incondicionalmente en el boot() del paquete; trátalo como una configuración que elige la aplicación consumidora.
Del mismo modo, el paquete no debe cambiar por su cuenta Schedule::alwaysOnOneServer(), la pausa del scheduler ni la política de ejecución durante el modo de mantenimiento.
Tras cambiar la configuración, crear un Schedule nuevo
En el ejemplo,enabled, la expresión cron y la zona horaria son configuración leída durante el registro. Cambiar solo los valores de config() sobre un Schedule ya creado no añade ni elimina eventos ni vuelve a leer la configuración. La comprobación del WeakMap tampoco sirve para reconfigurar.
En despliegues que usan la caché de configuración, reconstruye la caché y comprueba schedule:list en un proceso que cargue la nueva configuración. Comprueba también que no queden código ni configuración antiguos en schedulers de larga duración ni en schedule:run que estén ejecutando tareas de menos de un minuto. Para la interrupción de tareas de menos de un minuto durante el despliegue, consulta Programación de tareas.
Probar el registro por separado de la ejecución
A continuación se muestra un ejemplo detests/CourierScheduleTest.php que usa el autoload de Composer del paquete y PHPUnit. Se usan dobles de prueba para los mutex y se comprueba el resultado del registro sin usar una caché externa ni un cron real.
No ejecutes todas las tareas de la aplicación consumidora con
schedule:run solo para que pase la prueba de registro. Separar las pruebas del cuerpo de la tarea de las pruebas de integración del scheduler en un entorno controlado evita efectos secundarios de tareas periódicas no relacionadas.
Páginas relacionadas
Programación de tareas
Revisa la frecuencia de ejecución, la configuración de cron, la delegación a la cola y la operación durante los despliegues.
Hooks de resolución de servicios en paquetes
Revisa la aplicación a servicios no resueltos y ya resueltos, y las precauciones al volver a registrar.
Fuentes primarias consultadas
- Documentación oficial de Laravel: programación de tareas
- Documentación oficial de Laravel: desarrollo de paquetes
- FoundationServiceProvider: registro de Schedule como singleton
- Console Kernel: creación de Schedule y configuración predeterminada
- ServiceProvider: callAfterResolving()
- Schedule: adición de eventos, creación diferida de jobs y mutex compartido
- Event: nombre de mutex de los eventos de comando
- CallbackEvent: requisito de nombre y resolución de dependencias en tiempo de ejecución
- CacheEventMutex: bloqueo contra solapamientos y conversión de la caducidad
- CacheSchedulingMutex: bloqueo por hora de ejecución
- Pruebas oficiales: registro de eventos y zona horaria
- Pruebas oficiales: creación diferida a partir del nombre de la clase del job