Come creare un'architettura di eventi a tempo utilizzando un database SQL

7

Il titolo della mia domanda è generale perché ritengo che questo problema sia di natura generale, ma per impostare il livello fornirò un esempio specifico.

Utilizziamo un motore di flusso di lavoro locale che è guidato dalle tabelle del database. All'interno di queste tabelle si nasconde un grafico diretto che rappresenta il flusso di lavoro. Il grafico contiene fasi e attività; viene tracciata una linea tra due nodi Stage e il nodo Attività risultante contiene il codice da eseguire. Usiamo CSScript per compilare ed eseguire il codice al volo.

All'interno del flusso di lavoro, i record di attività rappresentano il lavoro da eseguire. Ogni attività contiene alcuni metadati rilevanti in formato XML. I record delle attività attraversano il grafico diretto e il codice viene eseguito mentre l'attività passa attraverso l'attività. Quindi in ogni momento, ogni fase potrebbe contenere x numero di compiti, in attesa di essere eseguiti su un'attività.

Per eseguire un'attività su un'attività, è necessario programmarla. Un record di pianificazione contenente un datetime, un taskid, stageid e activityid determina quando e dove questa operazione verrà eseguita successivamente. Periodicamente, eseguiamo una query che restituisce i record di pianificazione dovuti, quindi, per ogni record restituito, alziamo un'istanza di attività e la eseguiamo, assegnandole il record dell'attività come parametro.

Questa query era utilizzata per eseguire 10 volte al secondo. Di recente, ho aggiunto un codice che conta quante volte la query non restituisce alcun record e, se questo conteggio arriva a 60, riduco l'intervallo di query a una volta al secondo e ricomincio a contare. Se il conteggio raggiunge ancora 60, riduca l'intervallo a una volta al minuto. Se viene visualizzato un record nel risultato della query, imposto l'intervallo su 10 volte al secondo e ricomincio il processo di conteggio. L'effetto netto è che la tabella degli orari viene rapidamente interrogata durante i periodi di attività intensa e viene sottoposta a sondaggi scarsamente durante i periodi di tranquillità. Ci aspettiamo di risparmiare poche centinaia di dollari per istanza di Azure al mese, solo da questa semplice modifica.

Quindi ecco la mia domanda.

Questo è ovviamente un modello di sondaggio. C'è un modo per renderlo "event-driven," in modo che il database venga colpito solo quando è necessario un record di pianificazione, senza dover eseguire il polling continuo del database?

    
posta Robert Harvey 26.10.2016 - 17:34
fonte

6 risposte

4

La soluzione generale è utilizzare un database che supporti le notifiche asincrone. Diversi fanno:

  • Oracle : consente la registrazione per la notifica delle modifiche agli oggetti (notifica di modifica dell'oggetto o OCN) e le modifiche nei risultati delle query specificate (notifica di modifica dei risultati della query o QRCN).
  • PostgreSQL - Notifica semplice contenente un tag e un payload opzionale generato utilizzando l'istruzione NOTIFY come comando autonomo o come parte di una funzione. (Quest'ultimo potrebbe essere parte di un trigger.) I client possono iscriversi alle notifiche emettendo un'istruzione LISTEN e select ing sull'handle di connessione (esattamente come varia con il binding di lingua).
  • SQL Server - Sistema di accodamento incorporato in cui i client possono utilizzare una combinazione delle istruzioni WAITFOR e RECEIVE per ascoltare gli eventi. Può anche avere (o aver avuto) OCN / QRCN simile a Oracle.
  • Sybase : ha procedure registrate che consentono l'invocazione di callback sui client se sono stati richiesti. (Non positivo su questo.)

Se sei bloccato con uno di quelli che non lo fanno (MySQL, DB2), dovrà essere fatto fuori banda usando uno dei metodi descritti nelle altre risposte.

