Come garantire la sequenza / ordine di esecuzione dello script Web o non le code parallele

1

Questa è una versione molto semplificata del problema:

Hai un intero salvato su un server chiamato "B" e hai uno script salvato su una chiamata al server "A" esposta a Internet. Lo script estrae il numero intero dal server B, ne aggiunge uno e quindi aggiorna il numero intero remoto. Nessuna di queste operazioni è atomica, il pull, l'aggiunta e l'aggiornamento, tutti hanno una latenza casuale.

Se c'è un solo utente che chiama lo script in sequenza, non ci saranno problemi e l'int verrà incrementato in modo coerente. Tuttavia, poiché lo script è esposto a Internet, può essere chiamato in parallelo da più utenti contemporaneamente, quindi mentre un'istanza dello script incrementerà il valore localmente prima di aggiornarlo da remoto, un altro estrarrà il valore effettivo dal server remoto, incrementando una vecchia versione di esso.

La mia soluzione è usare una coda (o almeno provare); Quindi, invece di chiamare lo script, gli utenti chiameranno un altro script che scriverà su un DB la richiesta di esecuzione (un messaggio; "per favore chiama script.php"). Dato che la scrittura nel DB non è atomica, sto anche scrivendo il microtime del server, quindi più tardi posso ordinare le richieste di esecuzione. Un terzo script, di solito chiamato call worker, interrogherà il DB per questi messaggi ed elaborerà la richiesta.

Il problema che ho è la maggior parte delle attuali soluzioni di coda Web chiama il lavoratore ogni volta che ne ha bisogno, perché non gli interessa l'ordine, vogliono solo fare un lavoro sincrono, asincrono in modo che il sito possa essere più veloce . Non riesco a chiamare lo script di lavoro ogni volta che arriva una nuova richiesta poiché il problema persisterà, due o più lavoratori possono essere istanziati allo stesso tempo a incasinare i calcoli. Posso mettere il lavoratore in un cronjob per pranzare ogni 60 secondi, ma sarà lento per i miei bisogni. Posso anche solo inserire un ciclo while all'interno del worker che interroga il DB a tempo indefinito ma che mi sembra inefficiente, o forse questo è effettivamente il modo in cui funziona la coda? solo un po 'di tempo che cicla fino alla fine dei tempi con forse un sonno su di esso ... Cosa ne pensi?

    
posta DomingoSL 30.01.2016 - 23:31
fonte

2 risposte

1

Since the writing in the DB is not atomic,

Deve essere.

Se utilizzi un database relazionale ragionevole, dovresti essere in grado di utilizzare Transactions per rendere atomico il ciclo update + read. Nota l'ordine.

begin transaction ; 

   update table_with_counter 
   set counter = counter + 1 
   where ... ; 

   select counter 
   from table_with_counter ; 

commit ; 

La transazione garantirà che nessun altro (nessun altro processo) possa aggiornare il contatore fino a quando il primo aggiornamento non sia completato o ripristinato (nell'improbabile eventualità di un errore). Diversi DBMS variano nel modo in cui raggiungono questo, ma il principio è lo stesso.

    
risposta data 01.03.2016 - 13:07
fonte
0

Come puoi risolvere ciò dipende dalle primitive di sincronizzazione che hai a disposizione o che puoi facilmente implementare. Tuttavia, ciò non dovrebbe essere necessario: qualsiasi ACID - database compatibile garantisce l'atomicità. Se il tuo database non può farlo, è MongoDB o inutile. Ma perché vorresti paralizzarti deliberatamente? Esistono molti sistemi di database di software libero di alta qualità disponibili per l'utente.

Serrature e mutex

Per eseguire la sequenza delle scritture, è possibile rendere le scritture reciprocamente esclusive. Ogni processo che vuole scrivere sul DB acquisisce il blocco, quindi lo rilascia dopo la scrittura. Questo garantisce un corretto ordine. Come semplice meccanismo di blocco tra processi, è possibile utilizzare un file di blocco poiché tutti i file system comuni possono essere utilizzati principalmente ACID-ly (consultare la chiamata di sistema flock() in Linux e nella maggior parte degli Unix).

Potresti anche offrire agli utenti dei blocchi temporizzati del tuo servizio web, ovvero il blocco viene rilasciato con una richiesta di scrittura dall'utente o dopo un timeout. Questo può essere problematico con molti utenti (poiché potrebbe dover attendere molto tempo per un blocco), può essere facilmente preso di mira da un attacco DoS e potrebbe essere inutilizzabile per gli utenti con connessioni a latenza elevata poiché potrebbero non essere fisicamente in grado di fare una richiesta di scrittura entro il timeout.

Confronta e scambia

Per una richiesta di modifica, l'utente invia sia il vecchio valore che ha ricevuto quando ha deciso di modificare il valore, sia il nuovo valore che desidera modificare. Il server utilizza un meccanismo per garantire la propria atomicità e quindi esegue la richiesta dell'utente solo se il valore corrente corrisponde al valore inviato dall'utente. Se qualcosa è cambiato, l'utente viene avvisato e può inviare nuovamente.

Questo è problematico se hai molti utenti: di tutti gli utenti che stanno attualmente inviando una modifica, solo una richiesta avrà successo e tutti gli altri dovranno riprovare. Inoltre, gli utenti con connessioni a latenza elevata hanno meno probabilità di riuscire, il che riduce ulteriormente la loro esperienza.

    
risposta data 31.01.2016 - 00:05
fonte

Leggi altre domande sui tag