Come recuperare dallo stato di lavoro incoerente senza polling del database

5

Sto lavorando per ridimensionare un'applicazione che sta attualmente interrogando un database mySQL per inviare lavori asincroni a un sistema di elaborazione in background.

Una macchina a stati viene utilizzata per tenere traccia dell'avanzamento delle entità durante un flusso di lavoro. Ad esempio, potremmo avere tre stati:

  • In programma
  • Lavorazione
  • Completa

Il mio piano è di aggiungere un sistema di code di messaggi per mediare i lavori inviati al sistema di elaborazione in background. Quindi Application A inserirà una nuova entità, quindi invierà un messaggio alla coda. Application B consumerebbe questi messaggi e li indirizzerebbe al processo di elaborazione in background corretto.

def do_work(entity)
  # Precondition check
  raise "Wrong State" unless entity.scheduled?

  # Update state to processing
  entity.processing

  # ... do work

  # Update state to complete
  entity.complete
end

Dato un lavoro come sopra, sto avendo difficoltà a determinare come sarebbe possibile recuperare da una situazione in cui si è verificato un errore tra le transizioni di evento processing e complete . Ad esempio un crash del processo.

Il processore di lavoro riproverà il lavoro, ma ora l'entità si trova in uno stato incoerente e fallirebbe.

Come potrei gestire questo caso senza riportare al polling della tabella delle entità alla ricerca di processi obsoleti?

Modifica

Ci sono due concetti diversi in gioco qui. Abbiamo i dati e abbiamo lo stato dei dati in relazione all'esecuzione del lavoro (pianificato / di elaborazione) + lo stato del flusso di lavoro (completo).

Entrambe queste informazioni si trovano nella stessa tabella. Quindi, dopo che i dati sono cresciuti, il processo di polling diventa inefficiente (letture / aggiornamenti / inserimenti costantemente in corso).

Quindi forse una soluzione è avere Application B spostare i lavori attivi in un datastore separato. Quindi, quando il task "clean up" a cui si riferisce @ Ӎσᶎ è in esecuzione, il set di dati dovrebbe essere molto più piccolo.

In definitiva, sembra che non ci sia modo di evitare il polling del database per garantire che i dati siano uno stato corretto.

    
posta Karl 26.03.2014 - 03:17
fonte

1 risposta

1

Lo considererò più sotto forma di un piano di gestione dei rischi rispetto a un progetto di programma, perché è qui che deve iniziare la progettazione del programma.

Sembra che ci siano due canali di comunicazione: il database e una coda di messaggi. Assumiamo che il database sia accurato, poiché se fallisce normalmente richiederà un intervento manuale. Ma tutto il resto può fallire, anche in circostanze improbabili (incendi!)

Il flusso normale sembra essere:

process a: 
     (1) create job
     (2) add to database
     (3) send message
process b: 
     (4) message received 
     (5) get job from database 
     (6) process job 
     (7) mark complete in database

Probabilmente ognuno di questi passaggi può fallire. "Crea lavoro" potrebbe generare un lavoro non valido (che non può essere completato, ad esempio), oppure potrebbe non riuscire a recuperare le informazioni richieste. Quindi probabilmente vorrai una sorta di meccanismo di segnalazione degli errori (no, davvero:)

Si noti che uno di questi potrebbe essere risolvibile riprovando (i dati non sono disponibili), ma l'altro probabilmente vorrebbe un meccanismo di rilevamento degli errori - l'attività di "ripulitura". E la pulizia ha bisogno di classificare i problemi come risolvibili o meno, idealmente avendo il codice per risolverli integrati. In questo modo il suo output è normale flusso di flusso e report di errore, proprio come ogni altra parte del programma.

Suggerisco di passare attraverso il tuo codice e almeno di notare mentalmente la gerarchia dell'elaborazione degli errori che hai già, da "se questa stringa può essere trasformata in un intero fare ..." fino a "se c'è un'eccezione non gestita da qualche parte in il programma". Il codice "clean up" è più strettamente allineato a questo rispetto alla normale esecuzione. È simile al codice di verifica dell'heap che programmi come valgrind hanno: lo esegui periodicamente per assicurarti che lo stato del tuo programma sia corretto.

In un modo o nell'altro si finirà con una discussione o processo da qualche parte che pianifica i lavori. All'inizio (la tua domanda parla della pianificazione dei lavori piuttosto che della semplice messa in coda) o semplicemente per eseguire l'operazione di pulizia. Se quest'ultimo è possibile essere in grado di eseguirlo come un eseguibile separato tramite cron (suggerisco che Windows non venga coinvolto nell'equivalente, solo il mio ricordo personale di essere un PITA da usare a livello di programmazione. Una delle parti di "ripulire" è assicurarsi che l'attività pianificata esista)

Potresti essere in grado di ridurre la frequenza dell'attività di pulizia attivandola manualmente dai passaggi precedenti quando ci sono determinati tipi di errori (ad esempio, se l'elaborazione di un lavoro richiede più tempo di quanto consentito abbandonarla e chiamare clean up). Se riesci a renderlo abbastanza leggero, potresti riuscire a farla franca ogni volta che si esegue il passaggio 5 sopra, ma ti suggerisco di non farlo anche se inizialmente. O almeno provalo con diversi anni di dati in tutti i tuoi tavoli.

Ma a prescindere, suggerisco di eseguirlo all'avvio del programma quindi giornalmente o giù di lì, solo per cogliere cose che non hai ancora pensato. Come, cosa succede quando il tuo programma si spegne? La velocità con cui esce quando il computer viene spento e può lasciare un lavoro "in pausa" per riprendere o ha il tempo di reimpostare il lavoro su "pronto per essere eseguito"? Cosa fare se l'attività del database per reimpostare il processo scade perché il database è in fase di arresto anche?

(modifica: lo spostamento dei dati dalla tabella "coda" una volta che viene elaborato accelera le cose, e molto più nel tempo man mano che aumenta il numero di lavori elaborati. A seconda della quantità di dati e del numero di errori, la pulizia ha a che fare con esso può essere possibile aggiungerlo ai passaggi "get job" o "mark complete", ma questo è per really semplici lavori).

    
risposta data 26.03.2014 - 22:57
fonte