Salta al contenuto

Blog ·

Una pausa che teneva aperto l’ordine

Un’attesa dentro un flusso teneva aperta la richiesta d’ordine per tutta la sua durata. La correzione è una soglia e non una casella, e il numero è misurato.

Di Provibr

Un flusso può contenere un’attesa. Si effettua il provisioning di una macchina, si concede un momento all’agente, poi si invia il comando successivo. Fino a questa settimana quell’attesa era una sospensione dentro la richiesta che aveva avviato il flusso, il che significava che un cliente che effettuava un ordine restava a guardare un indicatore di caricamento per tutta la durata della pausa.

Il vecchio limite superiore era di cinque minuti. Qualcuno poteva quindi costruire un flusso dall’aria del tutto ragionevole il cui effetto era un modulo d’ordine da cinque minuti.

Ora che un’esecuzione può fermarsi ed essere continuata da un worker, la pausa non deve più avvenire dentro la richiesta. Il limite superiore è passato a trenta giorni. La domanda interessante non è però il tetto. È dove passa la linea fra una pausa che la richiesta può assorbire e una pausa che dovrebbe sospendere l’esecuzione.

La soglia è misurata, non scelta

Trenta secondi.

La domanda che ci siamo posti non era «quanto può durare una pausa», che non ha risposta, ma «quanta pausa tollera già la richiesta dentro cui gira questo flusso», che invece ce l’ha. Quella richiesta è l’ordine.

Misurata contro ciò che al suo interno accade già: un nodo di azione attende un comando per sessanta secondi in modo predefinito e può essere configurato fino a trecento, e una notifica con posta elettronica costa circa mezzo secondo per destinatario. Una pausa che aggiunge a tutto ciò un altro mezzo minuto è difendibile. Una pausa che aggiunge un minuto intero non lo è.

C’è qui una trappola in cui siamo entrati di proposito invece di aggirarla. La scelta comoda sarebbe stata fissare la soglia al vecchio limite superiore di trecento secondi, perché allora ogni grafo esistente si comporta esattamente come prima e nessuno resta sorpreso. Ed è anche sbagliata: conserva l’intera classe di problema che ha motivato il cambiamento. I grafi con una pausa fra trentuno e trecento secondi ora si comportano davvero in modo diverso. Il loro ordine si conclude prima e il resto del flusso segue dopo. Quello è il miglioramento, non il suo costo.

Il tetto di trenta giorni è una regola di prodotto e non un limite tecnico. Il meccanismo di attesa terrebbe volentieri un’esecuzione per un anno. Quello che non regge un anno è un’organizzazione in cui nessuno ricorda perché un’esecuzione sia aperta.

Perché è una soglia e non una casella di spunta

L’alternativa ovvia è un interruttore «attendi in modo durevole» sul nodo. Non lo abbiamo costruito, e la ragione è una domanda a cui non si può rispondere.

Se ci fosse una casella di spunta, quando la si vorrebbe disattivata? La risposta onesta è mai. L’attesa durevole è un dettaglio di implementazione: chi scrive un flusso sta esprimendo «fermati qui per questo tempo» e non ha alcuna opinione sul fatto che ciò costi una riga di database. Un’impostazione il cui valore corretto è sempre lo stesso non è un’impostazione, è una cosa che ci si è dimenticati di decidere.

Chi scrive digita quindi un numero di secondi, e a decidere è la soglia. Sotto la linea la visita si limita a dormire, dentro la richiesta, senza costare nulla. Sopra, l’esecuzione scrive la propria continuazione nella coda con un orario di risveglio e il chiamante ritorna subito.

Il passo che alla fine compare nel registro dell’esecuzione porta un contrassegno di durevolezza e il tempo trascorso davvero misurato, non quello richiesto, perché non sono lo stesso numero e la differenza è l’unico modo di vedere che la coda è in ritardo.

Flussi che chiamano flussi, e due limiti controllati due volte ciascuno

La seconda metà di questo lavoro è un nodo di sottoflusso: un flusso che esegue un altro flusso e lo attende.