Una volta che hai un metodo per il database per informarti che qualcosa è cambiato, puoi fare una query che determina quanto tempo deve trascorrere prima che si verifichi l'evento successivo e poi aspettare tanto a lungo per una notifica. Se ricevi una notifica, ripeti il ciclo di query / attesa. Se non ricevi una notifica, significa che il tempo che hai calcolato è arrivato ed è ora di fare qualsiasi cosa detta l'evento. Questo dovrebbe portarti al punto in cui stai interrogando il database solo quando sai per certo che qualcosa deve accadere.

    
risposta data 27.10.2016 - 13:53
fonte
1

Le query ripetute su un database per un programma a scadenza, in particolare quando si esegue il polling più volte al secondo, indica che si trarrebbe grande vantaggio da una cache in memoria degli oggetti di pianificazione.

Supponendo che i server delle applicazioni siano scalabili orizzontalmente e carichi il carico, quando un nodo nel cluster è in linea, è possibile eseguire un'inizializzazione per creare la coda del thread globale. Il mantenimento di ciascuna attività di pianificazione in una coda in memoria ha senso poiché sono dati ordinabili. In un primo momento, ciò costituirebbe una singola query di database per ciascun processo del nodo.

Esegui il polling della coda

Il costo dell'operazione di polling è essenzialmente una sbirciatina al primo elemento nella coda ordinata. La coda in memoria di questa operazione può essere misurata in nanosecondi. Se il primo articolo è scaduto, ora dovrebbe iniziare questo processo.

Generazione di attività basate su eventi

Questo è dove qualcosa come MQ può essere utile. Quando si disattiva la successiva attività della coda, è possibile inserire un messaggio in un MQ con i dettagli dell'attività. Una serie di processi di generazione di istanze di attività può essere ascoltata su questa coda, il che significa che il nodo più disponibile o più veloce da recuperare otterrà il messaggio e sarà responsabile per l'esecuzione dell'attività.

Quali sono le nuove attività pianificate?!

Usa un altro MQ con un processo di ascolto diverso per aggiungere nuove attività pianificate nel sistema. Sono questi processi che si prenderanno la responsabilità di aggiornare le tabelle del database con i nuovi programmi e attività. È comunque necessario aggiornare tutti gli elenchi dei processi in memoria. Ci sono vari modi in cui potresti essere in grado di raggiungere questo obiettivo, ma qualcosa come un argomento è un'ottima soluzione per un simile caso d'uso.

Ulteriori informazioni sugli argomenti qui: link

Ogni processo di nodo può iscriversi al tuo argomento che utilizza per applicare nuove attività pianificate alla sua coda di memoria.

Perché questo è un ottimo approccio?

Ha una certa complessità, ma la parte importante è che è scalabile, resiliente, efficiente e rapidamente recuperabile. I nodi possono essere rilasciati o aggiunti, e il database viene semplicemente usato come un libro mastro che ottiene un nuovo nodo correttamente inizializzato in modo che possa iniziare a contribuire.

    
risposta data 26.10.2016 - 20:09
fonte
1

Questa non è una soluzione guidata dagli eventi, ma penso che potrebbe essere una possibile soluzione alternativa al tuo particolare problema.

Mi sembra che il problema in cui ti stai imbattendo sia tipico del compromesso rischio / rendimento che incontri ogni volta che decidi se archiviare qualche informazione in memoria volatile o non volatile. La memoria non volatile può essere meno costosa e più sicura ma il recupero dei dati richiede molto più tempo e generalmente le dimensioni sono limitate dai costrutti di sistema.

Questi metadati che descrivi per ogni attività sembrano certamente essere salvati nel tuo database come gli oggetti programmati a lungo termine. Ma nel tuo post descrivi un processo che esegue continuamente la scansione del tuo database per sapere cosa eseguire successivamente. Questa coda immediata è sicuramente cruciale per la tua applicazione, ma non mi sembra che debba necessariamente essere persist dopo che l'applicazione è stata chiusa per la notte. La tua app ha solo bisogno di sapere cosa eseguire ora e poi andare avanti.

