2012-04-02 11 views
7

Esiste un modo per assicurare il comportamento FIFO (first in, out out) con le code di attività su GAE?Esiste un modo per assicurare il comportamento FIFO (first in, first out) con le code di attività su GAE?

GAE documentazione dice che FIFO è uno dei fattori che influenzano l'ordine di esecuzione delle applicazioni, ma la stessa documentazione dice che “la pianificazione del sistema potrebbe 'saltare' nuovi compiti alla testa della coda” e mi hanno confermato questo comportamento con un test. L'effetto: i miei eventi sono in fase di elaborazione fuori servizio.

Docs dice:

https://developers.google.com/appengine/docs/java/taskqueue/overview-push

L'ordine in cui vengono eseguiti i compiti dipende da diversi fattori:

La posizione del compito nella coda. App Engine tenta di elaborare le attività in base all'ordine FIFO> (first in, first out). In generale, le attività vengono inserite alla fine di una coda e eseguite dal capo della coda.

L'arretrato delle attività in coda. Il sistema tenta di fornire la minima latenza possibile per qualsiasi attività specifica tramite notifiche appositamente ottimizzate per lo scheduler. Pertanto, nel caso in cui una coda abbia un ampio backlog di attività, la pianificazione del sistema può "saltare" nuove attività in testa alla coda.

Il valore della proprietà etaMillis dell'attività. Questa proprietà specifica l'ora che un'attività può essere eseguita. App Engine attende sempre fino all' dopo l'ETA specificato per elaborare le attività push.

Il valore della proprietà countdownMillis dell'attività. Questa proprietà specifica il numero minimo di di attesa prima di eseguire un'attività. Conto alla rovescia ed eta si escludono a vicenda; se ne specifichi uno, non specificare l'altro.

Cosa devo fare? Nel mio caso d'uso, elaborerò 1-2 milioni di eventi/giorno provenienti dai veicoli. Questi eventi possono essere inviati a qualsiasi intervallo (1 secondo, 1 minuto o 1 ora). L'ordine dell'elaborazione dell'evento deve essere garantito. Ho bisogno di elaborare per ordine di timestamp, che viene generato su un dispositivo incorporato all'interno del veicolo.

Cosa ho ora?

  1. Un servlet Rest che viene chiamato dall'utente e crea un'attività (i dati degli eventi sono su payload).

  2. Dopo questo, un servlet operaio ottenere questo compito e:

    dati
    • Deserialize evento;

    • Put Event su Datastore;

    • Aggiornamento veicolo su archivio dati.

Così, ancora una volta, non v'è alcun modo per assicurare solo il comportamento FIFO? O come posso migliorare questa soluzione per ottenere questo?

+1

Perché hai bisogno di un FIFO rigoroso? Ricorda che l'ordine degli eventi è un po 'confuso in un sistema distribuito - anche il fatto di dire quale delle diverse richieste quasi simultanee sia avvenuta per prima è difficile (e spesso inutile) quando i tuoi frontend e backend sono distribuiti su più macchine. Cosa stai cercando di ottenere, come risultato finale? –

+0

Monitoriamo gli autobus pubblici, quindi abbiamo bisogno di sapere quando si è fermato in una fermata dell'autobus, o quando ha aperto un viaggio, o quando ha superato i limiti di velocità. Il problema è che alcuni eventi sono correlati a eventi precedenti perché facciamo anche la gestione dello stato. Ad esempio: un autobus può solo "aprire un viaggio" se nell'evento precedente ha registrato un "viaggio chiuso". Quindi, puoi immaginare cosa può accadere se ottengo questi eventi fuori ordine ... –

+0

Una coda di attività non sembra il modo migliore per farlo in ogni caso. Quello che si vuole fare è utilizzare le transazioni di archivio dati e limitare l'insieme di transizioni di stato per un dato bus solo a quelle consentite dalla macchina a stati. –

risposta

3

Ok. Questo è come l'ho fatto.

1) Rest servlet that is called from the consumer: 

    If Event sequence doesn't match Vehicle sequence (from datastore) 

     Creates a task on a "wait" queue to call me again 

    else 

     State validation 

     Creates a task on the "regular" queue (Event data is on payload). 


2) A worker servlet gets the task from the "regular" queue, and so on... (same pseudo code) 

In questo modo è possibile sospendere la coda "normale" per eseguire una manutenzione dei dati senza perdere eventi.

Grazie per le vostre risposte. La mia soluzione è un mix di loro.

3

Penso che la risposta semplice sia "no", tuttavia in parte per aiutare a migliorare la situazione, sto usando una coda di pull - tirando 1000 attività alla volta e poi ordinandole. Se il tempo non è importante, è possibile ordinarli e inserirli nell'archivio dati e quindi completare un lotto alla volta. Devi ancora capire cosa fare delle attività all'inizio e alla fine del batch, perché potrebbero essere fuori servizio con attività di interleaving in altri batch.

+0

Il tempismo è importante. Ho dimenticato di aver bisogno di elaborare l'ordine di timestamp, che viene generato su un dispositivo incorporato all'interno del veicolo. –

0

