Blog ·
Il figlio risveglia il genitore
Un flusso che ne chiama un altro può interrogare o essere avvisato: li abbiamo fatti entrambi. L’orologio di ripiego è un giorno, non cinque minuti: è misurato.
Di Provibr
Un flusso può ora chiamarne un altro. L’esecuzione chiamante si ferma, quella chiamata parte, e quando l’esecuzione chiamata finisce la chiamante continua sul filo ok o sul filo error.
L’intera questione di progetto in quella frase è la parola quando. Qualcosa deve accorgersi che il figlio ha finito. Ci sono due modi di organizzarlo e siamo finiti con entrambi, in un ordine preciso, e il secondo contiene un numero su cui c’è stato da discutere.
Il genitore non aspetta, gli viene detto
Il modo naturale di scrivere questa cosa è un genitore che aspetta. Il genitore sa quale esecuzione sta aspettando, quindi può chiedere. Interrogare ogni dieci secondi, vedere se il figlio abbia finito, continuare quando l’ha fatto.
È il verso sbagliato, e la ragione è che il figlio già lo sa. È lui a passare nello stato di concluso. Far chiedere il genitore significa che l’informazione viaggia dalla parte che ce l’ha alla parte che ne ha bisogno attraverso una domanda ripetuta, e la frequenza di quella domanda è la latenza dell’intera funzionalità.
Il figlio annuncia dunque. Quando un’esecuzione figlia raggiunge uno stato terminale, porta ad adesso l’orario di risveglio della riga di attesa che punta a essa, e il normale worker della coda la raccoglie entro il proprio tick successivo. È esattamente lo stesso meccanismo che usa un’approvazione umana: prendere una decisione porta l’orario di risveglio ad adesso. Una sola colonna guida la coda, e non c’è di proposito una seconda via verso l’esecutore.
Quell’annuncio è una sola istruzione: aggiorna la riga di attesa in cui l’identificatore dell’esecuzione figlia coincide e lo stato è ancora in attesa.
Perché quell’identificatore è una colonna e non parte del blocco di stato
La continuazione di una visita in pausa è conservata come JSON, e contiene tutto ciò di cui la visita ha bisogno per riprendere: la pila, i nodi visitati, le variabili, i contatori. Sarebbe stato coerente metterci dentro anche l’identificatore del figlio.
È invece una colonna, e la ragione è la direzione. Tutto il resto di quel blocco viene letto dalla visita, dall’interno. Questo unico valore viene letto dall’esterno, da un’esecuzione figlia che vuole trovare la riga che punta a essa. Nel blocco quella è una scansione su ogni attesa aperta del sistema, e l’indice che servirebbe per renderla economica è comunque questa colonna.
La chiave esterna è impostata su SET NULL invece che su CASCADE. Se l’esecuzione figlia viene ripulita, l’attesa del genitore non deve sparire con essa, perché allora un genitore resta sospeso senza nulla che lo risvegli mai. Perdere il riferimento non è fatale; perdere la riga sì.
L’orologio di ripiego è un giorno, e il numero è l’argomento
L’annuncio è il meccanismo. L’orologio è la rete di sicurezza per il caso in cui l’annuncio non arrivi mai: un contenitore caduto a metà della conclusione del figlio, una riga toccata a mano.
Il valore ovvio per una rete di sicurezza è qualcosa di piccolo. Cinque minuti danno una sensazione di responsabilità.
È sbagliato, e la ragione è quello che a un figlio è permesso fare. Un’esecuzione figlia può contenere un’approvazione umana, e un’approvazione può avere una scadenza a trenta giorni di distanza. Con un orologio da cinque minuti, il genitore di quel figlio si risveglia migliaia di volte in quei trenta giorni per scoprire che non è cambiato nulla, e ognuno di quei risvegli è una richiesta HTTP dal gateway al contenitore web.
Ventiquattro ore costano al massimo trenta tick inutili lungo la più lunga attesa che il prodotto consenta, e limitano a un giorno il danno di un annuncio davvero mancato. È questo lo scambio, enunciato nelle unità che contano: quante richieste costa la rete di sicurezza quando non c’è nulla che non va, contro quanto a lungo un guasto vero resta invisibile.
La forma generale: un intervallo di ripetizione non è «il più corto che ci possiamo permettere». È una funzione della più lunga attesa legittima, e se non ci si è chiesti quale sia, l’intervallo è una supposizione.
Un orologio che non si può accorciare è un orologio che non si può dimostrare
Una rete di sicurezza scatta solo quando il meccanismo sotto di essa è venuto meno. Con l’intervallo fissato a un giorno, dimostrare che funziona richiede un giorno, quindi il turno che l’ha costruita ha consegnato quel ramo come non dimostrato, dichiarandolo onestamente.
La tentazione è dimostrarlo a mano: entrare nel database, spostare nel passato l’orario di risveglio, guardare l’esecuzione riprendere. Questo dimostra la ripresa. Non dimostra l’orologio, perché l’orologio è precisamente la cosa che si è sostituita.
L’intervallo legge quindi una variabile d’ambiente, e l’aggancio vive nel sorgente accanto alla costante invece che dentro un file di test. Lo usa uno script end to end che lo imposta a pochi secondi, lascia che la rete di sicurezza scatti davvero e guarda un genitore il cui figlio non ha annunciato nulla riprendersi da sé.
Due proprietà rendono sicuro spedire quell’aggancio. Può soltanto rendere l’intervallo più corto: il valore è limitato dalla costante di produzione, così un ambiente configurato male non può mai far attendere un genitore più di un giorno. E tutto ciò che non sia un numero positivo ripiega sul valore di produzione.
La seconda non è codice difensivo fine a se stesso. Copiare il nostro file d’ambiente di esempio dà la variabile presente e vuota, e una stringa vuota non è indefinita. Convertirla in numero dà zero. Senza il setaccio, un file di configurazione copiato porterebbe l’intervallo a zero e metterebbe ogni genitore con un figlio in sospeso in un ciclo di risvegli contro il contenitore web. La distinzione fra stringa vuota e indefinito ci ha già colpiti in passato, in un punto in cui è costata un 404 a una rotta di download.
Due limiti, ciascuno imposto due volte
Un flusso che chiama un flusso solleva la questione di un flusso che chiama se stesso. I limiti sono due, la ricorsione e la profondità, e ciascuno è imposto in due posti.
Staticamente, quando un flusso viene salvato: il grafo viene percorso, i flussi che chiama vengono risolti, e un ciclo o una catena troppo profonda vengono rifiutati con la catena nominata nell’errore. Dinamicamente, durante un’esecuzione: di nuovo gli stessi due limiti, con codici di rifiuto propri.
Servono entrambi perché i flussi vengono modificati in modo indipendente. Due flussi che presi singolarmente vanno bene possono diventare un ciclo nel momento in cui qualcuno aggiunge una chiamata al secondo, e nulla riconvalida il primo. Il controllo statico è ciò che dà un messaggio d’errore utile nel momento in cui qualcuno può ancora rimediare; il controllo dinamico è ciò che tiene davvero.
La profondità è una colonna sull’esecuzione invece di qualcosa calcolato risalendo i genitori. Va contro la linea che teniamo di solito, per cui tutto ciò che è derivabile andrebbe derivato. Qui non regge, perché il riferimento al genitore è impostato su SET NULL: una volta ripulita un’esecuzione genitrice, la catena non si può ripercorrere all’indietro e la profondità di un figlio in esecuzione diventerebbe inconoscibile esattamente nel momento in cui conta.
Che cosa ha misurato il turno successivo
I rami che questo progetto ha lasciato aperti sono stati percorsi in un turno successivo, e due di essi sono quelli interessanti.
Un’esecuzione figlia che sparisce del tutto: l’attesa conserva la propria riga e perde il proprio riferimento, l’orologio di ripiego risveglia il genitore, e la ripresa non trova alcun figlio e fa fallire quel nodo con una ragione precisa invece di restare appesa. Un grafo che non si riesce proprio a leggere produce una ragione diversa, così le due situazioni sono distinguibili nel registro dell’esecuzione.
Un sottoflusso dentro un ciclo, nelle due forme che si comportano in modo diverso: un figlio che finisce dentro la richiesta d’origine, e un figlio che sospende se stesso. Il primo non tocca proprio mai la coda. Il secondo è l’intera funzionalità.
Che cosa annoteremmo
- La parte che sa dovrebbe dirlo, non essere interrogata. L’interrogazione ripetuta trasforma la sua latenza in un valore di configurazione.
- Riusi il percorso di risveglio che ha già. Per la coda, una decisione e un figlio concluso sono lo stesso evento.
- Un valore letto dall’esterno del blocco di stato appartiene a una colonna. A deciderlo è la direzione, non la coerenza.
- Imposti su
SET NULL, non suCASCADE, quando la riga che verrebbe cancellata è l’unico modo che qualcuno ha di essere risvegliato. - L’intervallo di una rete di sicurezza è una funzione della più lunga attesa legittima, non di quanto reattivi si vorrebbe sembrare.
- Enunci lo scambio in richieste e in ore. Trenta tick sprecati contro un giorno di invisibilità è una decisione; «cinque minuti» è un riflesso.
- Una costante che non si può variare è un ramo che non si può dimostrare. L’aggancio va accanto alla costante.
- Lasci che l’aggancio possa solo rendere le cose più sicure, e lo limiti al valore di produzione.
- Una variabile d’ambiente vuota non è indefinita, e convertirla in numero dà zero.
- Controlli un limite dove si può ancora rimediare e dove deve tenere. I grafi modificati in modo indipendente hanno bisogno di entrambi.
Per chiarezza su che cosa sia: la verifica di merge ha eseguito 16 passi su 16 senza una riga di fallimento, la suite web è stata 4583 verdi, packages, gateway e tools insieme 7286 verdi, la visita 75 verdi, il controllo statico dei sottoflussi 13 verdi, la corsa end to end contro il database di sviluppo 58 verdi, la misura dei destinatari 4 verdi, una prova nel browser con un vero worker di gateway 31 verdi in cui una pausa di novanta secondi è ripresa dopo novantacinque secondi, la prova dell’editor 9 verdi e la scansione di accessibilità non ha trovato nulla su sei pagine. Non dimostrati all’epoca: l’orologio di ripiego, un sottoflusso dentro un ciclo, un’esecuzione figlia sparita e il ramo di esaurimento dei tentativi. Quei quattro sono stati percorsi nel turno seguente.
Dal lavoro di piattaforma su Provibr. Come la visita è diventata qualcosa che si può scrivere in una riga si trova in ogni chiamata ricorsiva era in posizione di coda, e il turno che ha finalmente raggiunto i rami che questo ha lasciato aperti si trova in verde perché non poteva arrivarci.