Blog ·
Una unión en lugar de una conciliación
Un paso de automatización se congeló en el estado que vio. El arreglo no era un proceso de fondo: esa fila explica una decisión, y repararla la destruye.
Por Provibr
Un flujo de automatización puede enviar un comando a una máquina y esperarlo. Si la espera se agota, el paso falla y la ejecución toma su salida de error. Es lo correcto: el flujo pidió esperar hasta que el trabajo estuviera hecho, y no lo está, así que nadie sabe si el nodo siguiente va a actuar sobre una máquina que todavía se está apagando.
El paso registra lo que vio en ese instante. Medido en una ronda anterior contra un agente real, registró sent, y sesenta milisegundos después el comando pasó a tiempo agotado. El paso seguía diciendo sent, un día más tarde, para siempre.
La lectura evidente es que la fila está caducada y hay que repararla. Esa lectura es incorrecta, y averiguar por qué costó la mayor parte del pensamiento de esta ronda.
La fila es una explicación, no un campo de estado
El valor congelado no es una copia del estado del comando que se haya desviado. Es la respuesta a otra pregunta: qué sabía el flujo cuando decidió tomar la salida de error.
Si se sobrescribe con timeout, la fila dice ahora que el flujo reaccionó a un tiempo agotado. No lo hizo. Reaccionó a una espera que se acababa mientras el comando seguía en vuelo, un suceso distinto con otra causa y otro arreglo. Un mes después, quien leyera esa ejecución sacaría la conclusión equivocada de una fila corregida con buena intención.
El valor congelado se queda congelado, pues. Lo que estaba realmente mal era el comentario de encima, que prometía deducir de esta fila si el comando acabó teniendo éxito. No se podía, y una promesa en un comentario dura hasta el día en que alguien confía en ella.
La pregunta de verdad, si al final salió bien, se responde leyendo el estado actual del comando en el momento en que alguien mira. La página de la ejecución muestra ambos, uno al lado del otro: lo que vio el flujo entonces y lo que es cierto ahora.
| De dónde procede | Qué responde | |
|---|---|---|
| Entonces | La fila de auditoría, congelada en el momento de decidir | Por qué la ejecución tomó la salida que tomó |
| Ahora | La tabla de comandos, leída al abrir la página | Si el trabajo acabó terminando |
| Fila ausente | Ninguna de las dos; se muestra como estado propio | El comando ya no figura en los libros |
Esa última línea importa. Si la fila del comando se ha limpiado, la vista no recae en repetir el valor congelado. «Ya no figura en los libros» y «sigue diciendo sent» son dos hechos distintos, y una página que dibuja el segundo cuando el cierto es el primero miente sin pestañear.
Tres razones por las que un proceso de fondo pierde ante una unión
La alternativa era un proceso de fondo que reescribiera el estado final en la fila de auditoría en cuanto el comando quedara resuelto. Hay procesos con esa forma exacta en otras partes, así que era una elección real y no un espantapájaros.
Un proceso de fondo sobrescribe una explicación. Ya visto. La salida es escribir un segundo campo al lado, y eso es honrado, pero es una segunda verdad que se leería en exactamente un sitio: el que ahora hace la unión.
Un proceso de fondo solo repara lo que sucede después de él. La fila que inició toda esta investigación ya existe. Uno desplegado hoy no la tocaría nunca, ni tampoco ninguna ejecución de las dos rondas anteriores, porque sus comandos quedaron resueltos hace mucho y ningún suceso futuro volverá a pasar por ahí. Una unión las muestra todas desde el día en que se publica, incluida la que se buscaba.
Un proceso de fondo trae todo un aparato. Actualizar filas en segundo plano exige una cadencia, una reclamación para que dos instancias no choquen, una espera contra la deriva, una respuesta sobre conservación y un camino de fallo propio, y cada uno de ellos puede estar mal a las tres de la madrugada. Buscar un puñado de identificadores por clave primaria cuando alguien abre una página no exige nada de eso.
La regla general de fondo: mejor leer la fuente que copiarla, salvo que la copia responda una pregunta que la fuente no pueda responder. Aquí la copia responde otra pregunta y se quieren las dos, que es justo el caso en que conviene mantenerlas separadas.
El identificador salió de una columna JSON, así que la consulta va acotada
Un detalle de esa unión no es optimización ni cortesía.
El identificador del comando vive dentro de un bloque JSON de la fila de auditoría. Las columnas JSON no tienen claves foráneas, así que nada garantiza que el identificador de ahí dentro pertenezca a esta cuenta. Hoy pertenece, porque lo escribió nuestro propio código. Eso no es una frontera, es una costumbre.
La búsqueda queda por tanto acotada a la organización además de al identificador. Sin esa frontera, un valor editado a mano en ese bloque leería el estado del comando de otra persona. Fuga pequeña, fácil de pasar por alto, y de las que solo encuentra quien las buscaba.
Un flujo por momento, con el momento dentro de la clave
Un plan de alojamiento podía enlazar un flujo de automatización, en una sola columna. Esa columna significaba «ejecuta esto cuando se aprovisione una máquina», y eso no estaba escrito en ninguna parte salvo en el comportamiento de la función que la leía.
Los clientes querían también un flujo al reinstalar y otro al eliminar. El arreglo evidente son tres columnas. Es incorrecto por un motivo que merece nombrarse: cada una de esas tres columnas podría contener un flujo cuyo propio disparador diga otra cosa. Un flujo que corre al eliminar, sentado en la columna que se dispara al aprovisionar. La invariante «el disparador dice cuándo corre» pasa a ser violable en tres sitios en lugar de en ninguno, y el esquema no objeta nada.
El enlace se convirtió pues en una tabla propia, con el plan y el momento juntos como clave única. Un flujo por momento y por plan, impuesto por un índice en lugar de por cuidado.
Dos detalles de esa migración merecen conservarse.
El momento se guarda como texto con una restricción de comprobación que enumera tres valores, y no como la enumeración de disparadores existente. Esa enumeración tiene cinco valores, y dos de ellos son disparadores que este mecanismo no elige nunca: una ejecución manual la inicia una persona, y una por webhook una petición entrante. Una columna enumerada aceptaría cualquiera de los dos, y entonces existiría una fila de enlace con la que nunca pasa nada. Un ajuste que en silencio no hace nada es peor que un ajuste que falta.
Y la columna antigua se eliminó en la misma migración que copió su contenido. Dejarla como cómoda lectura derivada es precisamente la segunda verdad que esta tabla viene a suprimir. Al compilador se le hicieron visibles todos los lectores en cuanto la columna salió del esquema; eran siete. La copia corre antes del borrado, en una transacción, así que no hay instante en que la configuración de un cliente no esté en ninguna parte.
La copia toma además el disparador propio de cada flujo en lugar de suponer el significado de la columna vieja. Las filas cuyo flujo llevaba otro momento no habrían corrido nunca con el código antiguo; ahora el enlace que hizo una persona hace de verdad lo que ella creía.
Correo por adhesión, en el grafo y no en los ajustes
Un flujo puede escribir un aviso, y en esta ronda ese aviso puede salir también por correo. Resultó ser una cuestión de valores por omisión y no de envío.
Las categorías de correo de la plataforma son una lista de exclusión: lo que no está apagado expresamente, está encendido. Eso es correcto para las categorías que ya existen, todas ellas cosas que un cliente necesita saber. No lo es para un aviso cuyo texto escribió alguien en un editor de automatización, porque un flujo que corre en cada pedido escribiría a todo el equipo cada vez hasta que alguien encontrara el ajuste.
En lugar de invertir la lista, el valor por omisión vive en el grafo: la casilla del nodo está apagada, y encenderla es un acto deliberado de quien escribió el texto. La categoría sigue existiendo para poder apagar el correo en el sitio de siempre, igual que las demás. Dos mecanismos, cada uno haciendo aquello en lo que es bueno.
Una ejecución de prueba nunca envía correo. Escribe el aviso del panel, que es interno y se descarta con un clic, y no envía nada hacia fuera, por la misma razón por la que una ejecución de prueba nunca toca una máquina: el correo llega al buzón de personas reales, y un botón de prueba que sorprenda a alguien no es un botón de prueba.
Otra cosa menor del mismo trabajo. Se daba por hecho que el lado del envío era inalcanzable desde donde corren los flujos, y por eso esto estaba aplazado. Medido, resultó al revés: la función que se pretendía rodear existe precisamente porque el otro proceso no sabe componer correo, y pide al proceso en el que los flujos ya corren que lo componga. El rodeo era más largo que el camino recto, y el camino recto tiene un salto HTTP, una firma y un camino de fallo menos.
Lo que anotaríamos
- Una fila de auditoría explica una decisión. No hay que repararla. La pregunta de qué es cierto ahora tiene otra respuesta y merece su propio sitio.
- Una reparación en segundo plano solo arregla el futuro. Si la fila que motivó el trabajo no se tocaría, eso es una respuesta de diseño y no un detalle.
- Leer la fuente, salvo que la copia responda algo que la fuente no pueda.
- Un identificador salido de una columna JSON no tiene clave foránea. Acotar la consulta a la cuenta; que el valor lo escribiera nuestro código no es una frontera.
- Poner la distinción en la clave y no en el nombre de la columna. Tres columnas son tres sitios donde guardar lo incorrecto; una clave es ninguno.
- Eliminar la columna vieja en la misma migración. Una lectura derivada que se queda es la segunda verdad que se estaba suprimiendo.
- Una lista de exclusión es el valor por omisión equivocado para texto que escribió un cliente. La adhesión va donde se escribe el texto.
Para que quede claro: todo esto está construido y funcionando. El envío en sí se demostró con un controlador que solo registra y no contra un servidor de correo real, y el enlace del plan aún no se ha ejercitado mediante un pedido real.
Del trabajo de plataforma en Provibr. Cómo se comportan la espera y la salida de error para empezar está en un cable que se arrastra, y un solo disparador, y por qué un tiempo agotado no es un botón de parada está en un tiempo de espera no es un botón de parada.