Planificador Dinámico para Procesos Asíncronos

Como hemos visto en las 2 entradas anteriores, las capacidades asíncronas de Salesforce y su planificación son de gran ayuda.

Aún así, vimos que podemos encontrar una carencia: si nuestros procesos asíncronos tiene ncondiciones funcionales o técnicas de las que depende lanzar su ejecución.

Mi objetivo es explicar la idea, ofreciendo una prueba de concepto funcional, para que puedas darle la vuelta, mejorarlo y ampliarlo para tus necesidades.

¿Cuál es la necesidad?

Veamos varios ejemplos de requerimientos que el planificador estándar no puede satisfacer:

  1. El proceso A debe ejecutarse a las 3.00 de la mañana con las máximas garantías de ejecución (es decir, debemos garantizar que a las 3.00, al menos queda 1 slot libre de los 5 disponibles) cuando tenemos otros tantos procesos en posible ejecución.
  2. El proceso C sólo debe ejecutarse cuando los procesos A y B hayan finalizado correctamente, pero no controlamos ni queremos controlar, su concurrencia, cual de los 2 finaliza primero, etc.
  3. Una variante del anterior es añadir una condición adicional. El proceso C solo debe ejecutarse no antes de las 3.00 de la madugrada, y además, solo si los procesos A y B se han ejecutado satisfactoriamente, no tenemos ni queremos tener control sobre la ejecución de los procesos A y B, ni de su duración.
  4. La ejecución de los procesos B y C son prioritarios sobre las de A y D, con lo que quiero que estos se ejecuten antes que esos procesos. Aunque el encolamiento FIFO,  garantiza este requerimiento para 2 procesos, ¿qué sucede en un escenario con diversas prioridades cambiantes con varios procesos? Deberíamos ejecutar código de reordenación, de cierta envergadura o actuaciones sobre las colas del sistema.
  5. Quiero poder cambiar las condiciones en caliente, sin necesidad de eliminar, reordenar o preocuparme de los aspectos de concurrencia del planificador estándar.

Adicionalmente, siempre hecho de menos la capacidad de concentrar la configuración de nuestros procesos Queueable, Scheduable y Batchable de forma centralizada, facilitando el mantenimiento, análisis de impacto, etc.

Si has trabajado en SOA o en un entorno de microservicios, este sería el equivalente al Registry (realmente muy útil para mantenimiento y reutilizar el trabajo realizado por otros compañeros).

Mi Propuesta

Programar un Planificador Dinámico con ciertos objetos con las siguientes características:

  1. Describir de los procesos con sus condiciones en un Custom Object. Esto pemite al administrador, centralizar, gestionar y modificar en caliente los procesos del sistema, SIN CAMBIOS EN EL CÓDIGO.
  2. Utilizar Platform Events para informar de la finalización de Procesos y así planificar nuevos procesos (planificación agresiva – procesar lo más rápido posible)
  3. El planificador, en cada ejecución, busca los procesos que son “mejores candidatos” a ejecutarse y no mantiene una planificación estática, dado que queremos una planficación modificable en caliente.

