Messaggi e gestione degli errori: business errors, don’t exist.


mistake-876597_1280

Abbiamo parlato di permanent errors e transient errors, c’è un’ultima categoria di errori che dobbiamo prendere in considerazione: gli errori di business.

Gli errori di business non esistono

Supponiamo uno scenario di questo genere:

Il servizio X invia il comando CreaUtente con annessi tutti i dettagli, il servizio Y prende in carico il messaggio e la validazione delle regole di business fallisce perché un utente con la stessa mail esiste già.

In un caso come questo, e potete immaginarne infiniti altri:

  1. Ha senso riprovare il messaggio come faremmo con un transient error?
  2. Ha senso inoltrare il messaggio ad una coda di errore dove un membro del team operation non ha nessuna possibilità di farci nulla?

Un messaggio come quello dell’esempio, se genera un errore di business deve essere seguito da uno o più eventi, o messaggi di risposta, che dettagliano quello che è successo. Quindi il servizio Y può ad esempio pubblicare un evento CreazioneUtenteFallita con i dettagli del problema.

È quindi importante in scenari come questo andare dal business e chiedere: cosa deve succedere se la mail è duplicata? di certo non è un’eccezione come se il database non fosse disponibile. È tutto li :-)

author: Mauro Servienti | posted @ lunedì 23 gennaio 2017 12.23 | Feedback (0)

Messaggi e gestione degli errori: transient errors


MD1ihgJ

Non tutti gli errori nascono uguali. Abbiamo visto cosa sono i permanent error, ma non sono l’ unica forma possibile di errore.

Transient Error

Un problema tipico caratteristica dei sistemi distribuiti è la disponibilità ballerina delle risorse. I sistemi distribuiti mettono in luce molto rapidamente la così dette fallacies of distributed computing, è quindi cosa più che normale che il database, o una risorsa qualsiasi di cui abbiamo bisogno, anche la rete stessa, non sia disponibile nel momento in cui ne abbiamo bisogno.

Sempre tipico dei sistemi distribuiti è che se riproviamo poco dopo tutto fila liscio come ci aspettiamo perché la risorsa in questione è nuovamente disponibile.

Si parla in questo caso di errori transienti, che sono quindi quegli errori che si risolvono da soli “semplicemente” riprovando.

Responsabilità

In questa direzione è interessante notare come il modello di comunicazione scelto modifichi radicalmente responsabilità e conseguenze.

Il modello RPC impone che l’onere del riprovare sia carico del chiamante e qualsivoglia problema il destinatario abbia impatta di conseguenza anche il chiamante. Il modello asincrono basato sui messaggi e code sposta la responsabilità del riprovare sul destinatario, come è giusto che sia per certi versi. Inoltre eventuali problemi del destinatario non impatto diretto sul chiamante.

Ovviamente dobbiamo accettare che il modello di sviluppo sia asincrono e fire & forget.

Adesso che finalmente Ale ha bloggato posso tornare alla mia serie su Services UI Composition, ma prima un ultimo post sulla gestione degli errori di business ;-)

author: Mauro Servienti | posted @ venerdì 20 gennaio 2017 15.50 | Feedback (0)

Messaggi e gestione degli errori: permanent errors


20150326_095931_server_error_msg

Abbiamo detto che ci sono errori ed errori. Un permanent error, nel mondo della messaggistica, si verifica (tipicamente) quando un messaggio non può essere processato.

Spesso nel mondo della messaggistica si sente parlare anche di poison message, i due vanno a braccetto. Un poison message è una forma di permanent error.

OK, ma che cosa significa?

Significa ad esempio avere un messaggio in una coda di ingresso e il messaggio è malformato. Il fatto che il messaggio sia malformato comporta che la deserilizzazione fallisca ad ogni tentativo. Non è molto diverso da un documento di Word corrotto su disco, Word cerca di aprirlo, ma fallisce ogni tentativo.

E quindi?

Ovviamente un messaggio di questo genere non lo possiamo semplicemente lasciare nella coda, sarebbe un buon modo per fare Denial of Service. Un messaggio allo stesso tempo non può essere perso, ha quindi molto senso spostarlo in un altro storage persistente in modo da permettere intervento umano finalizzato, per esempio, a un tentativo di recupero.