Questo crea due modi di costruire qualcosa che non termina mai, e si somigliano abbastanza da rendere allettante controllarli insieme. Non sono la stessa cosa. Un ciclo è infinito. Una catena troppo profonda è finita ma troppo lunga. Se si risponde a entrambi con una sola funzione, un ciclo viene segnalato come «troppo profondo», e chi scrive si mette a cercare una catena lunga mentre la sua è lunga due flussi.

Le domande sono quindi due, e ciascuna viene posta due volte.

Vengono poste una volta quando un flusso viene salvato, staticamente, contro gli altri flussi dell’organizzazione. Quel controllo deve esserci perché è l’unico punto in cui è possibile un buon messaggio d’errore: può dire il nome del flusso che chiude l’anello.

Vengono poste di nuovo durante l’esecuzione. Quel controllo deve esserci perché i flussi vengono modificati in modo indipendente. Il flusso A viene salvato e va bene. Qualcuno modifica poi il flusso C, che ora punta di nuovo ad A. Nulla ha riconvalidato A. Il controllo statico è una cortesia verso chi scrive; quello dinamico è la garanzia vera.

Vale la pena nominare due dettagli di implementazione. Chiedere «questo flusso può raggiungere se stesso in almeno un passo» risponde in una sola interrogazione sia al caso diretto sia a quello indiretto, e quell’«almeno un» è tutta la definizione: un percorso di zero passi farebbe raggiungere se stesso a ogni flusso. E la visita in profondità tiene il proprio percorso e restituisce infinito quando incontra un flusso che vi si trova già, quindi non si blocca proprio sull’ingresso per cui esiste il controllo dei cicli. Questo conta perché significa che i due controlli si possono eseguire in un ordine qualsiasi, il che significa che nessuno deve ricordare una regola di ordinamento.

La profondità è memorizzata come colonna sull’esecuzione figlia, il che va contro la nostra linea abituale per cui un valore derivabile non è una colonna. La chiave esterna verso il genitore si annulla quando il genitore viene cancellato, quindi dopo una cancellazione non si può risalire la catena per ricalcolarla. Un valore derivato la cui fonte può sparire non è derivabile.

L’orologio di ripiego è un giorno, non cinque minuti

Quando un’esecuzione figlia finisce, porta ad adesso l’orario di risveglio sulla riga di attesa del genitore, e il genitore viene preso in carico entro dieci secondi. Il meccanismo è questo.

C’è anche un orologio di ripiego, per il caso in cui questo non avvenga: un contenitore caduto a metà della conclusione del figlio, una riga toccata a mano. È di ventiquattro ore.

Cinque minuti sarebbero la scelta istintiva ed è quella sbagliata. Un figlio fermo su un’approvazione può attendere legittimamente trenta giorni. Con un orologio corto il genitore si sveglierebbe migliaia di volte in quel periodo per constatare che nulla è cambiato, e ognuno di quei risvegli è una richiesta HTTP dal gateway al contenitore web. Un giorno limita a un giorno il danno di un segnale perso e costa al massimo trenta risvegli inutili sull’attesa più lunga che il sistema consenta.

Un’approvazione di cui nessuno viene avvisato è un’esecuzione ferma

Una richiesta di approvazione ora può inviare un messaggio di posta elettronica, e la categoria sotto cui viene inviato è la propria e non la categoria generale dei flussi. È stata una deviazione voluta dal piano.

La categoria generale dei flussi va a chiunque abbia il modulo di automazione. Una richiesta di approvazione dovrebbe andare alle persone che possono davvero deciderla, cioè un insieme diverso e più piccolo. C’è quindi un secondo setaccio sulla categoria: la notifica viene consegnata soltanto ai membri che hanno il permesso di decidere.

Misurato sulla nostra organizzazione di prova: la categoria generale dei flussi si è risolta in tre destinatari e la categoria di approvazione in due. Due numeri che differiscono sono tutta la prova che il setaccio sta facendo qualcosa.

Un clic che non ha selezionato nulla

La lezione di misura di questo turno riguarda la prova nel browser e non il prodotto.

