diff --git a/db/clientcursor.cpp b/db/clientcursor.cpp index 54b2d1807cc..e803afd459c 100644 --- a/db/clientcursor.cpp +++ b/db/clientcursor.cpp @@ -447,16 +447,29 @@ namespace mongo { return rec; } - bool ClientCursor::yieldSometimes( RecordNeeds need ) { + bool ClientCursor::yieldSometimes( RecordNeeds need, bool *yielded ) { + if ( yielded ) { + *yielded = false; + } if ( ! _yieldSometimesTracker.ping() ) { Record* rec = _recordForYield( need ); - if ( rec ) + if ( rec ) { + if ( yielded ) { + *yielded = true; + } return yield( yieldSuggest() , rec ); + } return true; } int micros = yieldSuggest(); - return ( micros > 0 ) ? yield( micros , _recordForYield( need ) ) : true; + if ( micros > 0 ) { + if ( yielded ) { + *yielded = true; + } + return yield( micros , _recordForYield( need ) ); + } + return true; } void ClientCursor::staticYield( int micros , const StringData& ns , Record * rec ) { diff --git a/db/clientcursor.h b/db/clientcursor.h index b3bd996c768..75c7da85cc6 100644 --- a/db/clientcursor.h +++ b/db/clientcursor.h @@ -186,9 +186,10 @@ namespace mongo { /** * @param needRecord whether or not the next record has to be read from disk for sure * if this is true, will yield of next record isn't in memory + * @param yielded true if a yield occurred, and potentially if a yield did not occur * @return same as yield() */ - bool yieldSometimes( RecordNeeds need ); + bool yieldSometimes( RecordNeeds need, bool *yielded = 0 ); static int yieldSuggest(); static void staticYield( int micros , const StringData& ns , Record * rec ); diff --git a/db/ops/update.cpp b/db/ops/update.cpp index 3221fe0f277..7f46cf5b9a4 100644 --- a/db/ops/update.cpp +++ b/db/ops/update.cpp @@ -1060,11 +1060,10 @@ namespace mongo { debug.updateobj = updateobj; - /* idea with these here it to make them loop invariant for multi updates, and thus be a bit faster for that case */ - /* NOTE: when yield() is added herein, these must be refreshed after each call to yield! */ + // idea with these here it to make them loop invariant for multi updates, and thus be a bit faster for that case + // The pointers may be left invalid on a failed yield recovery. NamespaceDetails *d = nsdetails(ns); // can be null if an upsert... NamespaceDetailsTransient *nsdt = &NamespaceDetailsTransient::get_w(ns); - /* end note */ auto_ptr mods; bool isOperatorUpdate = updateobj.firstElementFieldName()[0] == '$'; @@ -1105,6 +1104,9 @@ namespace mongo { shared_ptr< MultiCursor::CursorOp > opPtr( new UpdateOp( mods.get() && mods->hasDynamicArray() ) ); shared_ptr< MultiCursor > c( new MultiCursor( ns, patternOrig, BSONObj(), opPtr, true ) ); + d = nsdetails(ns); + nsdt = &NamespaceDetailsTransient::get_w(ns); + if( c->ok() ) { set seenObjects; MatchDetails details; @@ -1120,13 +1122,19 @@ namespace mongo { cc.reset( new ClientCursor( QueryOption_NoCursorTimeout , cPtr , ns ) ); } - if ( ! cc->yieldSometimes( ClientCursor::WillNeed ) ) { + bool didYield; + if ( ! cc->yieldSometimes( ClientCursor::WillNeed, &didYield ) ) { cc.release(); break; } if ( !c->ok() ) { break; } + + if ( didYield ) { + d = nsdetails(ns); + nsdt = &NamespaceDetailsTransient::get_w(ns); + } // ***************** // May have already matched in UpdateOp, but do again to get details set correctly @@ -1146,6 +1154,8 @@ namespace mongo { if ( !c->ok() ) { break; } + d = nsdetails(ns); + nsdt = &NamespaceDetailsTransient::get_w(ns); } continue; } @@ -1276,6 +1286,8 @@ namespace mongo { if ( !c->ok() ) { break; } + d = nsdetails(ns); + nsdt = &NamespaceDetailsTransient::get_w(ns); } if (atomic) diff --git a/jstests/updatef.js b/jstests/updatef.js index 638b026cf53..69425932f19 100644 --- a/jstests/updatef.js +++ b/jstests/updatef.js @@ -1,7 +1,5 @@ // Test unsafe management of nsdt on update command yield SERVER-3208 -if ( 0 ) { // SERVER-3208 - prefixNS = db.jstests_updatef; prefixNS.save( {} ); @@ -24,5 +22,3 @@ for( i=0; i < 20; ++i ) { } s(); - -} \ No newline at end of file