2013-08-13 6 views
5

Ho riscontrato un problema in Grails che ritengo possa costituire un potenziale problema relativo alla modalità di gestione della concorrenza; e non sono sicuro di come gestire al meglio questo (o se esiste già una soluzione/pratica che posso adattare).Gestione di modifiche simultanee in Grails (GORM) evitando le eccezioni di oggetti stanti

Sfondo


applicazione I miei Grails serve come API REST, e ha un metodo di cifratura per i dati che si basa su un variabile contatore come un meccanismo di salatura per la cifratura.

Questo contatore variabile deve essere mantenuto, e non può essere inferiore in quanto i dati sta per una carta SIM in cui il contatore non può essere modificato successivamente all'emissione, quindi è importante che io sostengo in modo corretto questo contatore. Inoltre, se il contatore non è corretto, il messaggio verrà respinto dalla SIM.

Quando un utente chiama, per esempio: http://example.tld/service/controller/action?id=1 server farà il seguente:

  • get dell'oggetto controller con identificativo 1
  • Modificare il counter membro/fila dell'oggetto
  • save la oggetto

Questo è stato bene, per oltre 20.000 richieste. Tuttavia, due volte, ho avuto uno StaleObjectException che ho determinato per accadere a causa dell'oggetto che si accede alla stessa ora esatta. Ho determinato che ciò accade mentre l'API wrapper che ho fornito utilizza dieci thread e, per caso, entrambi i thread hanno chiamato lo action allo stesso tempo.

Ho letto e notato che mi può:

  • Spegnere il blocco ottimistico (questa sembra una cattiva idea)
  • Accendere gli aggiornamenti dinamici (non credo che questo mi aiuterà, poiché sto ancora aggiornando la stessa riga allo stesso tempo)
  • lock l'oggetto - ma penso che questo ancora solleverà un'eccezione a causa dell'oggetto bloccato il secondo tempo a cui si accede?
  • Utilizzare executeUpdate che penso sia simile all'accensione degli aggiornamenti dinamici? Forse non utile nel mio caso d'uso.

Domanda


Mi chiedo se c'è forse qualche meccanismo di transazione posso usare? Cioè un metodo per verificare se un oggetto è attualmente bloccato e, in tal caso, sleep per t per consentire il completamento della transazione nel database.Non

In ultima analisi, il mio obiettivo finale è quello di respingere qualsiasi richiesta, quindi, se il meccanismo di operazione di cui sopra esiste (che presumo sarà una sorta di blocco pessimistico) e detto meccanismo di transazione rifiuta la richiesta, come l'oggetto è bloccato, Preferirei un'altra soluzione, in quanto vorrei evitare a tutti i costi di respingere le richieste al servizio poiché complica le consegne precedenti ai clienti.

La soluzione attuale sto pensando è di soli:

try { 
    Foo.save() 
catch (RespectiveException ex) { 
    Thread.sleep(1000) 
    if(depth < 3) { 
     recursiveCallToThisMethod(depth++) 
    } else { 
     render(letTheUserKnowWhyItFailed) 
    } 
} 

Ma sì ... piuttosto brutto.

+0

In genere un'eccezione di modifica simultanea deve essere gestita dal client, che deve quindi riprovare l'operazione. –

+0

questa domanda potrebbe aiutare: http://stackoverflow.com/questions/129329/optimistic-vs-pessimistic-locking –

+0

Hai trovato qualche soluzione a questo? Ho lo stesso problema. L'unica cosa che posso pensare di provare è avere una concurrenthashmap di oggetti blocco blocco sincronizzati a cui fa riferimento una chiave univoca dell'oggetto dominio stesso e tenuta dal singleton del servizio. Quando viene chiamato il metodo di servizio, il primo controlla la mappa per vedere se esiste già un oggetto di blocco per il valore della chiave degli oggetti del dominio e lo utilizza nel blocco sincronizzato. Se l'oggetto di blocco non è lì, ne crea uno e lo memorizza sulla mappa. L'oggetto deve essere rimosso dalla mappa in un blocco finale quando hai finito –

risposta

0

Prova blocco pessimistico. Non ci saranno eccezioni sulla seconda richiesta, bloccherà solo fino al termine del primo.

def controller = Controller.lock(params.id) 
controller.counter += 1 
controller.save()