Potrei fare luce su quello che potrebbe essere un refactoring significativo, ma mi chiedo se non puoi spostare quella parte del tuo scheduler dal database e fino al livello dell'applicazione sotto forma di una struttura dati. Se invece di eseguire il polling del database direttamente per tutte le attività pianificate, l'operazione è avvenuta solo una volta ogni ora (o qualsiasi segmento temporale definito) per identificare attività pianificate a lungo che devono essere eseguite durante il segmento successivo e posizionarle in un struttura dei dati a livello di applicazione, questo ridurrebbe in modo significativo la quantità di chiamate al database che la vostra applicazione avrebbe bisogno di fare. Lo scheduler potrebbe quindi eseguire il polling di questa struttura di dati memorizzata localmente direttamente per eseguire attività immediate. Questo ovviamente richiederebbe che la classe scheduling fosse a conoscenza di questo segmento del tempo di elaborazione e inserisca immediatamente tutte le attività pianificate nella struttura dei dati (al contrario del database).

Tuttavia, il risultato finale sarebbe essenzialmente lo stesso sistema in uso al momento, tranne il fatto che si limita il database per la memorizzazione a lungo termine sfruttando meglio il livello dell'applicazione per l'elaborazione immediata.

    
risposta data 27.10.2016 - 15:39
fonte
0

L'unica cosa che posso pensare è usare chiamate di servizi web di qualche tipo per notificare in modo proattivo al processo di Watcher che un'attività deve essere eseguita.

Facendo un ulteriore passo avanti, è possibile utilizzare un sistema di notifica come RabbitMQ per inviare un messaggio a una coda continuando le informazioni richieste per eseguire tale attività. The Watcher può iscriversi a questa coda e colpire il database solo quando c'è qualcosa da elaborare.

Detto questo, avresti bisogno di un modo per eseguire un'attività in sospeso nei casi in cui il server RabbitMQ va giù - questo aumenta la necessità che il tuo sistema sia altamente fault-tolerant.

    
risposta data 26.10.2016 - 17:42
fonte
0

A quanto ho capito, un'attività deve essere eseguita perché

  • Ora è necessario eseguire
  • Oppure è appena stato creato da un'altra attività
  • Oppure è appena passato al passaggio successivo ed è pronto per essere eseguito

Ti interessa che un'attività che deve essere eseguita, venga eseguita alcuni secondi dopo il momento in cui dovrebbe essere eseguita? (Sto assumendo non)

Quindi eseguire la query per trovare nuove attività ogni 60 secondi e ogni volta che si aggiunge alla tabella delle attività. Ciò si tradurrà in attività "avviate" appena create che sono veloci, ma molto meno polling.

Oppure usa SqlDependency per ricevere una notifica quando un'attività viene aggiunta alla tabella. Quindi la tua query ritorna a bassa lunghezza finché l'attività successiva non è scaduta, così come tutte le attività dovute.

    
risposta data 09.11.2016 - 18:41
fonte
0

Puoi risolverlo senza eseguire il polling.

Suppongo che tu sia interessato solo agli eventi temporali.

  • hai una tabella di database con tutti gli eventi temporali non eseguiti.
  • hai una query sql "getNextEvent" che restituisce il prossimo evento non ancora eseguito minimo-datetime.
  • getNextEvent viene eseguito ogni volta che la coda evento-datetime viene modificata o quando un'azione tempo-evento è terminata.
  • se questo evento-datetime è precedente a now (nel passato), l'evento è scaduto e può essere eseguito immediatamente.
  • se questo evento-data / ora è in futuro il tuo evento-tempo-egine può dormire fino a quel tempo datato.

La batteria della sveglia Android funziona in questo modo

    
risposta data 09.11.2016 - 19:32
fonte

Leggi altre domande sui tag