Blog ·
Due ordini, una macchina
Un server dedicato si vende esattamente una volta. Bloccare l’intero bacino di candidate è costato in silenzio una prenotazione, e la cura fu bloccarne una.
Di Provibr
Un piano VPS sovravende di proposito e nessuno ci perde il sonno. Una macchina dedicata non può: ce n’è una sola, e se due ordini credono entrambi di essersela aggiudicata, un lunedì mattina un cliente si sentirà dire che ciò che ha pagato venerdì appartiene a qualcun altro.
Il requisito sta in una frase. Prenotare prima degli effetti collaterali e restituire la capacità in modo transazionale dopo un arresto o un fallimento. Realizzarlo ha prodotto due misure che hanno cambiato il disegno, e una di esse ci è costata una macchina che stava lì, libera.
L’arbitro è un indice
La regola per cui due ordini non possono mai trattenere la stessa macchina vive in un indice univoco parziale sull’identificatore dell’apparato, ristretto alle prenotazioni nello stato trattenuta. Non nell’applicazione, non in un blocco che prende il codice, non in un commento.
È di proposito la stessa forma dell’indice di occupazione sulle unità di armadio. Lo strato applicativo produce un rifiuto leggibile prima dello scontro, e la banca dati decide chi vince davvero. Solo una delle due può fare testo, e deve essere quella che nessun cammino di codice aggiunto l’anno prossimo può aggirare.
Trattenuta è l’unico stato che trattiene qualcosa, e ciò è espresso in una tabella accanto all’indice e non in una colonna booleana, perché un booleano accanto allo stato è una seconda verità corretta solo finché ogni scrittore se ne ricorda. Un test confronta la tabella con l’indice.
Ci sono quattro stati e due di essi formano di proposito una coppia: trattenuta, convertita, scaduta, annullata. La semplificazione allettante è un unico liberata per le ultime due, e lascerebbe senza risposta la domanda se qualcuno l’abbia annullata o se sia semplicemente rimasta lì, che è proprio la domanda che arriva quando un cliente telefona per un ordine non andato in porto. Una scopa che scrive in silenzio lo stesso stato di una persona cancella la differenza fra una decisione e un tempo scaduto.
Bloccare l’intero bacino è costato una prenotazione
La prima versione selezionava le macchine candidate e le bloccava in una sola interrogazione con FOR UPDATE SKIP LOCKED, saltando ciò che era già preso. Si legge bene ed è sbagliato, e solo una prova di concorrenza l’ha mostrato.
Otto ordini simultanei su tre macchine idonee dovrebbero produrre tre prenotazioni. Ne hanno prodotte due. La prima transazione ha afferrato tutte e tre le righe, ne ha usata una e ha trattenuto le altre due fino alla sua conferma; tutti gli altri sono passati oltre tutte e tre e non hanno trovato nulla. Il bacino non era esaurito, era in ostaggio.
La cura è bloccare una candidata per volta: prendi la prima, e se la tiene qualcun altro, passa alla successiva. La stessa prova di nuovo: tre prenotazioni su tre macchine diverse e cinque rifiuti onesti. Due ordini su una sola macchina danno una prenotazione e un rifiuto.
Quella cura porta con sé un rischio proprio, cioè la lunghezza del ciclo. Con cento ordini simultanei l’ultima transazione percorrerebbe cento candidate prima di vincere, e una transazione lunga trattiene tutte le altre. Perciò la scansione si ferma a venticinque candidate, e oltre quella soglia «riprovi fra un istante» è la risposta onesta.
Si noti che saltare e fallire vengono contati a parte. Una candidata trattenuta da qualcun altro viene saltata, e quello non è un tentativo, perché non è andato storto nulla. Una candidata che ha perso una corsa all’inserimento è un tentativo, e di quelli ce ne sono al massimo cinque.
Una violazione di unicità avvelena la transazione
Questa è la trappola attorno alla quale è costruito il ciclo di ripetizione, ed è facile scrivere codice che sembra corretto e crolla al primo scontro vero.
Dentro una transazione PostgreSQL, una violazione di chiave univoca interrompe la transazione. Ogni istruzione successiva viene allora rifiutata con un altro errore, «la transazione in corso è interrotta», finché qualcuno non torna indietro. Un ciclo che cattura la violazione e prova la candidata successiva non funziona affatto: l’inserimento della seconda candidata fallisce per una ragione che con quella seconda candidata non c’entra nulla.
Ogni tentativo gira perciò dentro un punto di salvataggio, che nel nostro costruttore di interrogazioni è semplicemente una transazione annidata. Tornare al punto di salvataggio, la transazione esterna è di nuovo sana, candidata successiva.
C’è un dettaglio collegato su come la violazione viene catturata. Il nostro costruttore di interrogazioni avvolge gli errori del driver in un tipo proprio e appende quello vero alla causa, quindi controllare un codice al livello superiore non vede nulla. Bisogna percorrere la catena delle cause, e bisogna guardare anche il nome del vincolo, perché questa tabella ha due vincoli di unicità e dicono cose opposte: uno dice che la macchina è presa, l’altro dice che quest’ordine ha già una prenotazione ed eccola.
Il rifiuto deve dire di che specie è
L’assegnatore costruisce due volte l’elenco delle candidate, e la differenza fra i due risultati è un messaggio per un cliente.
Il primo passaggio è l’elenco completo, con una ragione per ogni macchina scartata: sede sbagliata, produttore sbagliato, non pronta e così via. Il secondo passaggio è ciò che in quell’istante si è potuto davvero bloccare. Se l’elenco completo ha dato una vincitrice e quello bloccabile no, allora c’era qualcosa da vendere e qualcuno è arrivato una frazione di secondo prima. Questa è una frase diversa da «non c’è nulla»: una significa riprovi fra poco, l’altra significa compri più ferro. Meritano pulsanti diversi.
La ragione congelata della macchina scelta finisce nella riga della prenotazione, perché un mese dopo ogni apparato sta in un altro stato e non è più ricostruibile perché l’assegnatore avesse preso proprio quella. Allora la riga lo dice da sé.
La chiave di idempotenza viene chiesta due volte, e quella seconda volta è ancora qualcosa che solo una prova di concorrenza ha trovato. Cinque richieste simultanee con la stessa chiave, su quattro macchine, hanno prodotto una prenotazione e quattro rifiuti, fra cui uno da una transazione che non è riuscita a bloccare nulla e perciò non è mai arrivata all’inserimento né al ramo di ripetizione, mentre in quel momento una prenotazione con la sua chiave semplicemente esisteva. Chiedere di nuovo subito prima del rifiuto risolve la cosa, e funziona perché al nostro livello di isolamento ogni istruzione prende un’istantanea fresca: un vicino appena confermato è quindi visibile.
La garanzia che sopravvive a tutto ciò è stretta e merita di essere scritta così: cinque richieste con la stessa chiave danno una prenotazione e quattro volte «qualcuno è stato più veloce». Chi perde non vede ancora chi vince, perché in quell’istante non aveva ancora confermato. Non esiste una versione in cui vada diversamente senza aspettarsi a vicenda, e aspettare trasforma ogni minuto affollato in una coda.
La prenotazione diventa il legame
Quando l’ordine si conclude, la macchina deve passare da prenotata a venduta. La realizzazione ovvia libera la prenotazione e poi scrive il legame di servizio, e contiene una finestra di pochi millisecondi in cui la macchina è libera. In una giornata affollata quella finestra viene trovata.
Perciò la conversione scrive il legame e porta la prenotazione a convertita in una sola transazione, e la riga resta trattenuta finché il legame non esiste. In ogni istante di quella transazione un secondo ordine vede o la prenotazione o il legame, ed entrambi lo escludono. Non c’è alcun istante libero perché nessuna istruzione ne crea uno. La stessa forma della promozione di una prenotazione di armadio a posizione, un piano più su.
La scopa è una pulizia e non una guardia
Una prenotazione scade dopo quindici minuti per impostazione predefinita, e un lavoratore spazza via quelle scadute ogni minuto.
Quel numero non sono le quattro ore che riceve una prenotazione di armadio, e la differenza sta in che cosa aspettano le due. Una prenotazione di armadio aspetta una persona con un carrello: qualcuno deve andare in sala, montare guide, spostare cavi. Una prenotazione di macchina aspetta un acquisto: schermata di pagamento, controllo antifrode, conferma. Nel frattempo la scorta più costosa che un fornitore possiede è invendibile. Quindici minuti sono generosi per un pagamento interrotto che qualcuno riprende, e abbastanza brevi perché una tempesta di ordini abbandonati non lasci lo scaffale vuoto per mezza giornata.
Ciò che conta è dove viene applicata la scadenza. Viene controllata dentro la conversione, non dalla scopa. Se la scopa fosse l’unico posto, una prenotazione scaduta fra due passaggi si convertirebbe ancora, e una guardia che passa una volta al minuto non sorveglia nulla. La scopa rimette in ordine l’amministrazione e restituisce la macchina alla scorta; quello è lavoro di pulizia.
Inoltre non ha alcun interruttore di funzionalità, ed è un’eccezione voluta. C’è un interruttore di rilascio sull’intero modulo del centro dati, e ripeterlo qui vorrebbe dire che spegnere il modulo lascia macchine prenotate per sempre a ordini che nessuno può più vedere. Un interruttore il cui effetto collaterale è disattivare una scadenza non è un interruttore, è una falla.
Cosa metteremmo per iscritto
- Mettete l’arbitro nella banca dati. L’applicazione deve produrre il rifiuto leggibile; l’indice deve stabilire il vincitore.
- Bloccate una candidata per volta. Bloccare un insieme trattiene righe che non userete, e il sintomo è una vendita persa e non un errore.
- Limitate la scansione. Il blocco per candidata scambia un bacino in ostaggio con un ciclo lungo, e anche quel ciclo ha bisogno di un limite.
- Una violazione di unicità interrompe la transazione. Riprovare senza punto di salvataggio produce un errore che nomina la cosa sbagliata.
- Guardate il nome del vincolo. Una tabella ha spesso più unicità, e per l’utente significano cose diverse.
- «Non c’è nulla» e «qualcuno è stato più veloce» sono due messaggi. Costruire due volte l’elenco delle candidate è il modo di distinguerli.
- Promuovete invece di liberare e riscrivere. Qualunque sequenza contenga un istante libero prima o poi viene trovata.
- Applicate la scadenza nel punto d’uso. Una scopa periodica è governo della casa, e il governo della casa non è una regola.
Per chiarezza su cosa sia tutto questo: il lavoro di logistica del centro dati sta dietro un interruttore di rilascio spento per impostazione predefinita, con una regolazione per organizzazione sopra, quindi oggi nulla di ciò è acquistabile. Due cose non sono dimostrate. La scopa delle scadenze è stata misurata come funzione e non come lavoratore in esecuzione. E non è stata tentata una richiesta grezza verso le azioni del server con l’interruttore chiuso.
Dal lavoro di piattaforma di Provibr. La stessa promozione invece della riscrittura, applicata alle unità di armadio, è spiegata in la prenotazione diventa la posizione, e perché le unità libere in un armadio non sono capacità vendibile è spiegato in lo spazio in rack non è capacità.