Hace algún tiempo escribí sobre una migración exitosa de IBM BPM a Camunda, y ahora nuestra vida está llena de felicidad e impresiones agradables. Camunda no decepcionó, y seguimos siendo amigos con este motor BPM.
Pero, por desgracia, Camunda también puede presentar sorpresas desagradables, debido a que a veces no se obtienen los resultados más obvios. Este artículo considerará un caso que, a pesar de su simplicidad, resultó ser interesante y algo más complejo de lo que parecía a primera vista.
Nosotros entrenamos en gatos
Para describir el problema, considere un ejemplo sintético. Digamos que decidimos expandir nuestra base de clientes y necesitamos servir gatos y gatos. Cada cliente potencial debería ser revisado y, tal vez, ofrecerle algo de inmediato.
Verificaremos la confiabilidad del candidato y los posibles servicios que podemos ofrecerle. La verificación de confiabilidad y los posibles servicios no están conectados de ninguna manera; estas acciones se pueden realizar en paralelo. Esquemáticamente, en un diagrama bpmn, se verá así:
Diagrama 1. Proceso esquemático de servir furry
El diagrama muestra esquemáticamente los pasos básicos, bifurcar y unir puertas de enlace.
Este icono representa una puerta de enlace paralela. Parallel Gateway es la puerta de enlace más simple para construir una parte de un proceso en ejecución paralela.
Hay dos tipos de puertas de enlace paralelas:
- fork: crea una ejecución separada para cada rama;
- unirse: espera todas las ejecuciones entrantes.
Ejecución : representa una 'ruta de ejecución' en una instancia de proceso (de la documentación). Es decir, es un hilo de ejecución de proceso.
Ahora vamos a complicar un poco la tarea. Verificaremos y buscaremos servicios de la siguiente manera: primero verificamos el estado del cliente, luego observamos qué servicios pueden adaptarse a él y hacemos un preprocesamiento. Además, varios servicios pueden adaptarse a un cliente a la vez, por lo que debemos poder ofrecerlos todos al cliente.
Como trabajamos con clientes peludos, los servicios serán apropiados: valeriana, rastrillo, almohada de maestro y otras cosas útiles.
Gráfico 2. Gráfico de servicio al cliente refinado y esponjoso La
nueva versión del proceso tiene este aspecto. El proceso es paralelo a la verificación de confiabilidad y la búsqueda de posibles propuestas. La búsqueda también está paralela. En este caso, se ejecutarán aquellas ramas en las que se cumplan las condiciones correspondientes.
Para la paralelización con las condiciones, se utiliza la puerta de enlace inclusiva, que se indica mediante el siguiente icono:
Inclusive Gateway es una puerta de enlace paralela con condiciones de bifurcación. Se ejecutarán las ramas en las que se cumplen las condiciones.
Hay dos tipos de puertas de enlace:
- fork: para cada rama con una condición cumplida, se crea la ejecución, que se ejecuta en paralelo de la misma manera que la ejecución en Parallel Gateway;
- join, a diferencia de Parallel Gateway, no espera que las ejecuciones ejecuten todas las ramas, sino solo aquellas en las que la condición es verdadera.
Puede suceder que las comprobaciones realizadas sean insuficientes y que el cliente deba verificarse nuevamente. Para hacer esto, agregue una condición al final de todas las verificaciones, que puede enviarse para volver a verificar desde el principio:
Diagrama 3. La versión final del proceso que debería funcionar
Resultó ser engorroso, pero el proceso resuelve el problema.
¿Qué? ¿Que pasó?
Aquí comienzan a suceder cosas extrañas. La rama de verificación de confiabilidad funciona y llega a la puerta de enlace paralela de recolección. Hasta ahora, todo va bien.
La segunda rama verifica la condición del material y, según los resultados, se realizan las tareas correspondientes. Además, el proceso se detiene en la puerta de enlace que recoge la puerta de enlace inclusiva y no avanza más. Si observas el Coockpit (panel de administración de Kamunda), las ejecuciones dependerán de la puerta de enlace inclusiva y de la puerta de enlace paralela.
Diagrama 4. Proceso de servicio atascado El trabajo está
hecho. Podemos decir que tenemos un punto muerto en el proceso en Camunda. En este caso, no está directamente relacionado con los puntos muertos de la teoría de la programación paralela y los puntos muertos.
En busca de ̶̶r̶i̶k̶l̶uch̶e̶n̶i̶y̶ respuesta
Como no entendía lo suficiente lo que sucedió y por qué se detuvo el proceso, el problema tuvo que resolverse empíricamente.
¿Quizás necesita una rama predeterminada para la puerta de enlace inclusiva y, sin ella, el proceso no puede ejecutarse normalmente?
Extraño, por supuesto, pero intenta agregar una rama predeterminada. La presencia de una rama predeterminada es una buena práctica, ya que de lo contrario no se puede cumplir una sola condición y luego obtendremos un error.
Diagrama 5. Proceso de servicio con una rama predeterminada.
Comenzamos y obtenemos el mismo resultado: el proceso permanece suspendido en la puerta de enlace inclusiva.
Lo siguiente es ordenar todo tipo de parámetros, leer la documentación, y se prolonga durante medio día. En otro intento, el proceso inesperadamente pasa la mala suerte. La rama inferior con Inclusive Gateway funcionó en una situación en la que, durante el proceso de búsqueda y depuración, la rama superior se eliminó con una verificación de confiabilidad del cliente. Es decir, cuando el proceso degeneró solo en la rama inferior con la puerta de enlace inclusiva, el proceso finalizó.
Diagrama 6. Proceso degenerado
Resulta que Parallel Gateway influye de alguna manera en el Inclusive Gateway. Esto es extraño, ilógico, y no debería ser así.
¿Cómo es esto posible? Probablemente valga la pena volver a leer la teoría sobre cómo Parallel and Inclusive Gateway funciona nuevamente. ¿Qué debería pasar para que la puerta de enlace de unión reúna a todos y el proceso continúe? En Internet, escriben que cada puerta de enlace inclusiva (unión) que se espera espera a que ingrese el mismo número que la "bifurcación". Entonces surge una pregunta más: ¿cómo funciona este contador?
¿Que eres? ¿Como trabajas?
Este problema es digno de juegos de rompecabezas y programas de televisión inteligentes. Solo en los programas de televisión se les permite llamar a un amigo. Por otro lado, también puedo pedir ayuda. Llamaremos a nuestro arquitecto de procesos de negocio Denis.
- Denis, hola! ¿Puede decirme cómo la ruta de recolección determina cuándo es el momento para que el proceso avance? En todas partes escriben: "Cuánto salió, tanto debería entrar". Pero, ¿cómo lo piensa exactamente?
- Muy simple. Camunda cuenta el número de ejecuciones activas.
- Muchas gracias. Por ahora
considere lo que sucedió. Para hacer esto, una vez más recupere el esquema inicial, que resultó:
Diagrama 7. Proceso de suspensión con una rama predeterminada
Para simplificar, consideremos el caso cuando se cumplen todas las condiciones. ¿Qué tenemos en el momento en que se cumplen tres tareas después de estas condiciones?
¿Cuántas ejecuciones activas? Tres en la rama inferior y uno en la superior, donde verificamos la confiabilidad del cliente. A Camunda no le importa que estas sean ramas paralelas completamente diferentes. Solo me interesa la cantidad de ejecuciones activas, de las cuales hay cuatro, y la puerta de enlace inclusiva entrante recibió solo tres.
Fijación
Para rectificar la situación, el Gateway de recopilación debe recopilar todas las ejecuciones a la vez, y luego, en teoría, el proceso continuará. Intentemos dejar una en lugar de dos puertas de enlace de combinación:
Diagrama 8. Versión corregida del proceso Por
desgracia, después de los cambios, el proceso comenzó a parecer, en mi opinión, menos obvio. Pero funcionó según lo planeado inicialmente. En este punto, la búsqueda terminó de manera segura, pude empujar los cambios e irme a casa.
Interesante apenas comienza
Cuando me senté a escribir este artículo y se me ocurrió un ejemplo de un proceso en el que podría describir este caso, me decepcionó: el proceso funcionó como debería y no hubo punto muerto.
Al principio supuse que la versión de Camunda en el ejemplo es más alta que en el proyecto, y este problema ya se ha solucionado en la nueva versión. Pero degradar a Camunda no hizo nada. Por cierto, en todos los ejemplos se usa la versión 7.8.0: está lejos de ser la más reciente, pero en principio no importa. El problema también se ha probado y reproducido en la última versión, 7.13.
Por prueba y error, se estableció el problema. El ejemplo artificial inicial no tenía una rama inversa, a diferencia del proceso que desarrollé en el lugar de trabajo.
Resulta que en presencia de una rama inversa, el problema se reproduce y nos encontramos en una especie de punto muerto, pero sin una rama inversa, todo funciona como debería.
El caso exigió comprensión y análisis. Para hacer esto, tuve que mirar las fuentes de Camunda BPM. Dado que el problema era con Inclusive Gateway, parecía lógico buscar una respuesta en la clase que sea responsable del comportamiento de este elemento: InclusiveGatewayActivityBehavior . Al ejecutar un par de veces la depuración en ambas versiones del proceso, me di cuenta de cómo funciona.
Si no está claro, ¡vea las fuentes!
Para no hacer una historia aburrida, la descripción del trabajo de InclusiveGateway basada en el código fuente será incompleta. La lógica de interés para nosotros se concentra en el método de ejecución , donde el método activatesGateway es el más valioso para este caso . Según tengo entendido, verifica si es posible pasar la InclusiveGateway. Se llama al método execute para cada ejecución (para cada rama en ejecución). En nuestro caso, hay tres ramas de este tipo, lo que significa que este método se llamará tres veces.
Veamos cómo funciona el método activatesGateway. Para una mejor comprensión, daremos nombres a todas las ramas que se ejecutan.
Diagrama 9. Diagrama de proceso con ejecuciones
Según tengo entendido, la lógica del método es la siguiente:Hay una comparación de la cantidad de ejecuciones que llegaron a esta ruta y la cantidad de flechas incluidas en esta ruta . Esta verificación se realizó en el caso de la situación más simple, cuando se ejecutan todas las ramas de la puerta de enlace inclusiva, y la lógica de verificar la puerta de enlace de recolección es esperar hasta que el número de ejecuciones ingresadas sea igual al número de flechas entrantes. Es decir, en el caso más simple, el método de ejecución se llama tantas veces como haya ramas en la puerta de enlace de recopilación, luego el proceso continúa.
En nuestro caso, este método se llama tres veces, porque el número de ejecuciones que llegan aumentará de 1 a 3. En la última llamada, el número de llegadas y las esperadas serán 3 y 4, respectivamente, y saldremos en la rama falsa.
Si no se cumple la condición, se verifica que las ejecuciones restantes pertenezcan a la puerta de enlace inclusiva. Es decir, se verifica la capacidad de las ejecuciones activas para llegar a la Inclusive Gateway.
Aquí tienes que ser paciente, exhalar y leer. ¡El desenlace está cerca!
En la rama falsa del método activatesGateway, en cada llamada, los que aún no han llegado a las ejecuciones de unión inclusiva se verifican la posibilidad de llegar a esta unión. Si al menos una ejecución puede conducir a una puerta de enlace inclusiva, debe tenerla en cuenta y esperar a que también se una a esta unión. Si no hay ejecuciones que puedan resultar en unir, el método devolverá verdadero.
La parte más interesante está por llegar. A primera vista, la última ejecución (ejecución 1 en el diagrama) no puede conducir a la puerta de enlace inclusiva. Pero vale la pena observar la implementación del método canReachActivity , que se dedica a esta verificación, y la razón de este comportamiento de este elemento quedará clara.
Si descartamos todos los detalles del código, entonces el método isReachable se llama de forma recursiva dentro de este método, que paso a paso verifica la posibilidad de que esta ejecución ingrese a la puerta de entrada inclusiva. La rama inversa solo brinda esa oportunidad y, por desgracia, esto se tiene en cuenta, aunque no debería, ya que volveremos después de todas las uniones.
Como resultado, Inclusive Gateway está esperando otra ejecución, que nunca llegará. Por lo tanto, tenemos una especie de punto muerto. En principio, si descartamos las convenciones, obtenemos un punto muerto clásico: unirse en Parallel espera a que se ejecute la rama con Inclusive y, por el contrario, la rama con Inclusive espera a que se ejecute Parallel.
El siguiente diagrama muestra la dirección aproximada de verificar la accesibilidad de una unión de puerta de enlace inclusiva desde la ejecución, que vino a unirse a la puerta de enlace paralela a través de una rama paralela.
Diagrama 10. Posible camino desde
la unión en paralelo a la unión inclusiva El diagrama muestra que, de hecho, la puerta de enlace inclusiva de la unión está disponible desde la unión de la puerta de enlace en paralelo, y de acuerdo con la lógica de Camunda BPM, no importa que ya exista un "cable circular".
Después de descubrir las razones, surgió la pregunta involuntariamente: ¿es esto un error o una característica? En mi opinión, esto es un error. Ahora estoy recopilando información y casos para enviar un informe al equipo de Camunda.
Es bueno que el problema esté localizado. ¿Pero y ahora qué?
En realidad, ahora - las conclusiones:
- Prevenido vale por dos. Necesitamos construir nuestros procesos teniendo en cuenta este comportamiento de Camunda.
- , . parallel join.
- Inclusive Gateway , , executions .
- , . , Parallel Gateway .
La aparente simplicidad y claridad a veces son engañosas. Esto solo se puede combatir mediante la acumulación y la replicación del conocimiento. Por desgracia, al momento de resolver este problema, no tenía un conocimiento profundo de la lógica de la Inclusive Join, así que tuve que pensar. Obtuve este conocimiento por prueba, error, llamando a un amigo y fuente de depuración.
De todo esto se deduce una conclusión obvia y lejos de ser nueva de que necesita comprender cómo funciona la herramienta que está utilizando. Cuanto mejor entiendas, menos problemas serán.
La segunda conclusión también es bastante obvia: necesita descomponer no solo el código, sino también los procesos.
Enlaces que fueron útiles para analizar este caso y escribir un artículo: