2012-09-04 15 views
7

Non so da dove iniziare o quali informazioni sono rilevanti per favore fatemi sapere quali informazioni aggiuntive potrebbero essere utili per risolvere questo problema.Perché mongodb sembra salvare alcuni oggetti binari e non altri?

Sto sviluppando una semplice applicazione cometd e sto usando mongodb come back-end di archiviazione. Ottengo una singola istanza di mongodb all'avvio dell'applicazione e utilizzo questa istanza per tutte le query. Questo è infatti raccomandato dalla documentazione del driver mongo java come indicato qui: http://www.mongodb.org/display/DOCS/Java+Driver+Concurrency. Stavo cercando di capire che il problema aveva qualcosa a che fare con la sicurezza dei thread, ma secondo questo link mongodb è completamente thread-safe.

Ecco dove diventa interessante. Ho una classe che estende BasicDBObject.

public class MyBasicDBObject { 

    private static final String MAP = "map"; 

    public boolean updateMapAnd(String submap, String key, byte[] value) { 
     Map topMap = (Map)this.get(MAP); 
     Map embeddedMap = topMap.get(submap); 
     byte[] oldValue = embeddedMap.get(key); 

     newValue = UtilityClass.binaryAnd(oldValue, value); 

     embeddedMap.put(key, newValue); 
     topMap.put(submap, embeddedMap); 
     this.put(MAP, topMap); 
    } 

    public boolean updateMapXor(String submap, String key, byte[] value) { 
     Map topMap = (Map)this.get(MAP); 
     Map embeddedMap = topMap.get(submap); 
     byte[] oldValue = embeddedMap.get(key); 

     newValue = UtilityClass.binaryXor(oldValue, value); 

     embeddedMap.put(key, newValue); 
     topMap.put(submap, embeddedMap); 
     this.put(MAP, topMap); 
    } 
} 

Le prossime due classi di scheletro che si estendono MyBasicDBObject.

public class FirstDBObject extends MyBasicDBObject { //no code } 

public class SecondDBObject extends MyBasicDBObject { //no code } 

L'unica ragione per cui ho creato le mie classi in questo modo è quello di migliorare la leggibilità del codice nel trattare con questi due oggetti all'interno dello stesso ambito. Questo mi permette di effettuare le seguenti ...

//a cometd service callback 
public void updateMapObjectsFoo(ServerSession remote, Message message) { 

    //locate the objects to update... 
    FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... }); 
    SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... }); 

    //update them as follows 
    first.updateMapAnd("default", "someKey1", newBinaryData1); 
    second.updateMapAnd("default", "someKey2", newBinaryData2); 

    //save (update) them to their respective collections 
    firstCollection.save(first); 
    secondCollection.save(second); 
} 

public void updateMapObjectsBar(ServerSession remote, Message message) { 

    //locate the objects to update... 
    FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... }); 
    SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... }); 

    /** 
    * the only difference is these two calls 
    */ 
    first.updateMapXor("default", "someKey1", newBinaryData1); 
    second.updateMapXor("default", "someKey2", newBinaryData2); 

    //save (update) them to their respective collections 
    firstCollection.save(first); 
    secondCollection.save(second); 
} 

Il UtilityClass fa esattamente come i metodi sono chiamati, bit per bit & e bit ^ mediante iterazione negli array di byte passati.

Questo è il punto in cui sono completamente perso. updateMapObjectsFoo() funziona esattamente come previsto, sia first che second riflettono le modifiche nel database. D'altra parte, updateMapObjectsBar() riesce a aggiornare correttamente first.

ispezione tramite il debug updateMapObjectsBar() mostra che gli oggetti binari vengono infatti aggiornati correttamente su entrambi gli oggetti, ma quando mi dirigo verso la mongo shell di indagare il problema che vedo che first viene aggiornato nel DB e second non lo è. Dove ho avuto l'idea che la sicurezza dei thread avesse qualcosa a che fare con questo? L'unica differenza che mi infastidisce è che secondCollection è utilizzato da altri servizi di cometd mentre non lo è il firstCollection. Ciò sembra rilevante in una mano, ma non nell'altra poiché le opere Foo e Bar no.

