Blog ·
La retención se convierte en la colocación
Mover una máquina entre dos racks tiene un instante peligroso: aquel en que el destino ni está reservado ni está ocupado. Así es como lo eliminamos.
Por Provibr
Mover un servidor de una unidad de rack a otra es, físicamente, un trabajo corto con un destornillador y un segundo par de manos. El fallo interesante vive en la administración que lo rodea, y vive en un solo instante: aquel en el que el destino ya no está reservado y todavía no está ocupado.
Si la escritura de otra persona cae en ese instante, quedan registradas dos máquinas en el mismo slot. En una base de datos eso se lee como una violación de restricción sobre la que se puede discutir. En la sala se lee como un ingeniero plantado delante de un rack con una máquina en las manos y ningún sitio donde ponerla.
La ventana, y cómo no tenerla
La secuencia evidente son tres pasos. Liberar la asignación antigua, liberar la retención por traslado sobre el destino, escribir la colocación nueva. Dentro de una sola transacción esa ventana es invisible para las demás sesiones, que es exactamente el motivo por el que resulta fácil dejarla ahí: va a pasar todas las pruebas que se escriban. Sigue siendo una ventana, porque la sentencia posterior opera sobre un slot que la sentencia anterior liberó, y basta con un orden equivocado en una refactorización posterior.
Nosotros hacemos algo más simple. La retención por traslado se promueve en lugar de sustituirse. Las filas que registran qué unidades están ocupadas se quedan exactamente donde están; lo único que cambia de clase es la cabecera, que pasa de retención por traslado a colocación, y su caducidad se pone a nulo en la misma actualización. La colocación antigua se cierra en la misma transacción. No hay ningún punto en el que el destino esté libre, así que no hay una segunda oportunidad de colisionar, y todo el asunto es una actualización y no una inserción que tiene que salir bien.
El identificador de la asignación no cambia a lo largo de la promoción, que es la manera de comprobar que ha ocurrido realmente esto y no algo que desde fuera solo se le parece.
Poner la caducidad a nulo no es pulcritud. Una comprobación en la base de datos rechaza una colocación que lleve caducidad, porque una colocación que deja de contar en silencio un martes mientras la máquina sigue atornillada es un rack en el que el software cree que hay una unidad libre. Una reserva que caduca es una funcionalidad; una colocación que caduca es una mentira con temporizador.
El árbitro sigue siendo un índice
Nada de lo anterior es lo que impide una doble ocupación. Eso sigue siendo un índice único parcial sobre el rack, la cara y el slot, limitado a las filas que no se han liberado, y una retención por traslado cae bajo él exactamente igual que una colocación.
Dos ingenieros que eligen la misma unidad en el mismo segundo la ven libre los dos en la función de planificación que se ejecuta antes de la escritura, y exactamente uno de ellos atraviesa la base de datos. La escritura del otro falla sobre el índice, y la única vía que escribe la ocupación captura esa violación y la convierte en una negativa legible que nombra las unidades que chocan. Cuál de las dos frases aparece lo decide el nombre de la restricción que saltó, porque «esta unidad está ocupada» y «esta máquina ya está en otro sitio» mandan a una persona a dos racks distintos. Una inserción directa que se salta esa vía recibe en cambio la violación en crudo. Medido de las dos maneras, porque razonar sobre niveles de aislamiento no es prueba.
La función de planificación es un mensaje, no una defensa. La defensa es el índice. Mantener separados esos dos papeles es lo que permite que el mensaje sea útil sin que nadie caiga en la tentación de fiarse de él.
Nueve estados, y dos salidas que dejan de existir
Un traslado tiene nueve estados: borrador, planificado, aprobado, en curso, pendiente de verificación, pendiente de conciliación, completado, cancelado y fallido. Qué transiciones son legales es un registro sobre los nueve y no una sentencia switch. Un switch con rama por defecto deja que un estado recién añadido caiga hacia permitido, y la dirección peligrosa aquí es justamente esa: un estado que alguien añade porque está pasando algo inusual sería el primer estado con permiso para hacer cualquier cosa.
La fila interesante es la que sale de in_progress, porque le faltan dos salidas que sí tienen todos los estados anteriores: no hay camino de vuelta a aprobado ni camino hacia cancelado. En ese punto alguien tiene la máquina en las manos, y las únicas salidas honestas son «se informa de que está hecho», «no lo sabemos» y «ha fallado». Cancelar es una decisión que se toma de antemano; después de que se haya informado de trabajo físico, una reversión en la base de datos es una afirmación sobre el mundo físico que nadie ha verificado.
Los tres estados terminales, completado, cancelado y fallido, llevan listas vacías y no la ausencia de entrada. Esa es la diferencia entre «desde aquí no sigue nada» y «nadie ha pensado en esto», y solo lo primero es una decisión.
Una segunda tabla dice, por estado, si el destino debe seguir retenido. La entrada que merece defensa es needs_reconcile, que lo mantiene. En ese punto nadie sabe dónde está la máquina, así que liberar el destino podría poner una segunda máquina sobre una unidad que quizá ya esté ocupada.
Trasladarse no es estar en tránsito
Los activos llevan un estado de proceso, y este trabajo añadió moving junto al ya existente in_transit. Suenan a sinónimos y no lo son. moving significa que la máquina se está reubicando dentro de este centro de datos y que su espacio de destino está reservado. in_transit significa que va por carretera entre dos emplazamientos y que por tanto no está en ninguna parte. Los dos bloquean la entrega a un cliente, pero responden a preguntas distintas cuando alguien pregunta dónde está una máquina, y fusionarlos dejaría mal la respuesta a la segunda pregunta. La separación se impone y no solo se describe: una comprobación en la base de datos exige que el emplazamiento de origen y el de destino de un traslado sean el mismo, así que un traslado es por construcción algo que ocurre dentro de un mismo edificio, y la carretera entre dos emplazamientos es otro proceso con otra palabra.
Qué estado tenía la máquina antes del traslado se guarda en la comprobación previa congelada, no se supone. El hardware recién llegado está en staging, y devolverlo todo a normal después daría de baja en silencio de la fase de recepción a toda máquina reubicada.
Dos salidas de la incertidumbre, y ninguna es una reversión
Cuando el resultado físico es incierto, el traslado pasa a needs_reconcile y alguien tiene que ir a mirar. Hay dos respuestas, y las dos se registran como observaciones.
| Observación | Qué le ocurre al traslado | Qué le ocurre al espacio |
|---|---|---|
| Vista en el destino | Completado | La retención se convierte en la colocación, como arriba |
| Vista en el origen | Fallido, con un motivo que dice que nunca se ejecutó | La retención se libera; la colocación original queda intacta |
La segunda es deliberadamente fallida y no cancelada. Alguien hizo trabajo físico y la máquina no se movió; eso es un resultado, no una elección. Y no es una reversión: la base de datos no se devuelve a un estado que finja que no pasó nada. El historial de ubicación es de solo adición, las correcciones son asientos compensatorios nuevos, y la diferencia aparece un mes más tarde cuando alguien pregunta por qué una máquina estuvo quieta un día. Una reversión no deja nada detrás. Un asiento compensatorio muestra que hubo una incertidumbre y cómo se resolvió.
Esas filas de historial llevan identificadores de emplazamiento y de rack sin claves ajenas, a propósito. Un rack que se retire el año que viene no cambia lo que estuvo dentro de él el marzo pasado.
Dos decisiones pequeñas que importarán más adelante
El traslado apunta a su operación en segundo plano con una referencia que está en SET NULL cuando la operación se borra, en vez de CASCADE. Las operaciones se podan a los noventa días. La administración de hardware no, y una cascada significaría que la política de retención de las barras de progreso borra en silencio el registro de adónde fue una máquina.
Y un índice único parcial permite un traslado abierto por activo. Sin él, dos personas pueden planificar dos destinos distintos para la misma máquina y las dos parecen correctas en su propia pantalla.
Lo que dejaríamos por escrito
- Promover, no volver a crear. Si una reserva y una asignación tienen la misma forma, cambiar la clase en lugar de liberar y escribir, y comprobar que el identificador ha sobrevivido.
- Dejar el árbitro en la base de datos y la explicación en el código. El índice decide quién gana; la función de planificación dice qué unidades chocan.
- Escribir la tabla de transiciones sobre todos los estados. Una rama por defecto convierte un estado nuevo en un estado permitido.
- Después de trabajo físico informado, no revertir nunca. Añadir un estado que signifique «desconocido» y darle dos salidas documentadas, las dos observaciones.
- Hacer que el historial sea de solo adición y dejar que las correcciones sean filas nuevas, porque el hecho de que algo fuera incierto en su momento es información por sí mismo.
- Comprobar a qué está enganchada la política de retención. Una cascada de una tabla de vida corta hacia una de vida larga es un error de pérdida de datos con calendario.
Para dejar claro qué es esto: el trabajo de logística de centro de datos está detrás de un interruptor de despliegue apagado por defecto, con un ajuste por organización por encima, así que nada de ello se puede comprar hoy. Hay cuatro cosas sin demostrar. El evento de progreso en vivo de un traslado se ejercitó sin su bus de mensajes. Cancelar un traslado desde la pantalla de operaciones no se condujo en un navegador. Nunca se ha observado una retención que caducara de verdad, y no hay a propósito ninguna barrida que limpie una, porque una retención que se ordena sola mientras alguien está plantado delante del rack es exactamente la contradicción que el índice existe para evitar. Y esa misma comprobación de emplazamiento descrita más arriba no ha llegado a saltar nunca, porque no se sembró un segundo emplazamiento con el que hacerla saltar.
Del trabajo de centro de datos en Provibr. Por qué un índice único parcial es el único límite real a la ocupación está en el único límite real a la ocupación de un rack, y por qué las cinco preguntas de capacidad sobre un rack no se pueden sumar está en el espacio en rack no es capacidad.