Săriți la conținut

Blog ·

Două comenzi, o singură mașină

Un server dedicat se vinde exact o dată. Blocarea întregului grup de candidate ne-a costat în tăcere o rezervare, iar leacul a fost blocarea una câte una.

De Provibr

Un plan VPS vinde intenționat mai mult decât are și nimeni nu își pierde somnul. O mașină dedicată nu poate: există una singură, iar dacă două comenzi cred amândouă că au primit-o, într-o luni dimineața un client va auzi că lucrul plătit vineri aparține altcuiva.

Cerința încape într-o frază. Rezervă înainte de efectele secundare și eliberează capacitatea tranzacțional după o prăbușire sau o eroare. Construirea ei a produs două măsurători care au schimbat proiectul, iar una dintre ele ne-a costat o mașină care stătea acolo, liberă.

Arbitrul este un index

Regula potrivit căreia două comenzi nu pot ține niciodată aceeași mașină trăiește într-un index unic parțial pe identificatorul echipamentului, restrâns la rezervările în starea ținută. Nu în aplicație, nu într-un blocaj pe care îl ia codul, nu într-un comentariu.

Aceasta este intenționat aceeași formă ca indexul de ocupare al unităților de rack. Stratul de aplicație produce un refuz lizibil înaintea coliziunii, iar baza de date hotărăște cine câștigă cu adevărat. Doar una dintre cele două poate fi autoritatea, și trebuie să fie aceea pe care niciun drum de cod adăugat la anul nu o poate ocoli.

Ținută este singura stare care ține ceva, iar asta este scrisă într-un tabel alături de index și nu într-o coloană logică, fiindcă o valoare logică lângă stare este un al doilea adevăr, corect doar cât timp fiecare scriitor își aduce aminte de el. Un test compară tabelul cu indexul.

Există patru stări, iar două dintre ele formează intenționat o pereche: ținută, convertită, expirată, anulată. Simplificarea ispititoare este o singură eliberată pentru ultimele două, și ar lăsa fără răspuns întrebarea dacă cineva a anulat sau dacă pur și simplu a rămas așa, adică tocmai întrebarea care apare când un client sună despre o comandă care nu a mers mai departe. O măturare care scrie în tăcere aceeași stare ca un om șterge deosebirea dintre o hotărâre și un termen depășit.

Blocarea întregului grup a costat o rezervare

Prima versiune alegea mașinile candidate și le bloca într-o singură interogare cu FOR UPDATE SKIP LOCKED, sărind peste ce era deja luat. Se citește bine și este greșit, iar asta a arătat-o doar un test de concurență.

Opt comenzi simultane pe trei mașini potrivite ar trebui să dea trei rezervări. Au dat două. Prima tranzacție a apucat toate cele trei rânduri, a folosit unul și le-a ținut pe celelalte două până la confirmarea ei; toți ceilalți au trecut pe lângă toate trei și nu au găsit nimic. Grupul nu era epuizat, era luat ostatic.

Leacul este blocarea unei candidate pe rând: ia-o pe prima, iar dacă o are altcineva, treci la următoarea. Același test din nou: trei rezervări pe trei mașini diferite și cinci refuzuri oneste. Două comenzi pe o singură mașină dau o rezervare și un refuz.

Acel leac aduce propriul risc, anume lungimea buclei. La o sută de comenzi simultane, ultima tranzacție ar parcurge o sută de candidate înainte să câștige, iar o tranzacție lungă le ține pe toate celelalte pe loc. De aceea parcurgerea se oprește la douăzeci și cinci de candidate, iar peste acest prag „încercați din nou peste puțin timp” este răspunsul onest.

Observați că săritul și eșecul se numără separat. O candidată ținută de altcineva este sărită, iar aceea nu este o încercare, fiindcă nu a mers nimic prost. O candidată care a pierdut o cursă la inserare este o încercare, iar de acelea sunt cel mult cinci.

O încălcare a unicității otrăvește tranzacția

