Skip to content
Appunti personali
3 min di lettura

Container pronti in attesa: 45 secondi diventano una frazione, e intanto girano a vuoto

Terzo articolo della serie sul sandbox per agenti. Il pool caldo è il posteggio dei taxi in aeroporto — si parte subito, e i motori consumano anche da vuoti. Più il resoconto onesto dei tre difetti di questo dispatcher, che sono poi ciò che rende necessari gli articoli seguenti.

L’articolo precedente si chiudeva così: l’errore non era l’isolamento, ma aspettare l’utente prima di prepararsi. Allora ci si prepara prima.

C’è un’analogia già pronta: il posteggio dei taxi in aeroporto. Si esce dal terminal e si sale subito in auto perché le auto erano già in fila lì — nessuno ha mandato un autista dal centro dopo la chiamata.

Il codice: tre pezzi

Uno, una fila di container in attesa. Se ne tengono accesi cinque, sempre:

spec:
  replicas: 5          # cinque pronti, in ogni momento

Due, ogni container dichiara se è libero.

is_busy = False        # sto lavorando in questo momento?

@app.get("/health")    # qualcuno chiede, io rispondo: libero o occupato
def health_check():
    return {"status": "busy" if is_busy else "idle"}

Tre, un dispatcher. Arriva una richiesta, lui chiede in giro e affida il lavoro al primo libero:

for pod in pods:                       # chiede a uno a uno
    resp = await http_client.get(f"...{pod}/health")
    if resp.json().get("status") == "idle":
        return pod                     # trovato un libero, gli dà il lavoro

Sul percorso della richiesta non si costruisce più alcun container, e nessuno tormenta il sistema di gestione chiedendo se è pronto. L’attesa scende da 45 secondi a una frazione di secondo: quanto piccola dipende dalla macchina e dalla dimensione del pool, e l’endpoint restituisce il valore appena misurato — vale il numero che esce dalla propria esecuzione.

Centinaia di volte più veloce, con meno codice. Quell’euforia inganna, ed è per questo che la seconda metà di questo articolo conta di più.

La fattura: quelle centinaia di volte sono a noleggio

Il posteggio dei taxi consente di partire subito perché una fila di auto sta lì vuota. Le auto vuote non trasportano nessuno, e intanto gli autisti ci sono, il carburante si consuma, i posti restano occupati. Il pool caldo è esattamente lo stesso scambio: denaro speso di continuo, perché l’utente non aspetti mai.

Il problema smette di essere ingegneristico e diventa gestionale:

  • quante auto bastano? Non la domanda media, quella di picco. Un pool vuoto risponde «al momento non c’è capacità disponibile», che è peggio di aspettare 45 secondi;
  • come ci si adatta alla domanda? Aggiungere auto significa ripagare quei 45 secondi, quindi la crescita deve avvenire prima del picco, non come reazione;
  • se ne possono tenere meno? Sì, e il pool si svuota più facilmente nei picchi. Si può anche scendere a zero nelle ore morte, e il primo cliente ripaga i 45 secondi.

Non esiste una risposta standard, solo la posizione che la propria attività può reggere. L’importante è che quella posizione venga scelta di proposito: il pericolo del pool caldo è proprio far sembrare risolto il problema della latenza.

Tre difetti di questo dispatcher

Il codice è nel repository, e con esso i difetti — perché sono esattamente ciò che rende necessari gli articoli seguenti.

Uno, tra il chiedere e l’assegnare c’è un vuoto. Il dispatcher chiede «sei libero?», sente di sì, poi invia il lavoro — con un round trip di rete nel mezzo. Sotto carico due richieste possono scegliere lo stesso container; alla più lenta viene risposto «sono già occupato» e, poiché il dispatcher non ritenta, quell’errore arriva dritto all’utente.

Due dispatcher assegnano lo stesso taxi vuoto a due passeggeri. Il primo sale, il secondo resta lì in piedi, e nessuno gli chiama un’altra auto.

Due, chiedere in giro costa di più man mano che si cresce. Invisibile con cinque container; con cinquanta sono fino a cinquanta round trip di rete, ciascuno con un timeout da aspettare. «Trovare un libero» diventa esso stesso una fonte di ritardo.

Tre, quando è pieno sa solo rifiutare. Non esiste il concetto di coda o di numeretto, quindi un picco si assorbe respingendo le persone.

Una scorciatoia implementativa va nominata: il dispatcher raggiunge i container attraverso un tunnel locale kubectl proxy. Va bene in sviluppo, non è un pattern di produzione — in produzione si userebbe la service discovery o si contatterebbero direttamente i container.

Il muro successivo

Tutto quanto sopra è sopportabile. Questo no: «sono occupato» vive, come l’avanzamento del task, solo nella memoria di quel container.

Quando il container muore, se ne vanno entrambi. E questi container vengono recuperati, aggiornati e portati via dai guasti delle macchine come routine — ed è esattamente ciò che il prossimo articolo smonta.

In una riga: la velocità è noleggiata con il funzionamento a vuoto permanente; quello scambio va messo a bilancio di proposito, non scoperto quando arriva la fattura.

Codice: agent-sandbox-oss/lab3.