Blog ·
El único límite real a la ocupación de un rack
Dos ingenieros eligen la misma unidad de rack a la vez. El formulario dice libre a los dos. Quien decide es un índice único parcial, y nada más puede hacerlo.
Por Provibr
Dos ingenieros están colocando máquinas en el mismo rack. Los dos abren el formulario, los dos miran la U nueve, los dos ven un slot libre, los dos pulsan guardar. Haga lo que haga el código de la aplicación entre esas dos peticiones, hay una ventana en la que ambos leen la misma respuesta, y ninguna cantidad de comprobaciones previas a la escritura la cierra.
Esta es la carrera más vieja del manual y tiene una sola respuesta correcta: decide la base de datos. Todo lo que hay por encima está para producir una negativa legible, no para evitar una colisión. Invertir ese orden es exactamente como se acaba con una regla de unicidad que se escapa en un día ajetreado, y en un centro de datos un escape significa alguien con una carretilla plantado delante de un rack que ya está lleno.
La frontera es un índice
La ocupación se guarda dos veces a propósito. Una asignación tiene una fila de cabecera, que es lo que alguien hizo: esta cara, desde esta unidad hasta aquella y, en el caso de una colocación, la máquina que está en ella. Y tiene una fila por slot ocupado: este rack, esta cara, esta U concreta.
La tabla de slots existe por una sola razón. Lleva un índice único parcial sobre el rack, la cara y el número de slot, limitado a las filas que no se han liberado. Ese índice es el único límite real a la ocupación en todo el sistema. Ninguna otra cosa puede serlo, porque un índice único no puede hacer un join: solo ve las columnas de la fila sobre la que se asienta, así que el rack y la cara tienen que estar desnormalizados en la fila del slot para que la restricción exista siquiera.
El precio es que liberar una asignación son dos updates dentro de una misma transacción, ya que la marca de tiempo de liberación vive tanto en la cabecera como en los slots. El retorno es que el invariante lo impone la base de datos y no el cuidado de quien escriba la próxima funcionalidad.
Lo medimos en vez de razonarlo. Dos conexiones reales a Postgres, una barrera mutua para que ambas estén dentro de su transacción antes de que ninguna confirme, ambas reclamando la U nueve de la cara frontal. Resultado: exactamente una asignación escrita, un error 23505 que nombra ese índice, y ninguna cabecera huérfana dejada atrás por el rollback. Esa es la frase que uno quiere poder escribir sobre una garantía de concurrencia, y no se puede escribir leyendo el código.
Para qué sirve realmente la comprobación previa a la escritura
Hay una función pura que planifica una asignación, y se niega de tres maneras, en este orden.
La cara no existe. Un bastidor abierto sin parte trasera no es un rack en cuya espalda se cuelgue algo, y por eso que un rack admita o no una cara frontal y una trasera es una propiedad del rack y no una suposición.
Fuera de límites. El rango entero tiene que caber entre la unidad inicial del rack y su final, ambos incluidos. Ni todos los racks empiezan en uno ni todos numeran hacia arriba, así que esto es aritmética contra la geometría de ese rack concreto y no contra el número cuarenta y dos.
Ocupado, y esta negativa lleva la lista de slots que chocan. Un mensaje que dice «lleno» devuelve a alguien al formulario sin idea de dónde mirar. Un mensaje que dice que la U nueve y la U diez de la cara frontal están cogidas lo lleva hasta la respuesta.
Ninguna de las tres es la frontera. Los dos ingenieros del primer párrafo pasan las tres. La función existe para que el caso corriente, que es una sola persona cada vez equivocándose, produzca una frase en lugar de un código de error de base de datos.
La profundidad desconocida se comporta como completa
Todo activo tiene una clase de profundidad: completa, media o desconocida. Un elemento de media profundidad ocupa solo la cara de la que cuelga. Uno de profundidad completa ocupa la frontal y la trasera.
Lo desconocido ocupa también la frontal y la trasera, y esa elección merece una defensa porque lo intuitivo sería lo contrario. Lo desconocido parece un dato que falta y que se rellenará más adelante, y mientras se espera parece inofensivo dejar libre el otro lado.
No es inofensivo. Una máquina cuya profundidad resulta ser completa no se puede instalar si alguien ha llenado la parte trasera mientras tanto, y la corrección ocurre con la máquina ya en el sitio. Bloquear la trasera cuesta un slot que quizá se habría tenido. No bloquearla cuesta una entrega. Lo desconocido no es la ausencia de una restricción, es una restricción que todavía no se puede nombrar, y su lado seguro es el conservador.
El mismo principio recorre el resto del modelo de capacidad: un peso desconocido, un consumo desconocido o un estado de asignación obsoleto se leen como desconocido o bloqueado, y nunca como capacidad libre.
Una colocación no caduca nunca
Las asignaciones vienen en cuatro clases. Una colocación es una máquina que está físicamente ahí. Un bloqueo es espacio retirado de servicio para cableado. Una retención por traslado y una reserva son las dos que guardan espacio para algo en movimiento o pedido. Solo la primera tiene pantalla hoy; las otras tres existen como datos para que la primitiva sea una cosa y no cuatro cosas parecidas.
Una comprobación en la base de datos impone que una colocación no tenga caducidad. Suena a regla pequeña. Lo que evita es una fila que deja de contar en silencio un martes mientras la máquina sigue atornillada, es decir, un rack en el que el software cree que hay un slot libre. Una reserva que caduca es una funcionalidad; una colocación que caduca es una mentira con temporizador.
Una segunda comprobación exige que una colocación tenga un activo, ya que las otras tres clases legítimamente no lo tienen, y un índice único parcial permite como mucho una colocación activa por activo. Sin ese índice, la misma máquina puede quedar registrada en dos racks a la vez. Eso no es un problema de calidad de datos que se note en un informe; es un problema que se nota cuando alguien está plantado delante del rack equivocado.
Conviene saber una cosa de los índices de esta forma: son globales y no están acotados por organización. Una máquina que ya lleva una fila activa choca con la restricción de unicidad y no con una comprobación de propiedad, incluso cuando el segundo intento viene de otra organización. Ese es el lado seguro, y significa además que probar el caso de dos organizaciones necesita un tercer activo, porque la restricción se dispara antes de que la lógica de propiedad llegue a ejecutarse.
La misma forma un nivel más arriba
Entregar una máquina a un cliente tiene una estructura idéntica, y necesita dos índices en lugar de uno, porque son dos invariantes que la gente suele enunciar como una sola frase.
Un servicio está en como mucho una máquina. Sin eso, un servicio puede apuntar a dos activos y su ficha de ubicación apunta en dos direcciones.
Una máquina se entrega a como mucho un servicio. Sin eso, la misma máquina se vende dos veces, y uno se entera cuando llaman dos clientes.
Ambos son índices únicos parciales sobre las filas cuya fecha de fin sigue vacía, que es la misma construcción que el índice de slots y por la misma razón: la comprobación en la aplicación produce la negativa legible, el índice decide quién gana.
Una regla no se pudo meter en la base de datos. Un vínculo tiene que apuntar a un servicio dedicado, y una restricción de comprobación no puede leer otra tabla. Esa vive en la función que escribe el vínculo, y merece la pena ser explícito sobre cuáles de las reglas propias las impone el esquema y cuáles las impone un camino de código, porque la segunda clase vale exactamente lo que valga el número de caminos de escritura que uno se acuerde de encauzar por él.
Los vínculos son históricos y no actuales. La fecha de fin es lo que deja la fila inactiva, y toda lectura de una ubicación física filtra por ella y por la organización tanto del vínculo como del activo. Al quitar esos filtros, un antiguo cliente lee el rack y la unidad del cliente que vino después. Eso se demostró en rojo antes de arreglarlo, que es la única manera de saber que un filtro está haciendo algo.
Lo que apuntaríamos
- Poner la unicidad donde está la fila. Si la restricción necesita un join, desnormalizar las columnas que necesita; un índice no puede hacer un join y un disparador no es una restricción.
- Materializar la unidad de contención. Una fila por slot ocupado, no un rango en una cabecera, porque un rango no puede ser único frente a otro rango.
- Demostrar la carrera con dos conexiones y una barrera. Razonar sobre niveles de aislamiento no es evidencia.
- Dejar que la comprobación previa sea un mensaje y no una defensa, y hacer que nombre los slots exactos que chocan.
- Hacer que lo desconocido se comporte como el caso caro, para que una medición ausente nunca se presente como espacio libre.
Para dejar claro dónde encaja 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 lo anterior se puede comprar hoy. Hay dos cosas sin demostrar: la medición de concurrencia se ejecutó contra asignaciones de bloqueo en una prueba directa contra la base de datos y no contra el camino de colocación en sí, y las clases de retención por traslado y de reserva todavía no tienen quien las escriba. La costumbre más amplia de dejar que la base de datos sea el árbitro aparece también en otros puntos de este producto, en cómo se reparten las direcciones de un pool y en cómo un rango de vmid es en realidad una frontera de permisos.
Del trabajo de centro de datos en Provibr. Por qué las cinco preguntas de capacidad sobre un rack no se pueden sumar está en el espacio en rack no es capacidad, y qué ocurre cuando un sistema externo afirma saber dónde está una máquina está en cuando NetBox es la fuente de la verdad.