Blog ·
Actualizado a las tres, a propósito
La actualización automática del agente viene activada a las 03:00. Lo interesante: qué situaciones consumen una franja y una noche con dos veces las tres.
Por Provibr
El agente que corre en la red propia de un cliente recibe una compilación nueva cada pocos días. Hasta esta ronda, actualizarlo era un botón que alguien tenía que pulsar, de modo que en la práctica ocurría cuando alguien se acordaba de mirar. El deseo era una frase: cada cual puede fijar en su agente un horario de actualización, varios momentos al día, activado por omisión a las tres de la madrugada.
La frase es fácil. Lo que la convirtió en una ronda es que un reinicio programado de infraestructura tiene muchas maneras de estar mal en silencio, y casi todas se ven bien desde fuera.
El valor por omisión está en la columna, y ahí está todo el deseo
«Activado por omisión para todo el mundo» admite dos lecturas. Escrito en el código de la aplicación se aplica a los agentes creados a partir de ahora, lo que excluye justo a los clientes que ya tienen uno. Escrito como valor por omisión de columna en la migración se aplica a cada fila existente en una sola sentencia.
Así que es un valor por omisión de columna: un booleano que empieza en cierto y un arreglo JSON que empieza con una única entrada 03:00. Medido tras aplicar la migración sobre la base de datos de desarrollo: cada agente existente activado, cada uno a las tres, y un agente recién creado que no recibió ninguno de los dos valores quedó igual. Esa última comprobación es la que demuestra que el trabajo lo hace el valor por omisión y no un guion de siembra.
Los momentos son hora de reloj en la zona horaria de la plataforma, no UTC. No existe una zona por organización y el panel lo muestra todo en esa única zona, así que un horario guardado en UTC se desplazaría una hora en verano y a quien escribiera las tres se le interrumpiría a las cinco.
Dos capas sobre una misma expresión regular
Un momento es HH:MM sobre una rejilla de cinco minutos, y ese patrón existe dos veces: una en el validador de esquema, que rechaza un campo de formulario con un mensaje legible, y otra como restricción de base de datos, que rechaza todo lo que nunca pasó por ese formulario.
Una restricción de comprobación no puede contener una subconsulta, así que «cada elemento de este arreglo es un momento válido» no puede ser una prueba de existencia sobre el arreglo desplegado. Lo que sí puede ser es un predicado de ruta JSON que selecciona justo los elementos incorrectos, envuelto en una prueba de que esa selección está vacía.
La primera versión de ese predicado era sutilmente inútil, y parecía completa:
$[*] ? ( !(@ like_regex "^([01][0-9]|2[0-3]):(00|05|10|15|20|25|30|35|40|45|50|55)$") )La coincidencia de una expresión regular contra algo que no es una cadena no es falsa en ruta JSON. Es Unknown. Y la negación de Unknown también es Unknown, y un filtro conserva solo los elementos para los que el predicado es verdadero de verdad. La selección de elementos incorrectos volvía por tanto vacía para un arreglo de números, y la restricción lo aceptaba. Medido contra PostgreSQL 17.10, [3], [null] y [{"a":1}] pasaron los tres.
El arreglo es una cláusula delante: los elementos cuyo tipo no es cadena son incorrectos por definición. Los tres se rechazan ahora. Es exactamente la clase de fallo que nos sigue costando rondas: una salvaguarda que no atrapa nada y parece completa, y ninguna prueba falla, porque no había nada contra lo que fallar.
Cinco pasos, y el orden es la ronda
Cada minuto un trabajador pregunta quién tiene horario, entrega las filas a un planificador puro que decide qué franja toca, y luego recorre cinco pasos por agente. La reserva es el paso cuatro y no el uno, y cada paso anterior está ahí por un motivo propio.
| Paso | Si la respuesta es no |
|---|---|
| ¿Hay una compilación más reciente? | La franja se reserva igualmente, marcada como nada que hacer |
| ¿Está conectado el agente? | No se reserva nada, y el minuto siguiente lo vuelve a intentar |
| ¿Hay ya una actualización en curso? | No se reserva nada; la primera cumplirá la franja |
| Reservar la franja | Decide un índice único; quien pierde no envía nada |
| Enviar la orden | La misma función que el botón del panel |
Las dos primeras filas son opuestos deliberados, y esa asimetría es la decisión más interesante de la ronda.
Nada que hacer consume la franja, porque la respuesta ya no puede cambiar dentro de una franja: el manifiesto de la publicación en disco es el mismo durante toda la ventana, así que volver a juzgar cada minuto cuesta consultas y no aporta nada. Y le da al cliente algo que ver. Una página de agente que dice «03:00, ya al día» se distingue de un horario que no corre. Una lista vacía no.
Estar desconectado no consume nada, porque una franja reservada mientras la máquina se está reiniciando queda consumida para siempre, y entonces ese cliente pierde una noche en silencio. El minuto siguiente lo vuelve a intentar hasta que se cierra la ventana de recuperación; después el agente toma sencillamente su franja siguiente.
Quince minutos, y no seis horas
Las copias de seguridad tienen en esta plataforma una ventana de recuperación de seis horas. Esta es de quince minutos, y la diferencia está en lo que se estropea si se es generoso.
Una copia hecha a las nueve de la mañana en vez de a las tres sigue siendo una copia. Un agente que se reinicia a las nueve de la mañana en vez de a las tres es una interrupción en plena jornada, y las tres de la madrugada son justo lo que el cliente eligió para evitarlo. Quince minutos cubren aquello para lo que esta ventana existe de verdad, una pasarela recién reiniciada o un par de rondas saltadas, y nada más.
El planificador mira dos días de calendario atrás y no uno, y no es un descuido. Un momento de 23:55 con ventana de recuperación cae, justo pasada la medianoche, en el día de calendario de ayer; mirando solo hoy, esa franja no llegaría a tocar nunca por construcción.
Dos frenos, por dos motivos distintos
Cinco agentes por organización y ronda, y veinticinco en total.
El freno por organización trata del ancho de banda del cliente y no del nuestro. El binario ronda los veintidós megabytes, un alojador con cuarenta agentes suele tenerlos los cuarenta a las 03:00 porque es lo que viene puesto, y cuarenta descargas simultáneas salen por la misma conexión. Cinco por minuto reparten una flota de cuarenta a lo largo de ocho minutos, holgadamente dentro de la ventana de quince.
El freno global existe porque el primero funciona por organización: con cien organizaciones serían quinientas actualizaciones en un minuto. El resto llega al minuto siguiente, todavía dentro de la ventana.
No hay ninguna bandera de despliegue sobre nada de esto, cosa rara en nosotros. El interruptor ya está en el cliente, en la columna; una segunda bandera de plataforma prometería lo que la columna ya promete. Lo que sí hay es un freno de emergencia para nosotros: poner el intervalo de sondeo a cero detiene el temporizador sin cambiar ni un ajuste de cliente.
La noche con dos treses
El último domingo de octubre la hora de reloj 03:00 existe dos veces, y el último domingo de marzo las 02:30 no existen en absoluto. Las dos las resuelve una función auxiliar compartida, no copiada en este módulo, precisamente porque un algoritmo de horario de verano mantenido en dos sitios diverge un día al año.
Esa función responde a una hora de reloj ambigua con el primero de los dos instantes, y a una inexistente con el instante siguiente que sí existe. Las consecuencias son las que se quieren. En la noche larga, las 03:00 corren una vez y no dos; medido: una franja. En la noche corta, las 02:30 pasan a ser las 03:30 locales en lugar de saltarse, porque no saltarse es el lado seguro de esa moneda. Las dos quedan fijadas por pruebas.
Los días se recorren como días de calendario y no sumando un número fijo de milisegundos: sumar veinticuatro horas a través de un cambio salta un día o repite uno.
A quién se le cuenta, y cada cuánto
La conciliación que informa de los desenlaces vive en el trabajador y no en la vía de envío, y solo el horario avisa. Un clic a mano ya tenía a alguien mirando.
Los avisos se agrupan por organización, porque cuarenta agentes el día de una publicación no deben ser cuarenta filas en una bandeja. Un fallo se comunica como mucho una vez por agente y día de calendario. Y la marca de tiempo que registra que se valoró avisar se escribe también cuando la decisión fue callar, de modo que el silencio se distinga de un trabajador que nunca llegó.
Una actualización programada no aparece a propósito como operación raíz en el cajón de tareas: a las tres de la madrugada no espera nadie, y un cajón lleno de filas nocturnas es inútil para aquello para lo que existe. El rastro son una fila de ejecución, un suceso y un aviso. Ese suceso es de una clase propia, porque la línea de tiempo no debe afirmar que a las tres el cliente arrancó algo en persona.
Lo que anotaríamos
- «Por omisión para todo el mundo» es un valor por omisión de columna. En el código de la aplicación significa «para todo el mundo de ahora en adelante», y eso excluye justo a quienes tenía en mente.
- Unknown no es falso. En ruta JSON, un predicado sobre un valor del tipo equivocado no es ni verdadero ni falso, y negarlo no ayuda. Compruebe primero el tipo.
- Decida en cada situación si consume la franja. Nada que hacer e inalcanzable son respuestas opuestas, y ambas son invisibles si se intercambian.
- Una ventana de recuperación es una promesa sobre la interrupción, no sobre la completitud. Copie el número de un sistema con otro modo de fallo y tendrá el equivocado.
- Ponga el freno donde de verdad se nota. Cuarenta agentes en una red de cliente comparten un enlace, y ese es el límite, no nuestra ruta de descarga.
- Resuelva el horario de verano una sola vez. Dos copias del mismo razonamiento coinciden hasta el último domingo de octubre.
Para que quede claro qué es esto: el horario está en producción y activado por omisión. Varias cosas no están probadas. El trabajador no se ejerció dentro de una pasarela realmente en marcha, ningún agente real ha ejecutado todavía una actualización programada, el aviso de fallo y su deduplicación diaria no se observaron de extremo a extremo, y la pantalla se revisó en dos de los nueve idiomas.
Del trabajo de plataforma de Provibr. Por qué un agente rechaza una actualización que no puede verificar está en cómo una actualización firmada del agente se rechaza a sí misma, y por qué el agente conecta hacia fuera en vez de escuchar está en un agente saliente en lugar de redirección de puertos.