Naar de inhoud

Blog ·

De enige echte grens op rackbezetting

Twee technici kiezen tegelijk dezelfde rack-unit. Het formulier zegt tegen allebei: vrij. Wat beslist is een partiële unieke index, en niets anders kan dat.

Door Provibr

Twee technici plaatsen machines in hetzelfde rack. Allebei openen ze het formulier, allebei kijken ze naar U negen, allebei zien ze een vrij slot, allebei drukken ze op opslaan. Wat je applicatiecode tussen die twee verzoeken ook doet, er is een venster waarin ze allebei hetzelfde antwoord lezen, en geen enkele hoeveelheid controle vóór het schrijven sluit dat venster.

Dit is de oudste race uit het boekje en hij heeft één juist antwoord: de database beslist. Alles daarboven staat er om een leesbare weigering te maken, niet om een botsing te voorkomen. Die volgorde omdraaien is precies hoe je eindigt met een uniciteitsregel die op een drukke dag lekt, en in een datacenter betekent een lek dat er iemand met een steekwagen voor een rack staat dat al vol is.

De grens is één index

Bezetting wordt met opzet twee keer opgeslagen. Een claim heeft een kopregel, en dat is wat iemand gemaakt heeft: deze zijde, van deze unit tot die unit, en bij een plaatsing de machine die erin staat. En hij heeft één regel per bezet slot: dit rack, deze zijde, deze ene U.

De slottabel bestaat om precies één reden. Hij draagt een partiële unieke index op het rack, de zijde en het slotnummer, beperkt tot regels die niet vrijgegeven zijn. Die index is de enige echte grens op bezetting in het hele systeem. Iets anders kan het niet zijn, want een unieke index kan niet joinen: hij ziet alleen de kolommen van de regel waar hij op zit, dus het rack en de zijde moeten op de slotregel gedenormaliseerd worden om de constraint überhaupt te laten bestaan.

De prijs is dat een claim vrijgeven twee updates binnen één transactie kost, want het vrijgavetijdstip staat op zowel de kopregel als de slots. De opbrengst is dat de invariant door de database wordt afgedwongen en niet door de zorgvuldigheid van wie de volgende functie schrijft.

We hebben dit gemeten en er niet over geredeneerd. Twee echte Postgres-verbindingen, een wederzijdse barrière zodat allebei binnen hun transactie zijn voordat een van beide commit, allebei claimend op voorzijde U negen. Uitkomst: precies één claim geschreven, één fout 23505 die die index noemt, en geen verweesde kopregel achtergelaten door de rollback. Dat is de zin die je over een gelijktijdigheidsgarantie wilt kunnen schrijven, en je kunt hem niet schrijven op grond van het lezen van de code.

Waar de controle vóór het schrijven werkelijk voor is

Er is een zuivere functie die een claim plant, en hij weigert op drie manieren, in deze volgorde.

De zijde bestaat niet. Een open frame zonder achterkant is geen rack waar je aan de achterzijde iets in hangt, en daarom is het een eigenschap van het rack of het een voorzijde en een achterzijde heeft, en geen aanname.

Buiten de grenzen. Het hele bereik moet tussen de begineenheid van het rack en het einde ervan passen, grenzen inbegrepen. Racks beginnen niet allemaal bij één en nummeren niet allemaal omhoog, dus dit is rekenwerk tegen de geometrie van dat specifieke rack en niet tegen het getal tweeënveertig.

Bezet, en deze weigering draagt de lijst met slots die botsen. Een melding die "vol" zegt stuurt iemand terug naar het formulier zonder enig idee waar hij moet kijken. Een melding die zegt dat voorzijde U negen en U tien bezet zijn stuurt hem naar het antwoord.

Geen van deze drie is de grens. Beide technici uit de openingsalinea komen er alle drie doorheen. De functie bestaat zodat het gewone geval, namelijk één persoon tegelijk die zich vergist, een zin oplevert in plaats van een foutcode uit de database.

Onbekende diepte gedraagt zich als vol

Elke machine heeft een diepteklasse: vol, half, of onbekend. Een item van halve diepte claimt alleen de zijde waar het aan hangt. Een item van volle diepte claimt voorzijde en achterzijde.

Onbekend claimt eveneens voorzijde en achterzijde, en die keuze is het verdedigen waard, want de intuïtieve standaard is de omgekeerde. Onbekend voelt als ontbrekende gegevens die je later invult, en zolang je wacht lijkt het ongevaarlijk om de andere kant vrij te laten.

Ongevaarlijk is het niet. Een machine die volle diepte blijkt te hebben kan niet geplaatst worden als iemand de achterzijde intussen heeft volgehangen, en de correctie gebeurt met de machine al ter plaatse. De achterzijde blokkeren kost je een slot dat je misschien had gehad. Hem niet blokkeren kost je een levering. Onbekend is niet de afwezigheid van een beperking, het is een beperking die je nog niet kunt benoemen, en de veilige kant ervan is de behoudende.

Hetzelfde beginsel loopt door de rest van het capaciteitsmodel: een onbekend gewicht, een onbekende vermogensopname of een verouderde claimtoestand leest als onbekend of geblokkeerd, en nooit als vrije capaciteit.

Een plaatsing verloopt nooit