Ho strappato il codice e l'ho rimesso insieme e continuo a tornare a questo stesso problema. Cosa diavolo sta succedendo qui?

Sembra che abbia escluso la parte più rilevante di tutto ciò che è l'incubo dei generici java e il ricorso del driver mongodb a questa caratteristica del linguaggio. BasicDBObject è essenzialmente un wrapper per un Map<String, Object>. Il problema è che una volta archiviato un oggetto in quella mappa, devi riportarlo indietro a quello che era quando lo hai inserito. Sì, potrebbe sembrare del tutto ovvio, e lo sapevo benissimo prima di pubblicare questa domanda.

Non riesco a individuare ciò che è successo esattamente, ma offrirò questo consiglio agli utenti di java + mongodb. Casterai, MOLTO, e più le tue strutture di dati saranno complesse più cast di cui avrai bisogno. Per farla breve, non farlo:

DBObject obj = (DBObject) collection.findOne(new BasicDBObject("_id", new ObjectId((String)anotherObj.get("objId")))); 

Uno fodere sono tentati quando si sta facendo prototipi rapidi, ma quando si inizia a fare che più e più volte si sono tenuti a fare errori.Scrivi più codice ora, e soffrono meno frustrazione più tardi:

DBObject query = new DBObject(); 
String objId = (String) anotherObj.get("objId"); 
query.put("_id", new ObjectId(objId)); 
obj = (DBObject) collection.findOne(query); 

Credo che questo sia fastidiosamente verbose ma devo aspettare tanto interagendo direttamente con Mongo invece di utilizzare una sorta di biblioteca per rendere la mia vita più facile. Mi sono reso ridicolo su questo, ma spero che qualcuno imparerà dal mio errore e si risparmia un sacco di frustrazione.

Grazie a tutti per il vostro aiuto.

+1

Il problema, ovviamente, è con la frase "metodo essenzialmente identico", che chiaramente non è se ha un comportamento diverso. Potresti voler includere anche il codice per quel metodo. –

+0

sarebbe utile se è possibile fornire la definizione del metodo updateMapObjectsBar(). Se sono simili, è improbabile che uno lavori e l'altro no. –

+0

Probabilmente entrambi avete ragione che qualcosa è diverso, ma per brevità non posso includere l'intero corpo di entrambi i metodi. Mi sento come se stessi in qualche modo abusando di Mongo poiché l'unica differenza visibile è che il secondo metodo non riesce a memorizzare il secondo oggetto. –

risposta

2

Potrebbe facilmente essere un problema di multi-threading. Mentre si è certi che gli oggetti Mongo, DB e DBCollection sono a prova di thread se esiste una sola istanza di Mongo, i DBObjects sono non threadsafe. Ma anche se fossero a prova di codice, i metodi updateMapObjectsFoo/Bar non fanno nulla per assicurare che siano operazioni atomiche sul database.

Sfortunatamente, i cambiamenti che dovresti apportare al tuo codice sono più intensi di un semplice spargimento di alcune parole chiave "sincronizzate". Vedere se http://www.mongodb.org/display/DOCS/Atomic+Operations non aiuta a comprendere la portata del problema e alcune potenziali soluzioni.

+0

grazie per il link. È stato molto utile per comprendere meglio gli aggiornamenti in MongoDB e l'importanza delle operazioni atomiche. Sono molto nuovo per documentare l'archiviazione e la scena nosql. Provenendo da RDMS e utilizzando principalmente ORM (anche se ho una vasta conoscenza di SQL), sono scarsamente istruito sulle migliori pratiche di transazioni di database nella programmazione. Potresti avere suggerimenti per buone risorse su questo argomento? –

+0

Gran parte delle mie conoscenze derivano da una sessione in una conferenza Mongo locale. Non conosco alcuna risorsa online diversa da quella indicata nella mia risposta. La comunità e lo staff di Mongo sono di gran lunga la vostra migliore risorsa. Raccomando il gruppo google mongo-user per una guida più coinvolgente di quella che puoi ottenere da una risposta di overflow dello stack. – Fuwjax

+0

molto apprezzato. Credo che la tua risposta soddisfi molto bene la mia domanda iniziale. –

Problemi correlati