2016-05-03 6 views
1

Ich habe das folgende Codefragment, das BSON Dokumente aus einer Abfrage Cursor ansammelt, danach verarbeitet werden:Speicher BSONObj Objekt in std :: vector Abstürze mit großen Dokumenten

// Accumulate 
std::vector<BSONObj> results; 
while (cursor->more()) { 
    BSONObj r = cursor->nextSafe(); 
    results.push_back(r); 
} 
... 
// Process it (example) 
for (unsigned int ix = 0; ix < results.size(); ix++) { 
    BSONElement be = results[ix].getField("_id"); 
    // Do somtething with 'be' 
    ... 
} 

Dieser Code funktioniert hat gut von eine Zeit (Monate), aber wir haben kürzlich festgestellt, dass bei großen Dokumenten in DB (ca. 1,1 MB) die results[ix].getField("_id") Anweisung mit segfault abstürzt. Dies ist der Anfang der Backtrace:

(gdb) bt 
#0 readNative<int> (offset=0, t=<synthetic pointer>, this=<optimized out>) at src/mongo/base/data_view.h:46 
#1 readNative<int> (offset=0, this=<optimized out>) at src/mongo/base/data_view.h:53 
#2 readLE<int> (offset=0, this=<optimized out>) at src/mongo/base/data_view.h:59 
#3 objsize (this=0x7f74340022e0) at src/mongo/bson/bsonobj.h:309 
#4 BSONObjIterator (jso=..., this=<synthetic pointer>) at src/mongo/bson/bsonobjiterator.h:42 
#5 mongo::BSONObj::getField (this=0x7f74340022e0, name=...) at src/mongo/bson/bsonobj.cpp:635 
... 

ich das Problem mit results.push_back(r.copy()) statt results.push_back(r) gelöst haben. Daher wurde der Fehler wahrscheinlich verursacht, wenn das r Objekt am Ende des while Blockbereichs zerstört wurde und die Kopie im Vektor in einem instabilen Zustand zurückgestoßen wurde. Wenn man eine Kopie von r zurückdrückt, ohne den Blockbereich als neue Variable zu treffen, scheint das Problem zu lösen.

Also, ich habe folgende Fragen:

  1. Was ist die beste Art und Weise der Speicherung BSONObj in einem std::vector aus einem Abfrageergebnis bekam? Ich denke, ich habe eine vernünftige Lösung gefunden, bin mir aber nicht sicher, ob dies die beste Lösung ist.

  2. Warum funktioniert der Code mit push_back(r) mit kleinen Dokumenten? Wenn der richtige Weg ist r.copy() zu verwenden, um Probleme zu vermeiden r am Ende while Block Scope zu zerstören, ich verstehe, dass es immer, nicht nur im Falle von Objekten von etwa 1,1 MB.

Ich bin mit MongoDB C++ Treiber Legacy-1.0.7 (im Fall kann es helfen, oder das Problem mit bestimmten Versionen von MongoDB C++ Treiber verwendet werden könnte).

Antwort

1

Die von nextSafe zurückgegebenen BSONObj-Objekte besitzen keine eigenen Daten und werden durch nachfolgende Aufrufe von nextSafe ungültig gemacht.

So wird Ihr Vektor mit ungültigen BSONObj-Objekten aufgefüllt.

Rufen Sie BSONObj :: getOwned() stattdessen auf dem Cursor-Ergebnis auf, bevor Sie den Vektor zurückschieben.

Wenn Sie Ihr Programm unter AddressSanitizer oder valgrind ausführen, werden Sie sehr wahrscheinlich Fehler nach Verwendung-nach-Frei-Typ sehen.

+0

Danke für die Rückmeldung. Also meinst du mit 'results.push_back (r);' anstelle von 'results.push_back (r.copy());' * aber mit 'r.getOwned();' kurz vor 'results.push_back (r); '. Ist mein Verständnis richtig? – fgalan

+1

Nein, nur 'results.push_back (r.getOwned())' sollte den Trick machen. – acm

+0

Um das zu verdeutlichen, erstellt die BSONObj :: copy-Methode immer eine Kopie, aber BSONObj :: getOwned() erstellt nur eine Kopie, wenn 'r' nicht den Puffer besitzt. In diesem Fall sind sie gleichwertig. Die Verwendung von getOwned ist jedoch eine bessere Vorgehensweise, da unnötige Kopien vermieden werden, wenn Sie das Dokument bereits besitzen. – acm