summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2014-03-05 12:22:58 -0500
committerRandolph Tan <randolph@10gen.com>2014-03-14 10:29:28 -0400
commite44682821c37fdf3d4fd8cb58dcf5c34181ddbde (patch)
tree55b6d6b53a0f13703de7d9d433cb6f53a5b97c95
parentcde863db6f180479e250ea3e2172f624be3d2b18 (diff)
downloadmongo-e44682821c37fdf3d4fd8cb58dcf5c34181ddbde.tar.gz
SERVER-12954 w parameter is not propagated correctly on compatibility writeMode
Send resetError only when absolutely necessary.
-rw-r--r--jstests/core/bulk_legacy_enforce_gle.js100
-rw-r--r--jstests/multiVersion/mongos_downconvert_writes.js14
-rw-r--r--src/mongo/s/write_ops/batch_downconvert.cpp143
-rw-r--r--src/mongo/s/write_ops/batch_downconvert.h6
-rw-r--r--src/mongo/s/write_ops/batch_downconvert_test.cpp36
-rw-r--r--src/mongo/s/write_ops/batch_upconvert.cpp11
-rw-r--r--src/mongo/s/write_ops/dbclient_safe_writer.cpp16
-rw-r--r--src/mongo/s/write_ops/dbclient_safe_writer.h2
-rw-r--r--src/mongo/shell/bulk_api.js34
9 files changed, 280 insertions, 82 deletions
diff --git a/jstests/core/bulk_legacy_enforce_gle.js b/jstests/core/bulk_legacy_enforce_gle.js
new file mode 100644
index 00000000000..bec11749274
--- /dev/null
+++ b/jstests/core/bulk_legacy_enforce_gle.js
@@ -0,0 +1,100 @@
+/**
+ * Tests the resetError logic when the bulk api enforces the write concern for unordered
+ * writes. The tests indirectly checks whether resetError was called by inspecting the
+ * response of the getLastError command after executing the bulk ops.
+ */
+
+var coll = db.bulk_legacy_enforce_gle;
+
+// batch of size 1 no error case.
+coll.drop();
+var bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+assert.writeOK(bulk.execute());
+
+var gle = db.runCommand({ getLastError: 1 });
+assert(gle.ok, tojson(gle));
+assert.eq(1, gle.n);
+
+// batch of size 1 should not call resetError even when it errors out.
+coll.drop();
+coll.insert({ _id: 1 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+assert.writeError(bulk.execute());
+
+gle = db.runCommand({ getLastError: 1 });
+assert(gle.ok, tojson(gle));
+assert.neq(null, gle.err);
+
+// batch with all error except last should not call resetError.
+coll.drop();
+coll.insert({ _id: 1 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 });
+var res = assert.writeError(bulk.execute());
+assert.eq(2, res.getWriteErrors().length);
+
+gle = db.runCommand({ getLastError: 1 });
+assert(gle.ok, tojson(gle));
+assert.eq(1, gle.n);
+
+// batch with error at middle should not call resetError.
+coll.drop();
+coll.insert({ _id: 1 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 });
+res = assert.writeError(bulk.execute());
+assert.eq(1, res.getWriteErrors().length);
+
+gle = db.runCommand({ getLastError: 1 });
+assert(gle.ok, tojson(gle));
+// mongos sends the bulk as one while the shell sends the write individually
+assert.gte(gle.n, 1);
+
+// batch with error at last should call resetError.
+coll.drop();
+coll.insert({ _id: 2 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 });
+res = assert.writeError(bulk.execute());
+assert.eq(1, res.getWriteErrors().length);
+
+gle = db.runCommand({ getLastError: 1 });
+assert(gle.ok, tojson(gle));
+assert.eq(0, gle.n);
+
+// batch with error at last should not call resetError if { w: 1 }
+coll.drop();
+coll.insert({ _id: 2 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 });
+res = assert.writeError(bulk.execute());
+assert.eq(1, res.getWriteErrors().length);
+
+gle = db.runCommand({ getLastError: 1, w: 1 });
+assert(gle.ok, tojson(gle));
+assert.neq(null, gle.err);
+
+// batch with error at last should not call resetError if { w: 0 }
+coll.drop();
+coll.insert({ _id: 2 });
+bulk = coll.initializeUnorderedBulkOp();
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 });
+bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 });
+res = assert.writeError(bulk.execute());
+assert.eq(1, res.getWriteErrors().length);
+
+gle = db.runCommand({ getLastError: 1, w: 0 });
+assert(gle.ok, tojson(gle));
+assert.neq(null, gle.err);
+
diff --git a/jstests/multiVersion/mongos_downconvert_writes.js b/jstests/multiVersion/mongos_downconvert_writes.js
new file mode 100644
index 00000000000..7361cae7f14
--- /dev/null
+++ b/jstests/multiVersion/mongos_downconvert_writes.js
@@ -0,0 +1,14 @@
+/**
+ * Tests the mongos downconversion, specifically the resetError logic when it enforces
+ * the write concern.
+ */
+
+var st = new ShardingTest({ shards: 1, other: { shardOptions: { binVersion: '2.4' }}});
+st.stopBalancer();
+
+var conn = st.s;
+conn.forceWriteMode('commands');
+db = conn.getDB('test');
+load('./jstests/core/bulk_legacy_enforce_gle.js');
+
+st.stop();
diff --git a/src/mongo/s/write_ops/batch_downconvert.cpp b/src/mongo/s/write_ops/batch_downconvert.cpp
index 66475d8a39a..e3d706d0a83 100644
--- a/src/mongo/s/write_ops/batch_downconvert.cpp
+++ b/src/mongo/s/write_ops/batch_downconvert.cpp
@@ -28,6 +28,7 @@
#include "mongo/s/write_ops/batch_downconvert.h"
+#include "mongo/db/write_concern_options.h"
#include "mongo/util/assert_util.h"
namespace mongo {
@@ -175,19 +176,14 @@ namespace mongo {
break;
BatchItemRef itemRef( &request, static_cast<int>( i ) );
- bool isLastItem = ( i == request.sizeWriteOps() - 1 );
-
- BSONObj writeConcern;
- if ( isLastItem && request.isWriteConcernSet() ) {
- writeConcern = request.getWriteConcern();
- // Pre-2.4.2 mongods react badly to 'w' being set on config servers
- if ( nss.db() == "config" )
- writeConcern = fixWCForConfig( writeConcern );
- }
BSONObj gleResult;
GLEErrors errors;
- Status status = _safeWriter->safeWrite( conn, itemRef, writeConcern, &gleResult );
+ Status status = _safeWriter->safeWrite( conn,
+ itemRef,
+ WriteConcernOptions::Acknowledged,
+ &gleResult );
+
if ( status.isOK() ) {
status = extractGLEErrors( gleResult, &errors );
}
@@ -200,6 +196,10 @@ namespace mongo {
return;
}
+ if ( errors.wcError.get() ) {
+ response->setWriteConcernError( errors.wcError.release() );
+ }
+
//
// STATS HANDLING
//
@@ -227,67 +227,88 @@ namespace mongo {
response->setLastOp( stats.lastOp );
- //
- // WRITE ERROR HANDLING
- //
-
- // If any error occurs (except stale config) the previous GLE was not enforced
- bool enforcedWC = !errors.writeError.get()
- || errors.writeError->getErrCode() == ErrorCodes::StaleShardVersion;
-
// Save write error
if ( errors.writeError.get() ) {
errors.writeError->setIndex( i );
response->addToErrDetails( errors.writeError.release() );
}
+ }
- //
- // WRITE CONCERN ERROR HANDLING
- //
+ //
+ // WRITE CONCERN ERROR HANDLING
+ //
+
+ // The last write is weird, since we enforce write concern and check the error through
+ // the same GLE if possible. If the last GLE was an error, the write concern may not
+ // have been enforced in that same GLE, so we need to send another after resetting the
+ // error.
+
+ BSONObj writeConcern;
+ if ( request.isWriteConcernSet() ) {
+ writeConcern = request.getWriteConcern();
+ // Pre-2.4.2 mongods react badly to 'w' being set on config servers
+ if ( nss.db() == "config" )
+ writeConcern = fixWCForConfig( writeConcern );
+ }
- // The last write is weird, since we enforce write concern and check the error through
- // the same GLE if possible. If the last GLE was an error, the write concern may not
- // have been enforced in that same GLE, so we need to send another after resetting the
- // error.
- if ( isLastItem ) {
-
- // Try to enforce the write concern if everything succeeded (unordered or ordered)
- // OR if something succeeded and we're unordered.
- bool needToEnforceWC =
- !response->isErrDetailsSet()
- || ( !request.getOrdered()
- && response->sizeErrDetails() < request.sizeWriteOps() );
-
- if ( !enforcedWC && needToEnforceWC ) {
- dassert( !errors.writeError.get() ); // emptied above
-
- // Might have gotten a write concern validity error earlier, these are
- // enforced even if the wc isn't applied, so we ignore.
- errors.wcError.reset();
-
- Status status = _safeWriter->enforceWriteConcern( conn,
- nss.db().toString(),
- writeConcern,
- &gleResult );
-
- if ( status.isOK() ) {
- status = extractGLEErrors( gleResult, &errors );
- }
-
- if ( !status.isOK() ) {
- response->clear();
- response->setOk( false );
- response->setErrCode( status.code() );
- response->setErrMessage( status.reason() );
- return;
- }
- }
- // END Write concern retry
+ bool needToEnforceWC = WriteConcernOptions::Acknowledged.woCompare(writeConcern) != 0 &&
+ WriteConcernOptions::Unacknowledged.woCompare(writeConcern) != 0;
+
+ if ( needToEnforceWC &&
+ ( !response->isErrDetailsSet() ||
+ ( !request.getOrdered() &&
+ // Not all errored. Note: implicit response->isErrDetailsSet().
+ response->sizeErrDetails() < request.sizeWriteOps() ))) {
+
+ // Might have gotten a write concern validity error earlier, these are
+ // enforced even if the wc isn't applied, so we ignore.
+ response->unsetWriteConcernError();
+
+ const string dbName( nss.db().toString() );
- if ( errors.wcError.get() ) {
- response->setWriteConcernError( errors.wcError.release() );
+ Status status( Status::OK() );
+
+ if ( response->isErrDetailsSet() ) {
+ const WriteErrorDetail* lastError = response->getErrDetails().back();
+
+ // If last write op was an error.
+ if ( lastError->getIndex() == static_cast<int>( request.sizeWriteOps() - 1 )) {
+ // Reset previous errors so we can apply the write concern no matter what
+ // as long as it is valid.
+ status = _safeWriter->clearErrors( conn, dbName );
}
}
+
+ if ( !status.isOK() ) {
+ response->clear();
+ response->setOk( false );
+ response->setErrCode( status.code() );
+ response->setErrMessage( status.reason() );
+ return;
+ }
+
+ BSONObj gleResult;
+ status = _safeWriter->enforceWriteConcern( conn,
+ dbName,
+ writeConcern,
+ &gleResult );
+
+ GLEErrors errors;
+ if ( status.isOK() ) {
+ status = extractGLEErrors( gleResult, &errors );
+ }
+
+ if ( !status.isOK() ) {
+ response->clear();
+ response->setOk( false );
+ response->setErrCode( status.code() );
+ response->setErrMessage( status.reason() );
+ return;
+ }
+
+ if ( errors.wcError.get() ) {
+ response->setWriteConcernError( errors.wcError.release() );
+ }
}
response->setOk( true );
diff --git a/src/mongo/s/write_ops/batch_downconvert.h b/src/mongo/s/write_ops/batch_downconvert.h
index cd5440ae993..521a48143eb 100644
--- a/src/mongo/s/write_ops/batch_downconvert.h
+++ b/src/mongo/s/write_ops/batch_downconvert.h
@@ -72,6 +72,12 @@ namespace mongo {
const StringData& dbName,
const BSONObj& writeConcern,
BSONObj* gleResponse ) = 0;
+
+ /**
+ * Clears the error information on this connection.
+ */
+ virtual Status clearErrors( DBClientBase* conn,
+ const StringData& dbName ) = 0;
};
/**
diff --git a/src/mongo/s/write_ops/batch_downconvert_test.cpp b/src/mongo/s/write_ops/batch_downconvert_test.cpp
index d103cf9caab..b253c0d94aa 100644
--- a/src/mongo/s/write_ops/batch_downconvert_test.cpp
+++ b/src/mongo/s/write_ops/batch_downconvert_test.cpp
@@ -221,7 +221,8 @@ namespace {
public:
MockSafeWriter( const vector<BSONObj>& gleResponses ) :
- _gleResponses( gleResponses.begin(), gleResponses.end() ) {
+ _gleResponses( gleResponses.begin(), gleResponses.end() ),
+ _isClearCalled( false ) {
}
virtual ~MockSafeWriter() {
@@ -241,19 +242,37 @@ namespace {
const StringData& dbName,
const BSONObj& writeConcern,
BSONObj* gleResponse ) {
+ if (_gleResponses.empty()) {
+ return Status(ErrorCodes::IllegalOperation, "No more gle responses");
+ }
+
BSONObj response = _gleResponses.front();
+ *gleResponse = response.getOwned();
_gleResponses.pop_front();
- *gleResponse = response;
return Status::OK();
}
+ Status clearErrors( DBClientBase* conn,
+ const StringData& dbName ) {
+ _isClearCalled = true;
+ return Status::OK();
+ }
+
+ bool isClearCalled() const {
+ return _isClearCalled;
+ }
+
deque<BSONObj> _gleResponses;
+
+ private:
+ bool _isClearCalled;
};
TEST(WriteBatchDownconvert, Basic) {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 1.0, err: null}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
MockSafeWriter mockWriter( gleResponses );
BatchSafeWriter batchWriter( &mockWriter );
@@ -270,12 +289,14 @@ namespace {
ASSERT_EQUALS( response.getN(), 1 );
ASSERT( !response.isErrDetailsSet() );
ASSERT( !response.isWriteConcernErrorSet() );
+ ASSERT( !mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, GLENotOk) {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 0.0, errmsg: 'message', code: 1234}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
MockSafeWriter mockWriter( gleResponses );
BatchSafeWriter batchWriter( &mockWriter );
@@ -292,12 +313,14 @@ namespace {
ASSERT( !response.getOk() );
ASSERT_EQUALS( response.getErrCode(), 1234 );
ASSERT_EQUALS( response.getErrMessage(), "message" );
+ ASSERT( !mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, BasicUpsert) {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 1.0, err: null, n: 1, upserted : 'abcde'}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
MockSafeWriter mockWriter( gleResponses );
BatchSafeWriter batchWriter( &mockWriter );
@@ -318,6 +341,7 @@ namespace {
"abcde" );
ASSERT( !response.isErrDetailsSet() );
ASSERT( !response.isWriteConcernErrorSet() );
+ ASSERT( !mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, WriteError) {
@@ -327,6 +351,7 @@ namespace {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 1.0, err: 'message', code: 12345}" ) );
gleResponses.push_back( fromjson( "{ok: 1.0, err: null}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
MockSafeWriter mockWriter( gleResponses );
BatchSafeWriter batchWriter( &mockWriter );
@@ -348,6 +373,7 @@ namespace {
ASSERT_EQUALS( response.getErrDetailsAt(0)->getErrMessage(), "message" );
ASSERT_EQUALS( response.getErrDetailsAt(0)->getErrCode(), 12345 );
ASSERT( !response.isWriteConcernErrorSet() );
+ ASSERT( !mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, WriteErrorAndReplError) {
@@ -356,6 +382,7 @@ namespace {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 1.0, err: 'message', code: 12345}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
gleResponses.push_back( fromjson( "{ok: 1.0, err: 'norepl', wnote: 'message'}" ) );
MockSafeWriter mockWriter( gleResponses );
@@ -370,7 +397,6 @@ namespace {
BatchedCommandResponse response;
batchWriter.safeWriteBatch( NULL, cmdRequest, &response );
-
ASSERT( response.getOk() );
ASSERT_EQUALS( response.getN(), 1 );
ASSERT_EQUALS( response.sizeErrDetails(), 1u );
@@ -381,6 +407,7 @@ namespace {
ASSERT_EQUALS( response.getWriteConcernError()->getErrMessage(), "message" );
ASSERT_EQUALS( response.getWriteConcernError()->getErrCode(),
ErrorCodes::WriteConcernFailed );
+ ASSERT( !mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, FinalWriteErrorAndReplError) {
@@ -417,6 +444,7 @@ namespace {
ASSERT_EQUALS( response.getWriteConcernError()->getErrMessage(), "message" );
ASSERT_EQUALS( response.getWriteConcernError()->getErrCode(),
ErrorCodes::WriteConcernFailed );
+ ASSERT( mockWriter.isClearCalled() );
}
TEST(WriteBatchDownconvert, ReportOpTime) {
@@ -427,6 +455,7 @@ namespace {
vector<BSONObj> gleResponses;
gleResponses.push_back( fromjson( "{ok: 1.0, err: null, lastOp: Timestamp(10, 0)}" ) );
gleResponses.push_back( fromjson( "{ok: 1.0, err: null, lastOp: Timestamp(20, 0)}" ) );
+ gleResponses.push_back( fromjson( "{ok: 1.0}" ) );
MockSafeWriter mockWriter( gleResponses );
BatchSafeWriter batchWriter( &mockWriter );
@@ -445,6 +474,7 @@ namespace {
ASSERT( !response.isErrDetailsSet() );
ASSERT( !response.isWriteConcernErrorSet() );
ASSERT_EQUALS( response.getLastOp().toStringPretty(), OpTime(20, 0).toStringPretty() );
+ ASSERT( !mockWriter.isClearCalled() );
}
//
diff --git a/src/mongo/s/write_ops/batch_upconvert.cpp b/src/mongo/s/write_ops/batch_upconvert.cpp
index d371fde5848..59ff4a272cb 100644
--- a/src/mongo/s/write_ops/batch_upconvert.cpp
+++ b/src/mongo/s/write_ops/batch_upconvert.cpp
@@ -175,8 +175,15 @@ namespace mongo {
}
else if ( response.isErrDetailsSet() ) {
// The last error in the batch is always reported - this matches expected COE
- // semantics for insert batches and works for single writes
- lastBatchError = response.getErrDetails().back();
+ // semantics for insert batches. For updates and deletes, error is only reported
+ // if the error was on the last item.
+
+ const bool lastOpErrored = response.getErrDetails().back()->getIndex() ==
+ static_cast<int>(request.sizeWriteOps() - 1);
+ if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ||
+ lastOpErrored ) {
+ lastBatchError = response.getErrDetails().back();
+ }
}
else {
// We don't care about write concern errors, these happen in legacy mode in GLE.
diff --git a/src/mongo/s/write_ops/dbclient_safe_writer.cpp b/src/mongo/s/write_ops/dbclient_safe_writer.cpp
index c5370948cb5..143414fcf5d 100644
--- a/src/mongo/s/write_ops/dbclient_safe_writer.cpp
+++ b/src/mongo/s/write_ops/dbclient_safe_writer.cpp
@@ -96,9 +96,6 @@ namespace mongo {
BSONObj* gleResponse ) {
try {
- BSONObj resetResponse; // ignored, always ok
- conn->runCommand( dbName.toString(), BSON( "resetError" << 1 ), resetResponse );
-
BSONObjBuilder gleCmdB;
gleCmdB.append( "getLastError", true );
gleCmdB.appendElements( writeConcern );
@@ -112,4 +109,17 @@ namespace mongo {
return Status::OK();
}
+ Status DBClientSafeWriter::clearErrors( DBClientBase* conn,
+ const StringData& dbName ) {
+ try {
+ BSONObj resetResponse; // ignored, always ok.
+ conn->runCommand( dbName.toString(), BSON( "resetError" << 1 ), resetResponse );
+ }
+ catch ( const DBException& ex ) {
+ return ex.toStatus();
+ }
+
+ return Status::OK();
+ }
+
}
diff --git a/src/mongo/s/write_ops/dbclient_safe_writer.h b/src/mongo/s/write_ops/dbclient_safe_writer.h
index c4656281651..f1f07f4d0d3 100644
--- a/src/mongo/s/write_ops/dbclient_safe_writer.h
+++ b/src/mongo/s/write_ops/dbclient_safe_writer.h
@@ -57,6 +57,8 @@ namespace mongo {
const BSONObj& writeConcern,
BSONObj* gleResponse );
+ Status clearErrors( DBClientBase* conn,
+ const StringData& dbName );
};
}
diff --git a/src/mongo/shell/bulk_api.js b/src/mongo/shell/bulk_api.js
index f4b2a8c3ca7..5af814b81d9 100644
--- a/src/mongo/shell/bulk_api.js
+++ b/src/mongo/shell/bulk_api.js
@@ -46,13 +46,6 @@ var _bulk_api_module = (function() {
return db.runCommand( cmd );
};
- var enforceWriteConcern = function(db, options) {
- // Reset previous errors so we can apply the write concern no matter what
- // as long as it is valid.
- db.runCommand({ resetError: 1 });
- return executeGetLastError(db, options);
- };
-
/**
* Wraps the result for write commands and presents a convenient api for accessing
* single results & errors (returns the last one if there are multiple).
@@ -888,12 +881,27 @@ var _bulk_api_module = (function() {
}
}
- // The write concern may have not been enforced if we did it earlier and a write
- // error occurs, so we apply the actual write concern at the end.
- if (batchResult.writeErrors.length == 0 ||
- !ordered && (batchResult.writeErrors.length < batch.operations.length)) {
- result = enforceWriteConcern(collection.getDB(), writeConcern);
- extractedError = extractGLEErrors(result);
+ var needToEnforceWC = writeConcern != null &&
+ bsonWoCompare(writeConcern, { w: 1 }) != 0 &&
+ bsonWoCompare(writeConcern, { w: 0 }) != 0;
+
+ if (needToEnforceWC &&
+ (batchResult.writeErrors.length == 0 ||
+ (!ordered &&
+ // not all errored.
+ batchResult.writeErrors.length < batch.operations.length))) {
+
+ // if last write errored
+ if( batchResult.writeErrors.length > 0 &&
+ batchResult.writeErrors[batchResult.writeErrors.length - 1].index ==
+ (batch.operations.length - 1)) {
+ // Reset previous errors so we can apply the write concern no matter what
+ // as long as it is valid.
+ collection.getDB().runCommand({ resetError: 1 });
+ }
+
+ result = executeGetLastError(collection.getDB(), writeConcern);
+ extractedError = extractGLEErrors(result);
}
if (extractedError != null && extractedError.wcError != null) {