Aceasta este capcana în jurul căreia este construită bucla de reluare, iar este ușor să scrii cod care pare corect și cade la prima coliziune adevărată.

Într-o tranzacție PostgreSQL, încălcarea unei chei unice întrerupe tranzacția. Orice instrucțiune următoare este atunci refuzată cu altă eroare, „tranzacția curentă este întreruptă”, până când cineva revine. O buclă care prinde încălcarea și încearcă următoarea candidată nu funcționează deloc: inserarea celei de-a doua candidate eșuează dintr-un motiv care nu are nimic de-a face cu acea a doua candidată.

Fiecare încercare rulează de aceea în interiorul unui punct de salvare, care în constructorul nostru de interogări este pur și simplu o tranzacție imbricată. Revenire la punctul de salvare, tranzacția exterioară este iarăși sănătoasă, următoarea candidată.

Există un amănunt legat de felul în care este prinsă încălcarea. Constructorul nostru de interogări împachetează erorile driverului într-un tip propriu și o atârnă pe cea adevărată de cauză, deci verificarea unui cod la nivelul de sus nu vede nimic. Trebuie parcurs lanțul cauzelor și trebuie privit și numele constrângerii, fiindcă acest tabel are două constrângeri de unicitate, iar ele spun lucruri opuse: una spune că mașina este luată, cealaltă spune că această comandă are deja o rezervare, și iat-o.

Refuzul trebuie să spună de ce fel este

Alocatorul construiește lista candidatelor de două ori, iar diferența dintre cele două rezultate este un mesaj către un client.

Prima trecere este lista completă, cu un motiv pentru fiecare mașină care a căzut: sit greșit, producător greșit, nepregătită și așa mai departe. A doua trecere este ceea ce în acea clipă s-a putut într-adevăr bloca. Dacă lista completă a dat o câștigătoare, iar cea blocabilă nu, atunci era ceva de vânzare și cineva a ajuns cu o fracțiune de secundă mai devreme. Aceasta este altă propoziție decât „nu este nimic”: una înseamnă mai încercați, cealaltă înseamnă cumpărați mai mult fier. Merită butoane diferite.

Motivul înghețat al mașinii alese ajunge în rândul rezervării, fiindcă peste o lună fiecare echipament stă altfel și nu se mai poate reconstitui de ce alocatorul o luase tocmai pe aceea. Atunci rândul o spune singur.

Cheia de idempotență este cerută de două ori, iar acea a doua oară este încă un lucru pe care l-a găsit doar un test de concurență. Cinci cereri simultane cu aceeași cheie, pe patru mașini, au dat o rezervare și patru refuzuri, printre care unul de la o tranzacție care nu a putut bloca nimic și de aceea nu a ajuns niciodată la inserare și nici la ramura de reluare, în timp ce în acel moment exista pur și simplu o rezervare cu cheia ei. Întrebarea repetată chiar înainte de refuz rezolvă asta, și funcționează fiindcă la nivelul nostru de izolare fiecare instrucțiune ia un instantaneu proaspăt: un frate tocmai confirmat este deci vizibil.

Garanția care supraviețuiește tuturor acestora este îngustă și merită scrisă așa: cinci cereri cu aceeași cheie dau o rezervare și de patru ori „cineva a fost mai rapid”. Cei care pierd nu îl văd încă pe cel care câștigă, fiindcă în acea clipă el nu confirmase încă. Nu există o versiune în care lucrurile să stea altfel fără ca fiecare să îi aștepte pe ceilalți, iar așteptarea transformă fiecare minut aglomerat într-o coadă.

Rezervarea devine legătura

Când comanda se încheie, mașina trebuie să treacă de la rezervată la vândută. Implementarea evidentă eliberează rezervarea și apoi scrie legătura de serviciu, iar ea conține o fereastră de câteva milisecunde în care mașina este liberă. Într-o zi aglomerată, acea fereastră este găsită.

