Blog ·
Aggiornato alle tre di notte, di proposito
L’aggiornamento automatico dell’agente è attivo alle 03:00. Interessante era quali situazioni consumano una fascia e una notte con due volte le tre.
Di Provibr
L’agente che gira nella rete propria di un cliente riceve una nuova compilazione ogni pochi giorni. Fino a questo giro, aggiornarlo era un pulsante che qualcuno doveva premere, quindi in pratica accadeva quando a qualcuno capitava di guardare. Il desiderio era una frase sola: ciascuno può impostare sul proprio agente un orario di aggiornamento, più momenti al giorno, attivo per impostazione alle tre di notte.
La frase è facile. A farne un giro è stato che un riavvio programmato di infrastruttura ha molti modi di essere silenziosamente sbagliato, e quasi tutti da fuori sembrano a posto.
L’impostazione sta sulla colonna, e lì sta tutto il desiderio
«Attivo per impostazione per tutti» si legge in due modi. Scritto nel codice applicativo vale per gli agenti creati d’ora in avanti, il che esclude proprio i clienti che ne hanno già uno. Scritto come valore predefinito di colonna nella migrazione vale per ogni riga esistente in una sola istruzione.
È quindi un valore predefinito di colonna: un booleano che parte da vero e un vettore JSON che parte con una sola voce 03:00. Misurato dopo l’applicazione della migrazione sulla base di sviluppo: ogni agente esistente attivo, ognuno alle tre, e un agente appena creato a cui non è stato passato nessuno dei due valori ha ricevuto lo stesso stato. Quest’ultima verifica è quella che dimostra che il lavoro lo fa il valore predefinito e non uno script di semina.
I momenti sono ora di orologio nel fuso della piattaforma, non UTC. Non esiste un fuso per organizzazione e il pannello mostra tutto in quell’unico fuso, quindi un orario conservato in UTC scivolerebbe di un’ora in estate e chi avesse scritto le tre verrebbe interrotto alle cinque.
Due strati su una sola espressione regolare
Un momento è HH:MM su una griglia di cinque minuti, e quel modello esiste due volte: una nel validatore di schema, che rifiuta un campo di modulo con un messaggio leggibile, e una come vincolo di base dati, che rifiuta tutto ciò che non è mai passato per quel modulo.
Un vincolo di controllo non può contenere una sottointerrogazione, quindi «ogni elemento di questo vettore è un momento valido» non può essere una verifica di esistenza sul vettore spacchettato. Ciò che può essere è un predicato di percorso JSON che seleziona proprio gli elementi sbagliati, avvolto in una verifica che quella selezione sia vuota.
La prima versione di quel predicato era sottilmente inutile, e sembrava completa:
$[*] ? ( !(@ like_regex "^([01][0-9]|2[0-3]):(00|05|10|15|20|25|30|35|40|45|50|55)$") )La corrispondenza di un’espressione regolare con qualcosa che non è una stringa, nel percorso JSON non è falsa. È Unknown. E la negazione di Unknown è anch’essa Unknown, e un filtro tiene solo gli elementi per cui il predicato è davvero vero. La selezione degli elementi sbagliati tornava quindi vuota per un vettore di numeri, e il vincolo lo accettava. Misurato contro PostgreSQL 17.10, [3], [null] e [{"a":1}] sono passati tutti e tre.
La correzione è una clausola davanti: gli elementi il cui tipo non è una stringa sono sbagliati per definizione. Tutti e tre ora vengono rifiutati. È esattamente la classe di errore che continua a costarci giri: una guardia che non intercetta nulla e sembra completa, e nessuna prova che fallisce, perché non c’era nulla contro cui fallire.
Cinque passi, e l’ordine è il giro
Ogni minuto un lavoratore chiede chi ha un orario, consegna le righe a un pianificatore puro che decide quale fascia è dovuta, e poi percorre cinque passi per agente. La prenotazione è il passo quattro e non il primo, e ogni passo precedente sta lì per un motivo proprio.
| Passo | Se la risposta è no |
|---|---|
| C’è una compilazione più recente? | La fascia viene comunque prenotata, marcata come nulla da fare |
| L’agente è connesso? | Non viene prenotato nulla, e il minuto dopo riprova |
| C’è già un aggiornamento in corso? | Non viene prenotato nulla; il primo onorerà comunque la fascia |
| Prenotare la fascia | Decide un indice univoco; chi perde non invia nulla |
| Inviare il comando | La stessa funzione del pulsante nel pannello |
Le prime due righe sono opposti voluti, e quell’asimmetria è la decisione più interessante del giro.
Nulla da fare consuma la fascia, perché la risposta non può più cambiare dentro una fascia: il manifesto della pubblicazione su disco è lo stesso per tutta la finestra, quindi rigiudicare ogni minuto costa interrogazioni e non porta nulla. E dà al cliente qualcosa da vedere. Una pagina di agente che dice «03:00, già aggiornato» si distingue da un orario che non gira. Un elenco vuoto no.
Essere fuori linea non consuma nulla, perché una fascia prenotata mentre l’host si sta riavviando è consumata per sempre, e allora quel cliente perde una notte in silenzio. Il minuto dopo riprova, finché la finestra di recupero non si chiude; poi l’agente prende semplicemente la fascia successiva.
Quindici minuti, e non sei ore
In questa piattaforma le copie di sicurezza hanno una finestra di recupero di sei ore. Questa è di quindici minuti, e la differenza sta in cosa va storto se si è generosi.
Una copia fatta alle nove del mattino invece che alle tre resta una copia. Un agente che si riavvia alle nove del mattino invece che alle tre è un’interruzione in piena giornata lavorativa, e le tre di notte sono proprio ciò che il cliente ha scelto per evitarla. Quindici minuti coprono ciò per cui questa finestra esiste davvero, una passerella appena riavviata o un paio di giri saltati, e nient’altro.
Il pianificatore guarda due giorni di calendario indietro e non uno, e non è una svista. Un momento delle 23:55 con finestra di recupero cade, poco dopo la mezzanotte, nel giorno di calendario di ieri; guardando solo oggi, quella fascia per costruzione non arriverebbe mai.
Due freni, per due motivi diversi
Cinque agenti per organizzazione a giro, e venticinque in tutto.
Il freno per organizzazione riguarda la banda del cliente e non la nostra. Il binario pesa circa ventidue megabyte, un fornitore con quaranta agenti di solito li ha tutti e quaranta alle 03:00 perché è l’impostazione, e quaranta scaricamenti simultanei escono dallo stesso collegamento. Cinque al minuto distribuiscono una flotta di quaranta su otto minuti, comodamente dentro la finestra di quindici.
Il freno globale esiste perché il primo agisce per organizzazione: con cento organizzazioni sarebbero cinquecento aggiornamenti in un minuto. Il resto arriva il minuto dopo, sempre dentro la finestra.
Su tutto questo non c’è alcuna bandiera di rilascio, cosa insolita da noi. L’interruttore è già dal cliente, sulla colonna; una seconda bandiera, valida per tutta la piattaforma, prometterebbe ciò che la colonna promette già. Ciò che esiste è un freno d’emergenza per noi: portare a zero l’intervallo di interrogazione ferma il temporizzatore senza cambiare una sola impostazione di cliente.
La notte con due volte le tre
L’ultima domenica di ottobre l’ora di orologio 03:00 esiste due volte, e l’ultima domenica di marzo le 02:30 non esistono affatto. A entrambe risponde una funzione ausiliaria condivisa, non ricopiata in questo modulo, proprio perché un algoritmo di ora legale mantenuto in due posti diverge un giorno all’anno.
Quella funzione risponde a un’ora di orologio ambigua con il primo dei due istanti, e a una inesistente con l’istante successivo che invece esiste. Le conseguenze sono proprio quelle che si vogliono. Nella notte lunga le 03:00 girano una volta e non due; misurato: una fascia. Nella notte corta le 02:30 diventano semplicemente le 03:30 locali invece di essere saltate, perché non saltare è il lato sicuro di quella moneta. Entrambe sono fissate da prove.
I giorni si percorrono come giorni di calendario e non sommando un numero fisso di millisecondi: sommare ventiquattro ore attraverso un cambio salta un giorno o ne fa uno due volte.
A chi viene detto, e quanto spesso
La riconciliazione che riporta gli esiti vive nel lavoratore e non sulla via di invio, e avvisa solo l’orario. Un clic a mano aveva già qualcuno che guardava.
Gli avvisi si raggruppano per organizzazione, perché quaranta agenti nel giorno di una pubblicazione non devono diventare quaranta righe in una casella. Un fallimento viene segnalato al massimo una volta per agente e per giorno di calendario. E la marca temporale che registra che si era valutato di avvisare viene scritta anche quando la decisione è stata tacere, così che il silenzio si distingua da un lavoratore che non è mai arrivato.
Un aggiornamento programmato di proposito non compare come operazione radice nel cassetto delle attività: alle tre di notte non aspetta nessuno, e un cassetto pieno di righe notturne è inutile per ciò per cui esiste. La traccia è una riga di esecuzione, un evento e un avviso. Quell’evento è di un tipo suo, perché la cronologia non deve sostenere che alle tre sia stato il cliente ad avviare qualcosa di persona.
Cosa annoteremmo
- «Per impostazione per tutti» è un valore predefinito di colonna. Nel codice applicativo significa «per tutti d’ora in avanti», e questo esclude proprio le persone a cui stava pensando.
- Unknown non è falso. Nel percorso JSON un predicato su un valore del tipo sbagliato non è né vero né falso, e negarlo non aiuta. Verifichi prima il tipo.
- Decida per ogni situazione se consuma la fascia. Nulla da fare e irraggiungibile sono risposte opposte, ed entrambe sono invisibili se le si scambia.
- Una finestra di recupero è una promessa sull’interruzione, non sulla completezza. Copi il numero da un sistema con un diverso modo di guastarsi e avrà quello sbagliato.
- Freni dove si sente davvero. Quaranta agenti in una rete di cliente condividono un collegamento, ed è quello il limite, non la nostra rotta di scaricamento.
- Risolva l’ora legale una volta sola. Due copie dello stesso ragionamento vanno d’accordo fino all’ultima domenica di ottobre.
Per chiarezza su cosa sia tutto questo: l’orario è in produzione e attivo per impostazione. Diverse cose non sono dimostrate. Il lavoratore non è stato esercitato dentro una passerella davvero in funzione, nessun agente reale ha ancora eseguito un aggiornamento programmato, l’avviso di fallimento e la sua deduplicazione giornaliera non sono stati osservati da capo a fondo, e la schermata è stata riletta in due delle nove lingue.
Dal lavoro di piattaforma di Provibr. Perché un agente rifiuti un aggiornamento che non può verificare è spiegato in come un aggiornamento firmato dell’agente rifiuta sé stesso, e perché l’agente si colleghi verso l’esterno invece di ascoltare è spiegato in un agente in uscita invece dell’inoltro di porte.