Programmazione ottimistica e pessimistica - Garantire più attività vengono chiamate una sola volta per record specifici

3

Supponiamo che tu abbia 10 record di database che devi elaborare nel modo seguente:

  1. Inizio
  2. Estrai 2 record dal database con il flag "Elaborato" impostato su "falso"
  3. Chiama il servizio web esterno con i dati personalizzati da questi 2 record.
  4. Aggiorna questi 2 record nel database e impostali come 'Elaborato' = 'vero'
  5. Vai a 1

Ora, cosa può accadere in questo scenario?

  1. -
  2. Si può verificare un errore mentre si estrae i record dal database
    • va bene, poiché nel prossimo round proveremo a riprovare.
  3. Si può verificare un errore durante la chiamata a un servizio Web esterno
    • va anche bene, poiché nel prossimo round proveremo a inviare nuovamente quei 2 record
  4. Si può verificare un errore durante la chiamata al database per l'aggiornamento del record elaborato
    • cosa fare ora? Sei ottimista sul fatto che questo non accadrà mai?
    • tieni presente che hai già inviato quei 2 record tramite il servizio web e non devi inviarli di nuovo a nessun costo
    • un'opzione è chiamare un aggiornamento del database in un ciclo finché non viene eseguito correttamente!?
  5. -

La domanda è, come (vorresti) gestire questa situazione?

** UPDATE **

Illustrazione del codice:

loop
{
    db = database
    ws = webservice

    trans = db.open_transaction

    recs = trans.query_for_two_records_and_update_processed_to_true

    ws_response = ws.send_text_messages(recs)

    if (ws_response == success)
    {
        trans.commit() // what would you do if this fails?
    }
    else
    {
        trans.rollback()
    }
}
    
posta HABJAN 19.03.2014 - 16:37
fonte

4 risposte

6

È sempre possibile arrivare a un caso ancora peggiore che rovini qualsiasi soluzione tu possa inventare. Assumi un servizio Web ostile o un database ostile e sei fregato.

Ma nello scenario che descrivi, ti serve principalmente un registro delle azioni più affidabile di un database esterno. Supponiamo che il file system locale sia affidabile se non si aumentano le dimensioni del file (che potrebbe facilmente fallire se il disco è pieno).

Quindi il programma dovrebbe, all'avvio, creare un piccolo file che contenga solo due record di dimensione fissa, ciascuno formato da uno spazio per un ID e un flag di stato. Inoltre, cambiamo il database in qualcosa di leggermente più elaborato: la bandiera elaborata ora può essere "nuova", "in corso" e "elaborata".

Allora vuoi qualcosa come questo pseudocodice:

txlog = disk struct { id, status }

record = db -> load record
txlog -> id = record.id, status = sending
db -> processed = in progress, on failure txlog -> clear and start over
web -> send request, on error response goto service error, on timeout abort
txlog -> status = sent
db -> processed = done, on failure abort
txlog -> clear

Il gestore degli errori di servizio ha il seguente aspetto:

txlog -> status = failed
db -> processed = new, on failure abort
txlog -> clear

Ora, la parte interessante viene dopo. Se si verifica un errore di db e si interrompe, o il programma scompare improvvisamente (interruzione dell'alimentazione), o se non si ottiene una risposta dal servizio Web (arresto del servizio Web o perdita di connettività), il programma ricomincerà alla fine e realizzerà che il txlog il file non è cancellato. Quindi deve entrare in modalità di ripristino degli errori.

Se il txlog ha lo stato "invio", controlla il record db. Se questo ha lo stato "nuovo", il tuo programma è morto prima che potesse inviare la richiesta. Cancella il log e procedi normalmente.

Se il record db ha lo stato "in corso", sei nei guai: significa che qualcosa è andato storto in un punto in cui potrebbe aver inviato la richiesta al servizio web e > potrebbe essere stato elaborato. Forse hai perso alimentazione o rete, o il servizio web, ma non puoi sapere se la richiesta è andata a buon fine. In questo caso, puoi solo avvisare un operatore umano di dare un'occhiata. Ne parlerò più tardi.

Se il txlog ha stato "inviato", il record è stato inviato. Se il db ha ancora lo stato "in corso", prova a reimpostare lo stato. In caso contrario, il programma è morto prima che potesse cancellare il txlog, oppure il db ha fatto il suo aggiornamento ma è scomparso senza inviare un messaggio di successo. In ogni caso, cancella il registro e prosegui.

Ma per quanto riguarda il caso di errore? Questo è il "caso peggiore" di cui ho parlato all'inizio, il vero problema del sistema inaffidabile con cui hai a che fare. Perdita di potenza nel momento sbagliato. Un cavo di rete che viene improvvisamente tagliato. Il tuo sistema deve adattarsi, e se, come nel caso che stai descrivendo, non può farlo (il servizio web che descrivi semplicemente non dà quell'opzione), sei fregato se accade il caso peggiore. Il processo che ho descritto ti avviserà almeno di ciò. L'operatore umano può quindi contattare gli amministratori del servizio Web per chiedere informazioni sui propri registri o, in caso contrario, prendere una decisione informata sulle operazioni da eseguire o meno.

Da una nota a margine, potremmo progettare, ad esempio, un servizio di messaggi di testo che sia idempotente? Sicuro. Il servizio richiederebbe, come primo passo in ogni processo, l'assegnazione di un GUID per il messaggio da inviare. Una volta ottenuto tale GUID, è possibile memorizzarlo nel registro delle transazioni. Quindi si invia il messaggio con il GUID. Il servizio può quindi verificare i propri record e scoprire che il messaggio con questo GUID è già stato inviato e quindi non inviarlo di nuovo. E anche se il servizio web non registra correttamente il fatto che ha inviato il messaggio, il ricevitore finale può fare lo stesso: il telefono, ad esempio, potrebbe vedere che ha già ricevuto un messaggio con quel GUID e ignora il nuovo.

    
risposta data 19.03.2014 - 17:47
fonte
3

Credo che i tuoi requisiti siano teoricamente impossibili da implementare.

Non c'è modo di verificare se il servizio web ha ricevuto i record? Qualche altra operazione di servizio? In tal caso, puoi controllare prima di inviare.

Controlla anche con il proprietario del servizio per vedere se possono rendere il loro servizio idempotente.

O imposta un processo manuale per gestire gli errori.

    
risposta data 19.03.2014 - 16:44
fonte
0

Dato che sei così sicuro di poter rielaborare il passaggio n. 3 (invio al servizio web), perché non puoi contrassegnare elaborato = true subito dopo il passaggio n. 2?

  1. avviare
  2. recupera 2 record non elaborati
  3. aggiornamento elaborato = true
  4. invia al servizio web.

Lo consiglio solo per l'importanza di non inviare mai più al servizio web più di una volta E sei così sicuro di poter continuare a rielaborare eventuali errori da inviare al servizio web.

    
risposta data 19.03.2014 - 17:21
fonte
0

Se non puoi usare le transazioni come ha detto @RobertHarvey, usa tre stati: 'unprocessed', 'processing' e 'processed'. Registra tutti gli errori in un registro persistente di qualche tipo (file), e se 3 o 4 falliscono, prova a reimpostarlo su "non elaborato".

E poi risolvi manualmente quelli bloccati in "elaborazione" utilizzando le informazioni nel registro.

    
risposta data 19.03.2014 - 18:12
fonte

Leggi altre domande sui tag