Il nostro helper per il browser ha un metodo di clic che invia una pressione e un rilascio del mouse. Su un pulsante ordinario funziona. Su un nodo del canvas dei flussi non fa assolutamente nulla: il pannello delle proprietà è rimasto sul nodo di innesco e l’elenco a tendina che volevamo cambiare non esisteva, il che si legge esattamente come una funzionalità mancante.

Il canvas avvia un trascinamento alla pressione del puntatore e chiama setPointerCapture sul nodo. Senza un evento di movimento del mouse che lo preceda, e senza il campo buttons impostato sulla pressione, il browser non produce un clic che il percorso di cattura riconosca. Aggiungendo quei due, la selezione è immediata.

C’è un secondo dettaglio nello stesso helper. Non clicchi in mezzo a un nodo. Il centro di un nodo con porte sta fra i punti delle porte, e quelli hanno un proprio gestore del puntatore per trascinare fuori un filo. Clicchi invece in alto a sinistra nell’intestazione.

Entrambi sembrano difetti del prodotto quando li si incontra, e nessuno dei due lo è. La forma generale del problema è questa: una simulazione di ingresso quasi corretta fallisce in un modo che non si distingue dal guasto della cosa che si stava provando a verificare.

Che cosa annoteremmo

  1. Una soglia batte una casella di spunta quando il valore corretto della casella è sempre lo stesso. Si chieda quando qualcuno la disattiverebbe; se non c’è risposta, non costruisca l’interruttore.
  2. Misuri la soglia contro il budget in cui deve stare, non contro la funzionalità che sta limitando.
  3. Non fissi la nuova soglia al vecchio limite solo perché così non cambia nulla. Che nulla cambi è il problema che stava risolvendo.
  4. Un ciclo e una catena troppo lunga sono errori diversi. Un solo messaggio per entrambi manda le persone nel posto sbagliato.
  5. Convalidi al salvataggio per il messaggio e in esecuzione per la garanzia. I record vicini cambiano senza riconvalidare il suo.
  6. Una ricerca che termina sul proprio percorso si può eseguire in qualsiasi ordine rispetto al controllo che sostiene, il che elimina una regola di ordinamento che nessuno ricorderebbe.
  7. Un valore derivato la cui fonte può essere cancellata non è derivato. Lo memorizzi.
  8. Dimensioni un orologio di ripiego sull’attesa legittima più lunga, non sulla rapidità con cui vorrebbe accorgersene.
  9. Instradi una notifica in base a chi può agire, non in base al modulo a cui appartiene.
  10. Un ingresso simulato quasi corretto fallisce come una funzionalità guasta. Controlli come il componente riceve davvero gli eventi prima di credere al risultato.

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, le suite dei pacchetti, del gateway e degli strumenti 7286 verdi, i test sulla visita 75 verdi, i controlli sui sottoflussi 13 verdi, la corsa end to end contro il database di sviluppo 58 verdi, la misura dei destinatari 4 verdi e la prova nel browser 31 verdi con un vero worker di gateway, nella quale una pausa da novanta secondi è ripresa dopo novantacinque secondi, un’esecuzione genitore e una figlia sono state collegate, è stato inviato esattamente un messaggio di approvazione per posta elettronica, e sia una catena di quattro sia un riferimento ricorsivo sono stati rifiutati con la catena incriminata nominata nel messaggio. La prova sull’editor è stata 9 verdi e la scansione di accessibilità non ha trovato nulla su sei pagine. Non dimostrati: i rami di esaurimento dei tentativi e di presa in carico scaduta, l’orologio di ripiego da ventiquattro ore, un sottoflusso dentro un ciclo, un’esecuzione figlia sparita, il messaggio a un non proprietario tramite un invio reale, e assolutamente nulla contro un vero hypervisor.

Dal lavoro di piattaforma su Provibr. Come la visita abbia imparato a fermarsi si trova in ogni chiamata ricorsiva era in posizione di coda, e la forma del grafo stesso si trova in un flusso è un grafo, e un grafo dev’essere finito.

Inizi con un host e un agente

Crei un account, installi l’agente con un solo comando e colleghi il suo primo host Proxmox. Il periodo di prova dura 14 giorni e non chiede alcun dato di pagamento.