2010-10-04 7 views
6

Sto provando Google App Engine Java, tuttavia l'assenza di un vincolo univoco sta rendendo le cose difficili. Sono stato through this post e this blog suggerisce un metodo per implementare qualcosa di simile. Il mio background è in MySQL. Il passaggio al datastore senza un vincolo univoco mi rende nervoso perché non ho mai dovuto preoccuparmi dei valori duplicati prima e controllare ogni valore prima di inserire un nuovo valore ha ancora spazio per errori.Enforcing Unique Constraint in GAE

"No, non è ancora possibile specificare univoco durante la creazione dello schema."

-David Underhill parla GAE e il vincolo univoco (post link)

cosa state usando per implementare qualcosa di simile a una chiave univoca o primaria?

Ho sentito parlare di uno strato datastore astratta creata utilizzando l'API di basso livello che ha lavorato come un normale RDB, che però non era gratuito (ma non mi ricordo il nome del software)

vista schematica di il mio problema

sNo = biggest serial_number in the db 
sNo++ 
Insert new entry with sNo as serial_number value //checkpoint 
User adds data pertaining to current serial_number 
Update entry with data where serial_number is sNo 

Tuttavia alla linea numero 3 (punto di controllo), mi sento due utenti potrebbero aggiungere lo stesso SNO. E questo è ciò che mi impedisce di lavorare con Appengine.

risposta

4

È possibile generare numeri di serie univoci per i prodotti senza dover applicare ID univoci o eseguire query sull'intero set di entità per scoprire quale sia il numero seriale più grande attualmente. È possibile utilizzare le transazioni e un'entità Singleton per generare il numero di serie "successivo". Poiché l'operazione avviene all'interno di una transazione, puoi essere certo che nessun prodotto avrà mai lo stesso numero di serie.

Questo approccio sarà, tuttavia, un potenziale ostacolo alle prestazioni e limiterà la scalabilità dell'applicazione. Se è il caso che la creazione di nuovi numeri di serie non avvenga così spesso da ottenere contese, potrebbe funzionare per te.

MODIFICA: Per chiarire, il singleton che contiene il numero di serie corrente o successivo che deve essere assegnato è completamente indipendente da qualsiasi entità a cui siano effettivamente assegnati numeri di serie. Non hanno bisogno di essere tutti parte di un gruppo di entità. Potresti avere entità di più modelli che utilizzano lo stesso meccanismo per ottenere un nuovo numero di serie univoco.

non ricordo Java abbastanza bene da poter fornire il codice di esempio, e il mio esempio Python potrebbe essere privo di significato per voi, ma qui è pseudo-codice per illustrare l'idea:

  1. Ricevi richiesta di creare un nuovo inventario articolo.
  2. Immettere la transazione.
  3. Recupera il valore corrente della singola entità del modello SerialNumber.
  4. Valore di incremento e scrittura nel database
  5. Valore restituito quando si esce dalla transazione.

Ora, il codice che esegue tutto il lavoro per creare effettivamente l'articolo di magazzino e archiviarlo insieme al suo nuovo numero di serie NON DEVE essere eseguito in una transazione.

Avvertenza: come detto sopra, questo potrebbe essere un collo di bottiglia di prestazioni importanti, in quanto è possibile creare un solo numero di serie in qualsiasi momento. Tuttavia, ti fornisce la certezza che il numero seriale che hai appena generato è unico e non in uso.

+0

Una transazione su un'entità Singleton non richiederebbe che Singleton e tutte le entità correlate si trovino nello stesso gruppo di entità? –

+0

@ Jason, penso che Singleton possa essere isolato nel proprio gruppo. Le entità che ricevono i numeri seriali non sono correlate ad essa. L'unica cosa che deve accadere nell'isolamento di una transazione è l'incremento del numero di serie. Sarei felice di creare un esempio, ma programma AppEngine con Python. Non ho toccato Java dal 1999. –

+0

@Jason Hall @ Adam Crossland Devo dirti che gran parte di quello che hai detto è andato 'woosh' sopra la mia testa. Non ho esperienza di 'transazioni', sono nuovo di JAVA, ma capisco un po 'di pseudo codice, che è simile a quello che faccio attualmente. Funzionerà in uno scenario a basso traffico, ma voglio essere relativamente sicuro anche in scenari negativi. Grazie per l'eccellente recensione! – abel

12

Questa e altre domande simili si presentano spesso quando si parla della transizione da un tradizionale RDB a un datastore BigTable come App Engine.

Spesso è utile discutere di perché lo il datastore non supporta chiavi univoche, poiché informa la mentalità in cui si dovrebbe essere quando si pensa ai propri schemi di archiviazione dei dati. Il motivo per cui i vincoli unici non sono disponibili è perché limita notevolmente la scalabilità. Come hai detto, imporre il vincolo significa controllare tutte le altre entità per quella proprietà. Se lo fai manualmente nel tuo codice o nel datastore lo fa automaticamente dietro le quinte, deve ancora succedere, e ciò significa prestazioni inferiori. Alcune ottimizzazioni possono essere fatte, ma deve ancora accadere in un modo o nell'altro.