De aceea conversia scrie legătura și trece rezervarea la convertită într-o singură tranzacție, iar rândul rămâne ținut până când legătura există. În fiecare clipă a acelei tranzacții, o a doua comandă vede fie rezervarea, fie legătura, iar amândouă o exclud. Nu există clipă liberă fiindcă nicio instrucțiune nu creează una. Aceeași formă ca promovarea unei rezervări de rack la amplasare, cu un nivel mai sus.

Măturarea este o curățenie, nu un paznic

O rezervare expiră după cincisprezece minute în mod implicit, iar un lucrător mătură cele expirate în fiecare minut.

Acel număr nu sunt cele patru ore pe care le primește o rezervare de rack, iar deosebirea stă în ceea ce așteaptă fiecare. O rezervare de rack așteaptă un om cu un cărucior: cineva trebuie să meargă în sală, să pună șine, să mute cabluri. O rezervare de mașină așteaptă o cumpărare: ecran de plată, verificare antifraudă, confirmare. Între timp, cel mai scump stoc pe care îl are un găzduitor este nevandabil. Cincisprezece minute sunt darnice pentru o plată întreruptă pe care cineva o reia și destul de scurte încât o furtună de comenzi abandonate să nu golească raftul o jumătate de zi.

Important este unde se aplică termenul. El este verificat în interiorul conversiei, nu de măturare. Dacă măturarea ar fi singurul loc, o rezervare expirată între două treceri s-ar mai converti, iar un paznic care trece o dată pe minut nu păzește nimic. Măturarea pune administrația la punct și dă mașina înapoi stocului; aceea este muncă de curățenie.

Ea nu are, în plus, niciun comutator de funcționalitate, iar aceasta este o excepție voită. Există un comutator de lansare peste întregul modul al centrului de date, iar repetarea lui aici ar însemna că oprirea modulului lasă mașini rezervate pe veci pentru comenzi pe care nimeni nu le mai poate vedea. Un comutator al cărui efect secundar este oprirea unui termen nu este un comutator, ci o scurgere.

Ce am nota

  1. Puneți arbitrul în baza de date. Aplicația trebuie să producă refuzul lizibil; indexul trebuie să hotărască învingătorul.
  2. Blocați o candidată pe rând. Blocarea unei mulțimi ține rânduri pe care nu le veți folosi, iar simptomul este o vânzare pierdută și nu o eroare.
  3. Limitați parcurgerea. Blocarea pe candidată schimbă un grup ostatic pe o buclă lungă, iar acea buclă are nevoie și ea de o limită.
  4. O încălcare a unicității întrerupe tranzacția. Reluarea fără punct de salvare produce o eroare care numește altceva.
  5. Priviți numele constrângerii. Un tabel are adesea mai multe unicități, iar pentru utilizator ele înseamnă lucruri diferite.
  6. „Nu este nimic” și „cineva a fost mai rapid” sunt două mesaje. Construirea listei de candidate de două ori este felul în care le deosebiți.
  7. Promovați în loc să eliberați și să rescrieți. Orice succesiune care conține o clipă liberă va fi până la urmă găsită.
  8. Aplicați termenul la punctul de folosire. O măturare periodică este gospodărie, iar gospodăria nu este o regulă.

Pentru claritate despre ce este vorba: munca de logistică a centrului de date se află în spatele unui comutator de lansare oprit implicit, cu o setare per organizație deasupra, deci nimic din toate acestea nu se poate cumpăra astăzi. Două lucruri nu sunt dovedite. Măturarea expirărilor a fost măsurată ca funcție și nu ca lucrător în execuție. Iar o cerere brută către acțiunile de server cu comutatorul închis nu a fost încercată.

Din munca de platformă a Provibr. Aceeași promovare în loc de rescriere, aplicată unităților de rack, se află în reținerea devine amplasarea, iar de ce unitățile libere dintr-un rack nu înseamnă capacitate vandabilă se află în spațiul din rack nu înseamnă capacitate.

Începeți cu o gazdă și un agent

Creați un cont, instalați agentul cu o singură comandă și conectați primul dumneavoastră host Proxmox. Perioada de probă durează 14 zile și nu cere date de plată.