Generalmente i trasporti hanno un concetto chiamato Dead Letter Queue (DLQ), il cui scopo è proprio permettere di fare persistenza in maniera permanente se servisse die poison message. Una DLQ è quindi una coda come tutte le altre con un compito ben preciso: conservare i messaggi che non possono essere processati.

Ma non tutti gli errori nascono uguali

Oltre alle DLQ i sistemi di messaggistica si appoggiano anche alle così dette code di errore. che hanno uno scopo leggermente diverso.

Alla prossima :-)

author: Mauro Servienti | posted @ mercoledì 18 gennaio 2017 7.27 | Feedback (0)

…e se i messaggi falliscono?


3415590901_b3fb25fcca_b

Abbiamo già detto per cosa non ha senso usare un messaggio. Una cosa fondamentale da comprendere, se il vostro sistema è basato su messaggi e code, è i messaggi non possono essere persi. Perdere un messaggio equivale a corrompere il sistema.

Errori

Ci sono tre tipologie di errore:

  • permanent: un messaggio non può essere processato, ad esempio la deserializzazione fallisce;
  • transient: un messaggio può essere processato ma una risorsa necessaria non è disponibile, riprovare potrebbe essere la soluzione;
  • business: un messaggio può essere processato ma viola le regole di business;

Approfondiremo il cosa, il come e il perché, nei prossimi post.

author: Mauro Servienti | posted @ lunedì 16 gennaio 2017 11.05 | Feedback (0)

Il linguaggio è importante.


Alessandro Melchiori da qualche settimana millanta che avrebbe scritto dei post inerenti all’argomento Services UI Composition, ecco millanta :-D

Forza Ale pubblica qualcosa!

Per cercare di smuovere ancora un po’ le acque sto preparando una nuova sessione che al momento è così definita:

Tutti i nostri aggregati sono sbagliati

Inizia sempre tutto bene, il requisito è semplice e l'implementazione procede senza intoppi. Poi i requisiti aumentano e ci ritroviamo con una strana sensazione allo stomaco e con la necessità di introdurre alchimie tecnologiche che non ci piacciono, ma non sappiamo perché.

Prenderemo una funzionalità tanto semplice, quanto usata, come il carrello di un e-commerce e proveremo a capire se è veramente così semplice. Guarderemo il problema tecnico che volgiamo risolvere e poi sposteremo l'attenzione sui requisiti di business. Requisiti che una volta compresi a fondo ci porteranno a capire quali sono le vere responsabilità del dominio.

Diciamo che il primo obiettivo è stimolare il solito Melchiori a scrivere, il secondo è più nobile e parte dal seguente snippet di codice:

var order = repository.GetById<OrderAggregate>( 44 );
order.Ship();
repository.CommitChanges();

Non vi suona male?

Provate a leggere in italiano quello che c’è scritto li, io leggo:

Archivio dammi l’ordine 44, ordine 44 spedisciti.

Quel design secondo me è conseguenza di un errore. L’errore di fondo è che pensiamo prima al problema tecnologico e poi forse a quello di design.

Diciamocela tutta: un ordine non si spedisce da solo.

Quando ci hanno detto Data & Behavior la & ci ha fatto sbombare il parser e ci siamo fermati a Data, un po’come i famosi hack dei CSS per far digerire a IE robe che non digeriva :-)

Inoltre quel design, dal punto di vista SOA, comporta un altro problema: mischia le carte in tavola in termini di responsabilità. Supponendo che un ordine sia in grado di spedirsi da solo, al fine di poterlo fare dovrebbe:

  • sapere dove spedirsi e a chi
  • sapere come spedirsi

Entrambe le cose non credo siano di competenza dell’ordine.

Obiettivo della sessione sarà proprio spingere l’acceleratore su come SOA disegna il modello di dominio.

author: Mauro Servienti | posted @ venerdì 6 gennaio 2017 10.28 | Feedback (0)

Un messaggio non è per…


