[Libri] Recensione libro Release It! parte 3


Stability Pattern

Scopo di questi pattern non è prevenire che succedano eventi negativi, ma minimizzarne gli effetti.


Use Timeout

L'uso di un timeout definisce un isolamento da una situazione erronea: non si permette ad un problema presente in un sottosistema
di propagarsi nel proprio. Si applica sempre negli integration point.
Il classico esempio riguarda il recupero di una connessione db da un pool, l'esecuzione di una query, trasformare
il resultSet in oggetti e restituire la connessione. In tale ciclo ci possono essere diversi punti in cui si attende una risorsa.
In tal caso può essere sensato inserire timeout in un oggetto che si occupa di far tutto questo , un QueryObject.
Il rovescio della medaglia dell'uso di timeout è il rapido retry. Quindi meglio tornare un risultato (positivo, negativo o
informare che la richiesta verrà esaudita) piuttosto che fornire una risposta lenta.


Circuit Breaker

Il pattern che mi ha colpito di più! Si tratta di un componente che controlla la disponibilità di una risorsa e può
assumere in ogni istante un solo stato tra:
* CHIUSO - permette l'esecuzione di operazioni come sempre. Se l'esecuzione fallisce il circuit breaker lo rimembra. Dopo
un certo numero di fallimenti, superata una soglia entra in stato
* APERTO - tutte le chiamate falliscono immediatamente: non c'è quindi alcun tentativo di esecuzione reale. E' meglio
distinguere il fallimento dovuto allo stato aperto del circuito dal fallimento dell'esecuzione avvenuto quando il circuito
è chiuso. Dopo un certo lasso di tempo in cui ci si augura che la risorsa sia di nuovo up&running entra in stato
* SEMI APERTO - in cui esegue la prima operazione. Se ha successo, ridiventa chiuso, altrimenti ritorna ad essere aperto.
Il circuit breaker può mantenere informazioni circa diversi tipi di fallimento. I cambiamenti di stato dovrebbero essere
sempre loggati e lo stato corrente dovrebbe sempre essere visibile, riportato, misurato.


Bulkheads

Metafora dei compartimenti stagni di una nave. Se un servizio ha due clienti e uno dei clienti è in una situazione
dannosa (si pensi ad un carico eccezionale di richieste) tutto il resto del sistema ne soffre. Ad esempio conviene
sempre mantenere un pool di riserva per le richieste che provengono dall'amministratore del sistema.
Qui il discorso si fa più fumoso. Il concetto rimane chiaro, ma non si intuisce bene come applicarlo.


Steady State

Minimizzare il numero di interventi sulla macchina di produzione: ognuno di essi è rischioso, dannoso.
Così si è pensato quali sono i casi in cui si accede. Uno di questi è la necessità di fare pulizia di dati, quali i log o
vecchi dati nel db. Quando infatti tali dati crescono sempre a dismisura, se non ci sono agenti di pulizia, prima o poi
si generano problemi. Tale pulizia deve essere implmementata nella logica applicativa, altrimenti può essere effettuata
da chi non sa che reazioni possono accadere (script creati da dba)
Tipicamente si esegue la prima release consci del problema delegandolo al futuro, senza poi
affrontarlo. Nel caso di log, prevedere rotazioni temporali e mantenerli in una macchina diversa da quella di produzione.
Un altro caso è l'uso di cache, dove il numero di chiavi può essere infinito, e quindi è opportuno definire un limite.
Serve inoltre una qualsiasi politica di invalidazione ad esempio time based o LRU etc..


Fail Fast

Se il sistema può determinare rapidamente se fallirà l'esecuzione di una operazione è opportuno fallisca
rapidamente. Un esempio di violazione si ha quando un load balancer accetta una richiesta ma sa che al momento
nessun server è attivo eppure la tiene in coda per un po' di tempo. La metafora è quella di un libro di cucina,
dove la precondizione per la preparazione di una ricetta è che ci siano tutti gli ingredienti richiesti.
Un esempio è quando si esegue la validazione dei campi di input dopo aver allocato una connessione: non
si dovrebbe far lavoro inutilmente. Quando si fallisce subito è opportuno notificare al client che è avvenuta
una system failure, ad esempio una risorsa non disponibile, distinguendola da una generica application failure.


Handshaking

Si tratta di implementare una comunicazione tra client e server. In alcuni protocolli di tipo application-level
come l'http non supportano l'handshaking. Può essere utile implementarne una versione custom quando
il costo legato alla necessità di eseguire una chiamata in più (quella per sapere lo stato) è minore di quello relativo
all'effettuare una chiamata che non avrà successo.
Ad esempio, via http, fornendo una pagina che pubblica lo stato del server. Se il cliente tramite questa comunicazione verifica
che il server non può rispondere evita di fare altre richieste (health-check).


Test Harness

(definizione: In software testing, a test harness or automated test framework is a collection of software and test data
configured to test a program unit by running it under varying conditions and monitor its behavior and outputs.)
I test di integrazione verificano il corretto funzionamento del sistema quando tutte le sue dipendenze funzionano correttamente.
Inoltre essi testano casi d'errore coperti da specifiche.
Si possono invece eseguire test di sistema che non verificano le specifiche, ad esempio impostando delle porte in modo
che rispondano con diversi comportamenti:
* una porta che accetta connessioni ma non risponde mai
* una che accetta connessinoi e risponde valori a caso
* accetta una connessione poi la chiude
* risponde molto lentamente
* risponde molto velocemente
Differisce dal mock object in questo senso: i mock object simulano un comportamento che aderisce alle interfacce definite.
Un test harnesses è eseguito su un diverso server e non si conforma alle interfacce; può provocare errori network, di protocollo
o anche a application-level.

Nessun commento: