Stavamo limitando i nostri stessi servizi
L'API di Mythex consente 300 richieste al minuto da un singolo indirizzo di rete. Quel limite esiste per proteggerci da una persona o uno script che bombarda l'API. In produzione ha cominciato a rifiutare i nostri stessi servizi, cioè la parte del nostro edge che serve le app pubblicate e il servizio che esegue l'agente IA, perché ciascuno dei due raggiunge l'API da pochissimi indirizzi. Le richieste che portano la nostra credenziale interna ora sono esentate, e tutti gli altri sono limitati esattamente come prima.
Il sintomo
L'API ha iniziato a rispondere ad alcune delle nostre chiamate interne con 429 Too Many Requests. Sono stati colpiti tre tipi di chiamata, e ciascuno conta per chi usa Mythex:
- Ricerca di quale app vive su un dominio personalizzato. Quando un visitatore apre un'app pubblicata sul proprio dominio, il nostro edge chiede all'API dove si trova quel sito. Un 429 lì significa un visitatore che non riesce a raggiungere un sito.
- Salvataggio di un turno dell'agente in esecuzione. Mentre l'agente lavora, il suo avanzamento viene salvato man mano, così una risposta sopravvive a un refresh o a una connessione caduta. Un 429 lì significa avanzamento che non è stato salvato.
- Verifica dell'accesso a un'anteprima. L'anteprima di un'app che stai costruendo è protetta, e la protezione chiede all'API. Un 429 lì significa un'anteprima che non si apre.
Nessuna di queste era una persona che chiamava l'API. Erano tutte chiamate nostre.
Perché succedeva
Un limite di frequenza per indirizzo fa una sola ipotesi: un indirizzo corrisponde più o meno a un chiamante. Per le persone è abbastanza vicino. Per la nostra infrastruttura è gravemente sbagliato.
- Il servizio edge che serve app pubblicate e anteprime raggiunge l'API da pochi indirizzi di uscita condivisi. Ogni visitatore di ogni app pubblicata, e ogni verifica di anteprima, arriva come se venisse da uno di quei pochi.
- L'orchestratore, cioè il servizio che esegue i turni dell'agente, raggiunge l'API da un solo indirizzo di container. Ogni turno in esecuzione, per ogni utente, condivide quell'indirizzo.
Quindi il budget pensato per una persona veniva diviso tra tutto il traffico di un intero servizio. Quei servizi lo esaurivano tra loro, e il limitatore ha fatto ciò per cui era stato configurato: li ha rifiutati.
La soluzione
I nostri servizi interni dimostrano già chi sono. Ogni chiamata interna porta un token interno, e ogni rotta sotto la sezione interna dell'API verifica quel token prima di fare qualsiasi cosa.
Il limitatore di frequenza ora usa la stessa verifica. Una richiesta il cui token coincide con quello interno finisce in una lista di esenzione e non viene conteggiata nel budget per indirizzo. Tutto il resto viene conteggiato esattamente come prima: 300 richieste al minuto per indirizzo.
Due dettagli hanno impedito che diventasse un buco:
- Un token sbagliato non ottiene alcun passaggio. La lista di esenzione corrisponde solo al vero token interno. Una richiesta con un token inventato viene conteggiata e limitata come ogni altra, e le rotte interne la respingono con
401 Unauthorized. - Il limite stesso non è cambiato. Non abbiamo alzato il numero per fare spazio. Alzarlo avrebbe nascosto il problema fino alla successiva crescita del traffico, e avrebbe indebolito la protezione per il caso per cui il limite esiste.
Abbiamo spostato le impostazioni del limitatore in una piccola funzione e scritto test su di essa. Uno invia 20 richieste con il token interno a un limitatore impostato su 5, e si aspetta che tutte e 20 vadano a buon fine. Un altro invia richieste normali da un solo indirizzo e si aspetta che le prime cinque riescano e quelle successive vengano rifiutate. Un terzo invia richieste con un token falsificato e si aspetta che vengano rifiutate allo stesso modo.
Cosa ci siamo portati a casa
- I limiti portano con sé delle ipotesi. «Un indirizzo, un chiamante» è incorporato in un limite per indirizzo. Smette di essere vero nel momento in cui i tuoi servizi interni stanno dietro indirizzi condivisi, e molti lo sono: load balancer, reti edge, container, reti di uffici.
- Distingui il tuo traffico per identità, non per indirizzo. Gli indirizzi indicano come arriva il traffico, non chi lo ha inviato. Avevamo già una credenziale che dimostra che una chiamata è nostra; il limitatore semplicemente non la guardava.
- Non risolvere alzando il numero. Un limite più alto avrebbe fatto sparire i 429 per un po', proteggendo meno, e sarebbe fallito di nuovo più avanti.
- Guarda chi viene limitato. Un 429 che colpisce il tuo stesso servizio è una segnalazione di bug sui tuoi limiti, non un segno che il limitatore funziona.
Cosa significa se costruisci su Mythex
Non devi cambiare nulla. Le app pubblicate su domini personalizzati, i turni dell'agente e le anteprime non dipendono più da quanto è occupato il resto di Mythex. Il traffico della tua app è affare della tua app: il limite descritto qui riguarda la nostra API, non le app che pubblichi.
Se gestisci un backend tuo, la stessa trappola vale per qualsiasi limite di frequenza tu aggiunga. Le nostre guide su cos'è un'API e la checklist di sicurezza per le app create con l'IA sono un buon punto di partenza. E per un altro caso in cui la nostra stessa infrastruttura si è messa di traverso, vedi come abbiamo portato i deploy a zero errori 502.