Claims komen in vier soorten. Een plaatsing is een machine die er fysiek staat. Een blokkering is ruimte die uit dienst genomen is voor bekabeling. Een verhuisreservering en een reservering zijn de twee die ruimte vasthouden voor iets dat onderweg of besteld is. Alleen de eerste heeft vandaag een scherm; de andere drie bestaan als gegevens zodat het primitief één ding is en niet vier soortgelijke dingen.

Een check-constraint in de database dwingt af dat een plaatsing geen vervaldatum heeft. Het klinkt als een kleine regel. Wat hij voorkomt is een regel die op een dinsdag stilletjes ophoudt mee te tellen terwijl de machine er nog in vastgeschroefd zit, oftewel een rack waarvan de software gelooft dat er een vrij slot in zit. Een verlopende reservering is een functie; een verlopende plaatsing is een leugen met een tijdklok erop.

Een tweede check eist dat een plaatsing een machine heeft, want de andere drie soorten hebben er terecht geen, en een partiële unieke index laat hoogstens één actieve plaatsing per machine toe. Zonder die index kan dezelfde machine in twee racks tegelijk vastgelegd worden. Dat is geen gegevenskwaliteitsprobleem dat je in een rapport opmerkt; het is een probleem dat je opmerkt wanneer iemand bij het verkeerde rack staat.

Iets om te weten over indexen van deze vorm: ze zijn globaal en niet per organisatie afgebakend. Een machine die al een actieve regel draagt botst met de unieke constraint en niet met een rechtencontrole, ook wanneer de tweede poging van een andere organisatie komt. Dat is de veilige kant, en het betekent ook dat het toetsen van het geval met twee organisaties een derde machine nodig heeft, want de constraint gaat af voordat de eigendomslogica ooit draait.

Dezelfde vorm een niveau hoger

Een machine aan een klant uitgeven heeft dezelfde structuur, en het vraagt twee indexen in plaats van één, want het zijn twee invarianten die mensen gewoonlijk als één zin uitspreken.

Eén dienst staat op hoogstens één machine. Zonder dat kan een dienst naar twee machines wijzen en wijst zijn locatiekaart twee kanten op.

Eén machine is aan hoogstens één dienst uitgegeven. Zonder dat wordt dezelfde machine twee keer verkocht, en kom je erachter wanneer er twee klanten bellen.

Allebei zijn het partiële unieke indexen op regels waarvan de einddatum nog leeg is, wat dezelfde constructie is als de slotindex en om dezelfde reden: de controle in de applicatie levert de leesbare weigering, de index beslist wie wint.

Eén regel kon niet in de database. Een koppeling moet naar een dedicated dienst wijzen, en een check-constraint kan geen andere tabel lezen. Die regel woont in de functie die de koppeling schrijft, en het is het waard uitdrukkelijk te zijn over welke van je regels door het schema worden afgedwongen en welke door een codepad, want de tweede soort is niet beter dan het aantal schrijfwegen dat je eraan denkt erlangs te leiden.

Koppelingen zijn historisch en niet actueel. De einddatum is wat de regel inactief maakt, en elke lezing van een fysieke locatie filtert daarop en op de organisatie van zowel de koppeling als de machine. Laat die filters weg en een voormalige klant leest het rack en de unit van de klant die na hem kwam. Dat is rood bewezen voordat het gerepareerd werd, wat de enige manier is om te weten dat een filter werk doet.

Wat we zouden opschrijven

  1. Leg de uniciteit waar de regel is. Heeft de constraint een join nodig, denormaliseer dan de kolommen die hij nodig heeft; een index kan niet joinen en een trigger is geen constraint.
  2. Materialiseer de eenheid waarover gestreden wordt. Eén regel per bezet slot, geen bereik op een kopregel, want een bereik kan niet uniek zijn tegen een ander bereik.
  3. Bewijs de race met twee verbindingen en een barrière. Redeneren over isolatieniveaus is geen bewijs.
  4. Laat de voorcontrole een melding zijn en geen verdediging, en laat hem de precieze slots benoemen die botsen.
  5. Laat onbekend zich als het dure geval gedragen, zodat een ontbrekende meting zich nooit als vrije ruimte voordoet.

Voor de duidelijkheid over waar dit staat: het werk aan datacenterlogistiek zit achter een uitrolvlag die standaard uit staat, met daarbovenop een instelling per organisatie, dus niets van het bovenstaande is vandaag te koop. Twee dingen zijn onbewezen: de gelijktijdigheidsmeting is in een rechtstreekse databasetoets tegen blokkeringsclaims gedraaid en niet tegen het plaatsingspad zelf, en de soorten verhuisreservering en reservering hebben nog geen schrijver. De bredere gewoonte om de database de scheidsrechter te laten zijn komt ook elders in dit product terug, in hoe adressen uit een pool worden uitgegeven en in hoe een vmid-bereik in werkelijkheid een rechtengrens is.

Uit het datacenterwerk aan Provibr. Waarom de vijf capaciteitsvragen boven een rack niet bij elkaar opgeteld mogen worden staat in rackruimte is geen capaciteit, en wat er gebeurt wanneer een extern systeem beweert te weten waar een machine zich bevindt, staat in als NetBox de bron van waarheid is.

Begin met één host en één agent

Maak een account aan, installeer de agent met één commando en koppel je eerste Proxmox-host. De proefperiode loopt 14 dagen en vraagt geen betaalgegevens.