È possibile inserire il lavoro in una riga nel datastore con un timestamp di creazione e quindi recuperare le attività di lavoro in base a tale timestamp, ma se le attività vengono create troppo rapidamente si verificheranno problemi di latenza.

0

Non conosco la risposta da solo, ma potrebbe essere possibile eseguire le attività in coda utilizzando una funzione posticipata nell'ordine inoltrato. Probabilmente avrai bisogno di un ingegnere di G. per ottenere una risposta. Tirare le code come suggerito sembra una buona alternativa, in più questo ti permetterebbe di considerare il batching dei tuoi put() s.

Una nota sui contatori semplificati: aumentano la probabilità di aumentare in modo monotono gli ID, ma non li garantiscono.

+0

posticipato utilizza la coda dei task. –

+0

Giusto, non sapevo se ci sarebbe potuto essere un po 'di magia sulle attività che entravano in coda da una funzione posticipata rispetto ad un accodamento standard. Sembra che la risposta sia "No", che è quello che avrei immaginato. – stevep

4

è necessario avvicinarsi questo con tre fasi distinte:

  1. implementare un Sharding Counter per generare una monotona crescente ID. Per quanto mi piaccia usare il timestamp dal server di Google per indicare l'ordine delle attività, sembra che i timestamp tra i server GAE possano variare di più di quanto richiesto.

  2. Aggiungi le tue attività a Pull Queue anziché a Push Queue. Quando crea il tuo TaskOption, aggiungi lo ID ottenuto dal passaggio 1 come tag. Dopo aver aggiunto l'attività, memorizzare lo ID da qualche parte sul datastore.

  3. Chiedi al tuo servlet di lavoro lease Tasks by a certain tag dal Pull Queue. Interroga il datastore per ottenere il primo ID che è necessario recuperare e utilizzare come il leasing tag. In questo modo, è possibile simulare il comportamento FIFO per la coda delle attività.

Dopo aver terminato la vostra elaborazione, eliminare il ID dal vostro archivio dati, e non dimenticate di eliminare il Task dal Pull Queue troppo. Inoltre, ti consiglio di eseguire i tuoi consumi di attività sul back-end.

AGGIORNAMENTO: Come notato da Nick Johnson e mjaggard, la separazione nel passaggio n. 1 non sembra essere praticabile per generare un ID in aumento monotono e sarebbero quindi necessarie altre fonti di ID. Mi sembra di ricordare che stavi usando i timestamp generati dai tuoi veicoli, sarebbe possibile usare questo al posto di un ID monotonicamente crescente?

Indipendentemente dal modo di generare gli ID, l'idea di base è quella di utilizzare il meccanismo di query di archivio dati per produrre un ordinamento FIFO di Tasks, e utilizzare compito Tag per tirare compito specifico dal TaskQueue.

C'è un avvertimento, però. A causa dell'eventuale coerenza della politica di lettura sui datastore ad alta replica, se si sceglie HRD come datastore (e si dovrebbe, l'M/S è deprecato dal 4 aprile 2012), potrebbero esserci dei dati non aggiornati restituiti dalla query su passo 2.

+0

È effettivamente possibile creare ID in modo monotono aumentante utilizzando contatori di tipo sharded? Ho pensato di no - la funzione per l'assegnazione di id nel datastore api di basso livello può generare id ma potrebbe non essere nell'ordine stretto che sospetto. – mjaggard

+0

@mjaggard: Grazie, ma non sono abbastanza sicuro di capire cosa volevi dire ... I contatori Sharded non usano la funzione ID di generazione dal datastore di basso livello. Fondamentalmente, siamo noi a incrementare i contatori da soli, ma diffondiamo il "contatore" su diverse entità per ridurre il conflitto di scrittura. Pertanto, i contatori aumenterebbero monotonicamente. Per favore, cmiiw, naturalmente. –

+1

Sì, non è possibile generare ID in modo monotono aumentando con contatori a contrappesi divisi su più gruppi di entità, quindi non c'è modo di garantire che qualsiasi numero generato con uno sia univoco. I contatori di Shard sono per il conteggio delle cose, non per l'assegnazione degli ID. –

0

Il modo migliore per gestire questo, il modo distribuito o "App Engine way" è probabilmente quello di modificare l'algoritmo e la raccolta dati per lavorare con un semplice timestamp, consentendo un ordinamento arbitrario delle attività.

Supponendo che questo non è possibile o troppo difficile, è possibile modificare l'algoritmo come segue:

  1. quando si crea il compito non mettere i dati sul carico utile, ma nel datastore, in una sorta di un ordinare i timestamp e conservarli come entità figlio di qualunque entità tu stia cercando di aggiornare (Veicolo?). I timestamp dovrebbero provenire dal client, non dal server, per garantire lo stesso ordine.

  2. eseguire un'attività generica che recupera i dati per il primo timestamp, li elabora e quindi li elimina all'interno di una transazione.

0

A seguito di questa discussione, io sono chiaro se il requisito FIFO rigorosa è per tutte le transazioni ricevute, o su una base per-veicolo. Latter ha più opzioni contro ex.

Problemi correlati