Además debe permitir:

  1. Programar los procesos como siempre, sin afectación, utilizando Chaining si queremos
  2. Implementar cualquier interfaz
  3. Ejecutar en tiempo mínimo ( actualmente se ejecuta en menos de 1”

Conceptos utilizados

Puedes definir 2 tipos de procesos:

Recurrente: deben ejecutarse cada X’ minutos. Estos procesos son los más prioritarios. Ejecutan tareas prioritarias. Si tienes más de 1, pueda ser que optes por modificar el código, para que siempre puedan entrar, pero entonces, no estarás aprovechando la capacidad máxima de la plataforma. Por ello, introduzco el concepto de “Recurrente Alarmado”, que es un proceso recurrente que debería haberse ejecutado, pero no fue posible, por tener los 5 slots ocupados. Los procesos alarmados, siempre son los más prioritarios, y son siempre los primeros candidatos para el planificador.

Single: son procesos no recurrentes, ejecutan tareas de largo procesamiento, implementando Queuable ó Batchable.

Definición del Custom Object para descripción de los procesos

El primer paso es proporcionar un mecanismo que nos permita describir las condiciones de nuestros procesos. Para ello, que más sencillo que crear un Custom Object donde describamos las condiciones y características.

En mi caso he creado el objeto Process__c, que consta de los siguientes campos de configuración:

  1. El nombre del proceso, que por convención debe ser el nombre de la clase que lo implementa en APEX
  2. Schedule Type: indica si el proceso es Recurrente ó Single, es decir que no es recurrente.
  3. Do not Run Before Time: indica que el proceso no puede lanzarse hasta la hora indicada.
  4. Ancestors List: lista de nombres de los procesos que deben haber finalizado previamente (los llamo procesos Ancestros, su orden de finalización no es importante – solo que hayan finalizado previamente – puedes mejorarlo añadiendo esa condición en el código y realizando una gestión de errores que te convenga.)
  5. Priority: prioridad de cada proceso. En mi caso opté por 2 prioridades: 1 y 2. Seguramente puedes pensar en opciones algo más complejas 🙂
  6. Refresh Window: es la ventana de refresco, tanto para los procesos recurrentes como de los Single. Un proceso puede tener una ventana de ejecución de 5′, ejecutándose cada 5′ y otro de 24h, para que se ejecute una vez al día.

En este mismo objeto voy registrando información básica de lo que les sucede a estos procesos mediante campos con información de ejecución.

Estos campos no son estrictamente necesarios, ya que podrían obtenerse mediante consulta del objeto AsyncApexJob, pero, tanto por rendimiento como por comprensión decidí incluirlos (si a ti no te convence, ningún problema, modificar el código para que esto no sea así, te llevará muy poco tiempo).

Estos campos son:

  1. Last Execution Date: fecha de la última ejecución del proceso.
  2. Status: que puede ser – **null**, **Executed** o **Planned** que indica que el proceso se ha ejecutado, y que ha sido planificado para su ejecución, respectivamente. Si el valor es null, es que no se cumple ninguna de las anteriores.
  3. Alarmed?: indica si un proceso recurrente debería haberse ejecutado, porque sus condiciones se cumplían, pero debido a que ya existía el máximo de procesos activos, no pudo. Estos procesos, pasan a ser top-prioritarios y el planificador intenta ejecutarlos lo antes posible.

Nota: explotando la información disponible en AsyncApexJob podríamos realizar métodos más complejos de planificación: como planificar a futuro, valorando la carga pendiente y los tiempos medios anteriores, pero no tengo este tipo de necesidades tan complejas y no las he explorado (puedes ampliarlo con estas capacidades ampliando el código existente).

Funcionamiento

Como he comentado, el objetivo del planificador es aprovechar al máximo la capacidad de 5 procesos de la plataforma pero nunca sin sobrepasarlo. Para ello su algoritmo es:

Planifica los procesos Recurrentes Alarmados en mi primer lugar

Planifica los procesos Recurrentes no Alarmados

Planifica los procesos Single

Comprueba si se cumplen las condiciones de ejecución del proceso (no iniciarse antes de tal hora y sus padres se han ejecutado) y en caso positivo lanza la ejecución.

Lanzamiento de un Platform Event notificando replanificación

Definimos un evento, que todos los procesos Single envían al finalizar. La idea es muy sencilla, si un proceso ha finalizado, muy probablemente, existe 1 slot disponible de ejecución para iniciar un nuevo proceso.

¿Por qué dices muy probablemente? Si el proceso finalizado lanza otro proceso vía Chaining éste ocupará un slot de los 5 procesos activos, y el planificador no encontrará ningún slot libre (si éste fuera justamente el 5o.). Además si lanzamos otros procesos asíncronos fuera del control del planificador, también puede ocurrir esta situación, por eso el probablemente.

Tenemos un trigger que está escuchando este evento, y relanza la ejecución del planificador.

En la última linea de execute ó finish para Queueable o Batchable repectivamente añade:

EventSender.sendEventBus(processName, processName + ' Executed'); que invoca un evento de la clase EventSender, una clase helper muy simple.

Otra clase helper que he implementado es un Logger que nos permite verificar los mensajes de todos los procesos de forma centralizada, dado que se generar diferentes contextos. No tiene ningún misterio, y si ya tienes en tu ORG, otra de tipo parecido, sustituye la que te doy con la tuya.

Creación dinámica de los procesos

No es necesario preocuparse por la creación del proceso, ya que mediante la característica que nos ofrece Apex con la creación dinámica de tipos, mediante la clase Type, podemos crear los procesos solo con su nombre.

Cuál es el resultado?

El resultado es una planificación hasta 5 procesos de ejecución.

Mediante un objeto Custom, voy almacenando un Log, con el cual podemos ver como se va ejecutando:

Cuyo resultado es:

Quizás hubiera sido perfecto que…

Como no, siempre hay algún detalle del cual no quedas del todo satisfecho.

Si tenemos una ejecución con 5 procesos ejecutándose y tenemos un proceso Schedulable que pudiera iniciarse mientras estos 5 procesos se están ejecutando, su ejecución no se iniciará.

Aunque con el planificador estándar ocurre la misma situación, me hubiera gustado paliar esta situación, pero requiere mucho cálculo, complicando el código y aunque posible, no es mi objetivo complicar tanto la idea.

Conclusión

Mis conclusiones – por eso el dibujo es canoso 😉

Para muchos de nosotros y en la mayoría de los casos, el planificador estándar de Salesforce, es excelente y de absoluta fiabilidad.

Para los casos más complejos, tenemos la capacidad de ampliarlo/modificarlo según nuestras necesidades, utilizando las capacidades y mecanismos de la plataforma, dado que tenemos capacidad de crear procesos, ejecutarlos, consultar su historial, su ejecución en tiempo real, etc.

Es decir, nuevamente la plataforma demuestra su gran capacidad de apertura y adaptación.

Espero que te sea de ayuda y si has llegado hasta aquí, felicidades!!!

Repositorio

Todo los artefactos, están disponibles para su uso y fork en este repositorio de Bitbucket.

Desafortunadamente, me tenía que decidir por comentar el código fuente en un idioma, y como tengo la intención, algún día de publicar este mismo artículo en inglés, me decidí por hacerlo en ese idioma.

Mil disculpas si eso no ayuda a su comprensión.

Grafismo creado por Alekksall en Freepik.com

One response to “Planificador Dinámico para Procesos Asíncronos

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

w

Conectando a %s