Quando si parla di messaggi, intesi come messaggi su una coda, c’è una cosa molto importante da comprendere:

I messaggi sono sempre e solo one-way, fire & forget.

A prescindere dal pattern di alto livello che stiamo usando, sia esso pub/sub o request/response, il mittente parla con una coda, il destinatario parla a sua volta con una coda, ma mittente e destinatario non sanno nulla l’uno dell’altro.

Two Generals' Problem

Questa puntualizzazione è molto importante perché introduce il noto problema dei due generali. Che in soldoni ci dice che il mettente non ha nessun modo di sapere con certezza se il messaggio sia stato consegnato al destinatario. Non esiste un concetto di ACK da parte dell’infrastruttura, e se anche lo inventiamo, sarebbe a sua volta un messaggio che a sua volta potrebbe non arrivare.

Perché vi sto dicendo tutto ciò?

Perché mi capita spesso, troppo spesso, di vedere cose del tipo:

queue.Send( message );
locker.Lock( message.id );

locker.Lock crea e usa un manual reset event, a questo punto questo thread è a tutti gli effetti congelato. E da qualche altra parte:

var message = queue.Deque();
locker.Unlock( message.id );

O, forse peggio, cose come:

var message = new QueryCustomers()
{
   Filter = ….
};
queue.Send( message );
locker.Lock( message.id );

Vengono quindi usati messaggi e una coda per leggere informazioni, a tutti gli effetti eseguire query. Oppure come nel primo caso vogliamo a tutti i costi metterci una coda ma non siamo disposti a ripensare il processo per essere totalmente asincrono, e ci inventiamo pastrocchi tecnologici per mimare un approccio procedurale.

Due ordini di problemi

Il problema dei due generali ci dice chiaro e tondo che potremmo aspettare a vita. Il destinatario potrebbe essere giù, o fallire nel processare il messaggio e spostarlo in una coda di errore, di fatto non rispondendo mai.

Il secondo problema, tipico dell’usare messaggi per leggere informazioni, è derivante dall’infrastruttura e dal concetto di quota. Come ci dicono chiaramente le 8 fallacies of distributed computing la latenza non è zero e la banda non è infinita, ergo i trasporti devono proteggersi dalle cazzate degli sviluppatori e impongono limiti, spesso molto stringenti, sulle dimensioni dei messaggi. Ne consegue che aspettarsi di poter ritornare i risultati di una query non è cosa buona e giusta.

Concludendo

Non usate request/response, e in generale una coda e messaggi, per leggere informazioni. Ci hanno dato HTTP, e tanti altri bei trasporti e protocolli  per farci qualche cosa, usateli :)

Lo scopo di messaggi su una coda è altro: è inviare comandi e notificare variazioni di stato pubblicando eventi.

author: Mauro Servienti | posted @ giovedì 5 gennaio 2017 7.16 | Feedback (0)

Solution mode, ovvero: quanto è importante comprendere il problema.


Questo va un po’ nella stessa direzione dell’ascoltare per rispondere.

  • Qualcuno racconta quello che per lui è un problema
  • L’interlocutore si mette immediatamente in modalità soluzione
  • …è tutto finito :)

Il mettersi in “modalità soluzione” comporta un problema interessante: non analizziamo il problema, diamo per scontato che quello che ci è stato raccontato sia il vero problema che deve essere risolto. Non sto parlando in nessun modo di malafede: chi racconta, racconta la sua visione del mondo e in quanto tale la sua verità, ma non sta scritto da nessuna parte che il punto di vista di chi racconta sia quello giusto, sia esaustivo, e soprattutto abbia preso in considerazione tutte le circostanze e possibilità.

È quindi essenziale dedicare tanto, ma veramente tanto, tempo a comprendere a fondo il problema prima di iniziare a pensare a una soluzione. Rischiamo di risolvere il problema sbagliato, o peggio risolviamo quello che non è un problema.

Quante volte mi capita di ascoltare tecnici che dicono una cosa del tipo:

Il processo (inteso come processo di business) che abbiamo implementato:

  1. Non può funzionare in nessun modo senza transazioni
  2. Non è possibile che i messaggi arrivino senza un ordine noto

