Blog ·
Due regole, e un collegamento morto in mezzo
Una scansione di 1776 pagine ha trovato esattamente 18 URL rotti. Entrambe le regole che li producevano erano corrette; nessuno aveva provato il loro incontro.
Di Provibr
Abbiamo scansionato per bene il sito di produzione per la prima volta dopo parecchio: ogni collegamento interno seguito, ogni alternativa di lingua dichiarata controllata, ogni blocco di dati strutturati convalidato, ogni immagine controllata per il testo alternativo.
1776 pagine, 1750 voci nella mappa del sito ed esattamente un difetto. Zero orfani, zero catene di reindirizzamento, zero errori di alternativa di lingua su 17218 dichiarazioni, zero blocchi di dati strutturati non validi su 1642, zero immagini senza testo alternativo su 15463, e 920 immagini di anteprima social che rispondono tutte 200.
L’unico difetto erano 18 URL che restituivano 404: le pagine di documentazione di due pannelli di fatturazione, in tutte e nove le lingue. Ciascuno di quei 18 aveva esattamente un collegamento interno che puntava verso di esso, dalla pagina di integrazione dello stesso prodotto nella stessa lingua.
Entrambe le metà della cosa sono volute. È questo che la rende degna di essere annotata.
Le due regole
Alcune delle nostre integrazioni stanno dietro un flag di rollout. Quando il flag è spento, il fornitore sparisce dal catalogo, dalla mappa del sito, dai contatori e dal selettore del tipo nel pannello, tutto derivato da un’unica tabella così che non ci sia un secondo elenco da dimenticare.
Due di esse non sono del tutto ordinarie. Per FOSSBilling e Paymenter c’è un modulo che si installa nel loro pannello, e la pagina all’indirizzo /integrations/fossbilling descrive quel modulo. Non è una pagina su una connessione che offriamo; è una pagina su una cosa che si può scaricare oggi. Esiste quindi una regola che tiene in piedi quelle due pagine di integrazione quando il fornitore dietro il flag è nascosto, e porta la propria ragione nel sorgente.
La pagina di documentazione è il caso opposto. /docs/fossbilling descrive la connessione, che è esattamente ciò che il flag spegne, quindi sparisce insieme al fornitore.
Entrambe le regole sono giuste. Uno slug, due superfici, due risposte, e il codice ha due funzioni invece di una che sarebbe sbagliata metà delle volte.
Ciò che nessuno aveva controllato è che cosa succede fra le due. La pagina sopravvissuta portava il collegamento tematico a quella svanita.
La regola era già annotata
La funzione che produce quel collegamento risolve un percorso di documentazione per uno slug di integrazione. Il suo stesso commento di documentazione diceva, prima che tutto questo accadesse, che un collegamento a una guida che non esiste non è un collegamento tematico ma un collegamento morto.
La regola c’era. Era misurata contro l’elenco sbagliato: l’insieme delle pagine di documentazione che esistono nel sorgente, invece dell’insieme di quelle che rispondono in questa build. Quei due insiemi sono gli stessi quasi sempre, e il giorno in cui differiscono è il giorno in cui qualcosa è nascosto.
La riparazione è una riga sola. Non è questa la parte interessante; la parte interessante è che una regola corretta, scritta per esteso nel posto a cui appartiene, non è un controllo. La prosa in un commento descrive un’intenzione. Soltanto qualcosa che gira può tenerla in riga.
Un terzo caso che nessuna scansione avrebbe potuto trovare
Abbiamo scritto una protezione invece di limitarci a correggere la riga: per ogni slug di integrazione, il percorso di documentazione risolto deve essere uno che questa build serve davvero. È stata dimostrata rossa prima, e ha riportato le due pagine che la scansione aveva trovato.
Ne ha riportata anche una terza: la nostra integrazione con il server di backup, che sta dietro un altro flag. La sua pagina di documentazione è nascosta, e il collegamento verso di essa era costruito esattamente allo stesso modo.
La ragione per cui nessun crawler troverà mai quel caso è che anche la sua pagina di integrazione è nascosta. Il collegamento morto viveva su una pagina che restituisce a sua volta 404, quindi non esiste alcun percorso dall’esterno verso la cosa sbagliata. Sarebbe emerso il giorno in cui qualcuno avesse acceso quel flag e pubblicato una pagina con sopra un collegamento rotto, e i due eventi sarebbero stati lontani settimane.
Un crawler può trovare difetti soltanto su pagine raggiungibili. Scritto così suona ovvio ed è facile da dimenticare quando una scansione torna pulita, perché una scansione pulita sembra un’affermazione sul codice invece che sulle pagine che si è dato il caso fossero visibili.
La protezione ha un altro test che non fa nulla se non tenere onesto il primo: afferma che più di dieci slug si risolvono davvero in una pagina di documentazione. Senza di esso, una modifica per cui il risolutore non restituisse più nulla per nessuno slug non produrrebbe alcun collegamento morto e nemmeno alcun collegamento tematico, e quelle due cose si leggono in modo identico in un’esecuzione verde dei test.
L’altra metà: dire a un motore di ricerca che qualcosa è cambiato
Lo stesso turno ha aggiunto un canale di segnalazione, che è una piccola cosa con dentro una sorpresa.
Il protocollo è un solo POST che dice a diversi motori di ricerca che un URL è cambiato. La proprietà dell’host si dimostra servendo una chiave come file di testo nella radice. La nostra chiave viene dall’ambiente, e quando è assente l’intera faccenda non fa nulla e lo dice una volta. È lo stato normale in sviluppo e in ogni test.
Due dettagli meritano di essere ripetuti perché sono entrambi trappole.
Il file della chiave viene servito attraverso una riscrittura, perché una rotta dinamica non può stare nella radice accanto al segmento di lingua. Una riscrittura che corrispondesse a qualsiasi cosa punto txt nella radice inghiottirebbe anche il file robots. Due cose lo impediscono: la riscrittura gira nella fase che vede soltanto le richieste a cui non ha corrisposto alcun file reale, e il modello della chiave richiede almeno otto caratteri, mentre robots ne ha sei e llms quattro. Il limite inferiore non è ordine; è la seconda serratura.
E la segnalazione è agganciata alla funzione che già sa che il contenuto pubblico è cambiato, non alla pubblicazione. Un rilascio che tocca soltanto una schermata del pannello annuncerebbe altrimenti ogni volta 1750 indirizzi come modificati, che è un buon modo per farsi ignorare. Quando cambia la tabella dei prezzi, vengono segnalate in nove lingue le due rotte che generano i prezzi, cioè 18 indirizzi.
La cosa che sembrava un errore di configurazione
La prima segnalazione vera dopo la pubblicazione, con la chiave di produzione al suo posto, ha restituito 403.
Il file della chiave restituiva 200 da ogni punto in cui abbiamo provato, anche con lo user agent di un crawler. Ogni ingrediente tornava. Il passo naturale successivo è andare a guardare la configurazione al bordo, o la riscrittura, o l’ordine delle fasi, ed è lì che se ne va un’ora.
Quello che abbiamo fatto invece è stato provare due cose più piccole. La segnalazione di un singolo URL ha restituito 200 all’istante. La stessa segnalazione in blocco, un minuto dopo, ha restituito anch’essa 200, su entrambi gli endpoint.
Il 403 era dunque un effetto di primo contatto dalla loro parte, non un difetto dalla nostra. Il file della chiave non era ancora stato scaricato e messo in cache dal servizio ricevente nel momento in cui è arrivata la prima richiesta in blocco.
L’abbiamo annotato dove vive il runbook, perché il guasto è disegnato alla perfezione per mandare qualcuno a caccia: il codice di stato dice vietato, che è un’affermazione sul permesso, e tutto ciò che riguardava il permesso era corretto. Prima di indagare su un fallimento di autorizzazione a una prima richiesta, ripeta la stessa richiesta. Non costa nulla ed è l’ipotesi più economica dell’elenco.
C’è una nota collegata per chi un giorno ruoterà quella chiave: il file viene servito con un’ora di cache, quindi una rotazione richiede uno svuotamento o un’ora di pazienza, e dentro quella finestra lo stesso 403 significa qualcosa di completamente diverso.
Che cosa annoteremmo
- Due regole corrette possono produrre un difetto nella giuntura fra loro. Nessuno dei due proprietari sbaglia e nessuno dei due sta guardando.
- Lo stesso identificatore può avere due superfici con due risposte giuste. Due funzioni, non una.
- Una regola in un commento non è un controllo. La nostra era scritta per esteso e misurata contro l’elenco sbagliato.
- Ciò che esiste e ciò che risponde sono insiemi diversi, e divergono esattamente quando qualcosa è nascosto.
- Un crawler trova difetti soltanto su pagine raggiungibili. Il nostro non poteva vedere affatto il terzo caso.
- Una protezione trova l’esemplare che non è ancora stato pubblicato. È l’intera ragione per scriverne una invece di correggere la riga.
- Dia a ogni protezione un test che dimostri che sta guardando. Nessun collegamento morto e nessun collegamento del tutto si leggono allo stesso modo.
- Un limite inferiore in un modello può essere un confine di sicurezza. Otto caratteri sono ciò che tiene una riscrittura lontana dal file robots.
- Annunci i cambiamenti di contenuto dalla cosa che sa che il contenuto è cambiato, mai dalla pubblicazione.
- Riprovi prima di diagnosticare il fallimento di una prima richiesta. Un vietato che un minuto dopo diventa un successo non ha mai riguardato il permesso.
Per chiarezza su che cosa sia: la scansione ha coperto 1776 pagine contro la produzione e 1750 voci della mappa del sito con 913 date di modifica, nessuna delle quali nel futuro, la correzione è una riga sola protetta da tre test dimostrati rossi prima, il percorso di segnalazione è stato esercitato da un capo all’altro contro un endpoint simulato compresi cinque percorsi di errore, e la campagna di controlli su una build di produzione standalone si è chiusa a 96 verdi. Non dimostrato: nel momento in cui la correzione è stata scritta era verde su una build di produzione locale e non era ancora stata confermata in produzione, e due famiglie di rotte nella scansione non hanno prodotto alcuna pagina perché i loro registri sono vuoti.
Dal lavoro di piattaforma su Provibr. Che cosa una migrazione di fatturazione può portarsi dietro e che cosa no si trova in quindici cose che non viaggiano, una pubblicazione il cui passo sullo schema non è mai girato si trova in la migrazione che non è mai partita, e un altro controllo che era verde perché non arrivava mai a destinazione si trova in verde perché non poteva arrivarci.