La risposta alla tua domanda è, davvero pensare al motivo per cui hai bisogno di quel vincolo unico.

In secondo luogo, ricordare che le chiavi do esistono nel datastore e sono un ottimo modo per applicare un semplice vincolo univoco.

my_user = MyUser(key_name=users.get_current_user().email()) 
my_user.put() 

Questo garantirà che non MyUser sarà mai essere creato con quella e-mail mai più, e si può anche rapidamente recuperare il MyUser con quella e-mail:

my_user = MyUser.get(users.get_current_user().email()) 

Nel runtime Python si può anche fare :

my_user = MyUser.get_or_create(key_name=users.get_current_user().email()) 

Quale inserirà o recupererà l'utente con quell'e-mail.

Tuttavia, qualsiasi cosa più complessa di quella non sarà scalabile.Quindi pensa davvero se hai bisogno che quella proprietà sia globalmente unica, o se ci sono modi per rimuovere la necessità di quel vincolo univoco. Spesso, dopo aver trovato alcuni piccoli accorgimenti, non hai avuto bisogno di quella proprietà per essere unica dopo tutto.

+0

Grazie per la correzione dettagliata. In parte capisco perché il datastore GAE non abbia le caratteristiche di un normale RDB. Un'app che sto attualmente funziona richiede numeri di serie incrementali da assegnare alle voci. Attualmente, controllo il db per l'ultimo numero di voce (sNo), aggiungo 1 (sNo ++) ad esso e poi inserisco una nuova voce con il nuovo sNo come valore, in modo che gli altri che lavorano sul sistema, non ottengano duplicati sNo per funzionare con. Ciò non è accaduto, ma temo che in periodi di lavoro intenso (quando 100-120 dipendenti aggiungono voci), potrebbe sorgere un duplicato sNo. Aggiornamento della domanda con un esempio. – abel

+2

+1. Ottima risposta! –

+1

@abel Idealmente il tuo sistema non richiederebbe numeri seriali in aumento monotono e potresti assegnare una chiave casuale a ciascun elemento. Se questo è per interagire con un altro sistema, questo potrebbe non essere facilmente fattibile. Puoi provare a utilizzare le transazioni per atomicità e memcache per le prestazioni. –

3

Ho riscontrato questo stesso problema in un'applicazione in cui gli utenti dovevano prenotare un periodo di applicazione. Avevo bisogno di "inserire" esattamente un'entità temporale unica, mentre aspettavo che gli utenti richiedessero simultaneamente lo stesso periodo temporale.

Ho isolato un esempio di come eseguire questa operazione sul motore di app e I blogged about it. Il post sul blog ha esempi di codice canonico che utilizzano Datastore e Objectify. (A proposito, vorrei consigliare di evitare JDO.)

Ho anche distribuito un live demonstration in cui è possibile avanzare due utenti verso riservare la stessa risorsa. In questa demo puoi sperimentare il comportamento esatto del datastore del motore dell'app clic-clic.

Se si sta cercando il comportamento di un vincolo univoco, questi dovrebbero rivelarsi utili.

-broc

0

ho pensato un'alternativa alla tecnica di transazione nel blog di Broc, potrebbe essere quello di fare una classe Singleton che contiene un metodo sincronizzato (ad esempio nome addUserName (String)) responsabile l'aggiunta di una nuova voce solo se è unico o genera un'eccezione. Quindi creare un contextlistener che istanzia una singola istanza di questo singleton, aggiungendolo come un attributo al servletContext. I servlet possono quindi chiamare il metodo addUserName() sull'istanza singleton ottenuta tramite getServletContext.

tuttavia questo non è una buona idea perché GAE rischia di dividere l'applicazione su più JVM potrebbero ancora verificarsi in modo più istanze di classe Singleton, uno in ogni JVM. see this thread

Un'alternativa più simile a GAE sarebbe quella di scrivere un modulo GAE responsabile della verifica dell'unicità e dell'aggiunta di nuovi input; quindi utilizzare il ridimensionamento manuale o base con ...

<max-instances>1</max-instances> 

allora avete una singola istanza in esecuzione su GAE che funge da unico punto di autorità, l'aggiunta di utenti uno alla volta al datastore. Se sei preoccupato che questa istanza sia un collo di bottiglia, potresti migliorare il modulo, aggiungere l'accodamento o un'architettura master/slave interna.

Questa soluzione basata su moduli consentirebbe l'aggiunta di molti nomi utente univoci all'archivio dati in un breve lasso di tempo, senza mettere in pericolo i problemi di contesa del gruppo di entità.