Spesso quelle affermazioni nascondono un problema più profondo: il processo non è stato compreso a fondo e le alchimie tecnico/tecnologiche ne sono la conseguenza.

author: Mauro Servienti | posted @ martedì 3 gennaio 2017 7.19 | Feedback (1)

Non ascoltare per rispondere


Ripete con me, almeno, una decina volte: Non ascoltare per rispondere.

Moltissime persone sono impazienti di dare una risposta, troppo impazienti. L’impazienza (spesso accompagnata dalla voglia di prevaricare) porta inevitabilmente a smettere di ascoltare chi parla. Ci concentriamo immediatamente sulla risposta, perdendo di vista quale è il verso scopo della conversazione:

Comprendere il punto di vista altrui.

Fate un favore a voi stessi, e al vostro interlocutore: ascoltate per comprendere, non per rispondere.

author: Mauro Servienti | posted @ venerdì 30 dicembre 2016 8.52 | Feedback (0)

Le “projection” non esistono


Vorrei lanciare un’altra provocazione, simile a i “fat event” non esistono: ci serve veramente il modello in lettura?

Non ne sono più così sicuro. Seguitemi.

Se modelliamo il nostro requisito affidandoci alla consistenza eventuale come unica soluzione del problema, allora si abbiamo bisogno sia dei fat event che delle projection.

Ma se la composizione delle informazioni avviene a livello di UI, è la UI stessa che rappresenta la projection in questione; riprendiamo un esempio che abbiamo già usato:

image

Ora, tentazioni.

La prima tentazione è di avere, nel nostro mondo, una cosa del tipo:

{
   ProductId: “”,
   ImageUrl: “”,
   Delivery: {
      Date: “”,
      Status: “”,
      Address: “”,
      Steps: [ … ]
   },
   OrderId: “”,
   OrderDate: “”,
   ShipmentDate: “”
}

Quella qui sopra è una projection atta a soddisfare il bisogno della UI. Non c’è nulla di sbagliato, la domanda è: ci serve veramente?

Probabilmente no.

Se abbiamo decomposto correttamente il nostro mondo probabilmente no. Possiamo modellare la pagina li sopra lasciando che ognuno dei servizi sia responsabile delle informazioni che gestisce e lasciando che sia la pagina a comporre quello che vediamo:

Quello che stiamo guardando è lo stato di una spedizione

{
   ShipmentDate: “”,
   DeliveryAddress: “”,
   OrderId: “”,
   ProductId: “”
}

Quando è stato spedito e cosa; in base a cosa possiamo recuperare le informazioni che ci servono sull’odine:

{
   OrderId: “”,
   OrderDate: “”,
   ProductId: “”,
}

e le informazioni sul prodotto

{
   ProductId: “”,
   ImageUrl: “”
}

infine le informazioni sullo stato della spedizione:

{
   OrderId: “”,
   Delivery: {
      Date: “”,
      Status: “”, 
      Steps: [ … ] 
}

Ovvio che il titolo è provocatorio e probabilmente ovvio che i piccoli modelli definiti in questo post sono a loro volta delle projection che danno una visione parziale e atta a soddisfare il requisito in questione. Come ho già avuto modo di dire le projection miste sono uno smell.

Ci sono dei casi in cui probabilmente sono un male necessario. Stay tuned.

Post in questa serie:

author: Mauro Servienti | posted @ mercoledì 28 dicembre 2016 7.12 | Feedback (4)

168, ovvero tre consigli per gestione del tempo


Sono le ore in una settimana, se siamo tra quelli il cui mantra è “non ho tempo”, probabilmente è solo perché non siamo capaci di organizzarle al meglio.

image

How to gain control of your free time è molto interessante e vale i 12 minuti che si porta via.

Tre consigli che con me funzionano:

  1. pianificate quello che dovete per forza fare
  2. date una priorità a quello che vorreste fare
  3. mettetevi l’anima in pace e accettate il fatto che non si può arrivare dappertutto

author: Mauro Servienti | posted @ lunedì 26 dicembre 2016 18.47 | Feedback (0)