summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Swanson <charlie.swanson@mongodb.com>2016-01-13 15:23:27 -0500
committerCharlie Swanson <charlie.swanson@mongodb.com>2016-01-21 09:29:23 -0500
commitf1ba338629578aaa6bff4ccadd4efbb8f862b37d (patch)
tree2e0de1e6f927dada888b82a2aa15eeb8b84cda20
parentdafe72f68c482ee0fdbd737ff8e404b3b39df27a (diff)
downloadmongo-f1ba338629578aaa6bff4ccadd4efbb8f862b37d.tar.gz
SERVER-22002 Do not retry findAndModify on MMAPv1
-rw-r--r--jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js27
-rw-r--r--jstests/concurrency/fsm_workloads/findAndModify_update_queue.js12
-rw-r--r--src/mongo/db/exec/delete.cpp10
-rw-r--r--src/mongo/db/exec/update.cpp11
-rw-r--r--src/mongo/dbtests/query_stage_delete.cpp45
-rw-r--r--src/mongo/dbtests/query_stage_update.cpp58
6 files changed, 29 insertions, 134 deletions
diff --git a/jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js b/jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js
index e1fd1f747d4..9df0e8c8636 100644
--- a/jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js
+++ b/jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js
@@ -10,6 +10,8 @@
*
* This workload was designed to reproduce SERVER-18304.
*/
+load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
+
var $config = (function() {
var data = {
@@ -65,10 +67,15 @@ var $config = (function() {
assertAlways.commandWorked(res);
var doc = res.value;
- assertWhenOwnColl.neq(
- doc, null, 'findAndModify should have found and removed a matching document');
+ if (isMongod(db) && !isMMAPv1(db)) {
+ // MMAPv1 does not automatically retry if there was a conflict, so it is expected
+ // that it may return null in the case of a conflict. All other storage engines
+ // should automatically retry the operation, and thus should never return null.
+ assertWhenOwnColl.neq(
+ doc, null, 'findAndModify should have found and removed a matching document');
+ }
if (doc !== null) {
- this.saveDocId.call(this, db, collName, doc._id);
+ this.saveDocId(db, collName, doc._id);
}
}
@@ -106,10 +113,16 @@ var $config = (function() {
var ownedDB = db.getSiblingDB(db.getName() + this.uniqueDBName);
if (this.opName === 'removed') {
- // Each findAndModify should remove exactly one document, and this.numDocs ==
- // this.iterations * this.threadCount, so there should not be any documents remaining.
- assertWhenOwnColl.eq(
- db[collName].find().itcount(), 0, 'Expected all documents to have been removed');
+ if (isMongod(db) && !isMMAPv1(db)) {
+ // On storage engines other than MMAPv1, each findAndModify should remove exactly
+ // one document. This is not true on MMAPv1 since it will not automatically retry a
+ // findAndModify when there is a conflict, indicating there were no matches instead.
+ // Since this.numDocs == this.iterations * this.threadCount, there should not be any
+ // documents remaining.
+ assertWhenOwnColl.eq(db[collName].find().itcount(),
+ 0,
+ 'Expected all documents to have been removed');
+ }
}
assertWhenOwnColl(function() {
diff --git a/jstests/concurrency/fsm_workloads/findAndModify_update_queue.js b/jstests/concurrency/fsm_workloads/findAndModify_update_queue.js
index 920be7feb66..0ca53015b68 100644
--- a/jstests/concurrency/fsm_workloads/findAndModify_update_queue.js
+++ b/jstests/concurrency/fsm_workloads/findAndModify_update_queue.js
@@ -13,6 +13,7 @@
*/
load('jstests/concurrency/fsm_libs/extend_workload.js'); // for extendWorkload
load('jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js'); // for $config
+load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
var $config = extendWorkload($config, function($config, $super) {
@@ -43,10 +44,15 @@ var $config = extendWorkload($config, function($config, $super) {
assertAlways.commandWorked(res);
var doc = res.value;
- assertWhenOwnColl.neq(
- doc, null, 'findAndModify should have found and updated a matching document');
+ if (isMongod(db) && !isMMAPv1(db)) {
+ // MMAPv1 does not automatically retry if there was a conflict, so it is expected
+ // that it may return null in the case of a conflict. All other storage engines
+ // should automatically retry the operation, and thus should never return null.
+ assertWhenOwnColl.neq(
+ doc, null, 'findAndModify should have found and updated a matching document');
+ }
if (doc !== null) {
- this.saveDocId.call(this, db, collName, doc._id);
+ this.saveDocId(db, collName, doc._id);
}
}
diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp
index 366ab76f108..3e5ece8d4d7 100644
--- a/src/mongo/db/exec/delete.cpp
+++ b/src/mongo/db/exec/delete.cpp
@@ -146,16 +146,6 @@ PlanStage::StageState DeleteStage::doWork(WorkingSetID* out) {
if (!member->hasLoc()) {
// We expect to be here because of an invalidation causing a force-fetch.
-
- // When we're doing a findAndModify with a sort, the sort will have a limit of 1, so will
- // not produce any more results even if there is another matching document. Throw a WCE here
- // so that these operations get another chance to find a matching document. The
- // findAndModify command should automatically retry if it gets a WCE.
- // TODO: this is not necessary if there was no sort specified.
- if (_params.returnDeleted) {
- throw WriteConflictException();
- }
-
++_specificStats.nInvalidateSkips;
return PlanStage::NEED_TIME;
}
diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp
index 504f3689481..7569e93b2cc 100644
--- a/src/mongo/db/exec/update.cpp
+++ b/src/mongo/db/exec/update.cpp
@@ -827,17 +827,6 @@ PlanStage::StageState UpdateStage::doWork(WorkingSetID* out) {
if (!member->hasLoc()) {
// We expect to be here because of an invalidation causing a force-fetch.
-
- // When we're doing a findAndModify with a sort, the sort will have a limit of 1, so
- // will not produce any more results even if there is another matching document.
- // Throw a WCE here so that these operations get another chance to find a matching
- // document. The findAndModify command should automatically retry if it gets a WCE. The
- // findAndModify command should automatically retry if it gets a WCE.
- // TODO: this is not necessary if there was no sort specified.
- if (_params.request->shouldReturnAnyDocs()) {
- throw WriteConflictException();
- }
-
++_specificStats.nInvalidateSkips;
return PlanStage::NEED_TIME;
}
diff --git a/src/mongo/dbtests/query_stage_delete.cpp b/src/mongo/dbtests/query_stage_delete.cpp
index 047cbefdb7f..4b6dacc27d1 100644
--- a/src/mongo/dbtests/query_stage_delete.cpp
+++ b/src/mongo/dbtests/query_stage_delete.cpp
@@ -302,50 +302,6 @@ public:
}
};
-/**
- * Test that a delete stage which has been asked to return the deleted document will throw an
- * exception when a child returns a WorkingSetMember in the OWNED_OBJ state. A WorkingSetMember in
- * the OWNED_OBJ state implies there was a conflict during execution.
- *
- * The delete stage is only asked to return documents during a findAndModify, and should throw a
- * WriteConflictException if there was a conflict. The findAndModify command will automatically
- * retry any WriteConflictExceptions.
- */
-class QueryStageDeleteShouldRetryConflictsForFindAndModify : public QueryStageDeleteBase {
-public:
- void run() {
- // Various variables we'll need.
- OldClientWriteContext ctx(&_txn, nss.ns());
- Collection* coll = ctx.getCollection();
- const BSONObj query = BSONObj();
- const auto ws = make_unique<WorkingSet>();
- const unique_ptr<CanonicalQuery> cq(canonicalize(query));
-
- // Configure a QueuedDataStage to pass an OWNED_OBJ to the delete stage.
- auto qds = make_unique<QueuedDataStage>(&_txn, ws.get());
- {
- WorkingSetID id = ws->allocate();
- WorkingSetMember* member = ws->get(id);
- member->obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
- member->transitionToOwnedObj();
- qds->pushBack(id);
- }
-
- // Configure the delete.
- DeleteStageParams deleteParams;
- deleteParams.isMulti = false;
- deleteParams.returnDeleted = true; // Emulate a findAndModify.
- deleteParams.canonicalQuery = cq.get();
-
- const auto deleteStage =
- make_unique<DeleteStage>(&_txn, deleteParams, ws.get(), coll, qds.release());
-
- // Call work, passing the set up member to the delete stage.
- WorkingSetID id = WorkingSet::INVALID_ID;
- ASSERT_THROWS(deleteStage->work(&id), WriteConflictException);
- }
-};
-
class All : public Suite {
public:
@@ -356,7 +312,6 @@ public:
add<QueryStageDeleteInvalidateUpcomingObject>();
add<QueryStageDeleteReturnOldDoc>();
add<QueryStageDeleteSkipOwnedObjects>();
- add<QueryStageDeleteShouldRetryConflictsForFindAndModify>();
}
};
diff --git a/src/mongo/dbtests/query_stage_update.cpp b/src/mongo/dbtests/query_stage_update.cpp
index f4721c7b9f3..10526f041ac 100644
--- a/src/mongo/dbtests/query_stage_update.cpp
+++ b/src/mongo/dbtests/query_stage_update.cpp
@@ -588,63 +588,6 @@ public:
}
};
-/**
- * Test that an update stage which has been asked to return the updated document will throw an
- * exception when a child returns a WorkingSetMember in the OWNED_OBJ state. A WorkingSetMember in
- * the OWNED_OBJ state implies there was a conflict during execution.
- *
- * The update stage is only asked to return the updated document during a findAndModify, and should
- * throw a WriteConflictException if there was a conflict. The findAndModify command will
- * automatically retry any WriteConflictExceptions.
- */
-class QueryStageUpdateShouldRetryConflictsForFindAndModify : public QueryStageUpdateBase {
-public:
- void run() {
- // Various variables we'll need.
- OldClientWriteContext ctx(&_txn, nss.ns());
- OpDebug* opDebug = &CurOp::get(_txn)->debug();
- Collection* coll = ctx.getCollection();
- UpdateLifecycleImpl updateLifecycle(false, nss);
- UpdateRequest request(nss);
- UpdateDriver driver((UpdateDriver::Options()));
- const BSONObj query = BSONObj();
- const auto ws = make_unique<WorkingSet>();
- const unique_ptr<CanonicalQuery> cq(canonicalize(query));
-
- // Populate the request.
- request.setQuery(query);
- request.setUpdates(fromjson("{$set: {x: 0}}"));
- request.setSort(BSONObj());
- request.setMulti(false);
- // Emulate a findAndModify.
- request.setReturnDocs(UpdateRequest::ReturnDocOption::RETURN_OLD);
- request.setLifecycle(&updateLifecycle);
-
- ASSERT_OK(driver.parse(request.getUpdates(), request.isMulti()));
-
- // Configure a QueuedDataStage to pass an OWNED_OBJ to the update stage.
- auto qds = make_unique<QueuedDataStage>(&_txn, ws.get());
- {
- WorkingSetID id = ws->allocate();
- WorkingSetMember* member = ws->get(id);
- member->obj = Snapshotted<BSONObj>(SnapshotId(), fromjson("{x: 1}"));
- member->transitionToOwnedObj();
- qds->pushBack(id);
- }
-
- // Configure the update.
- UpdateStageParams updateParams(&request, &driver, opDebug);
- updateParams.canonicalQuery = cq.get();
-
- const auto updateStage =
- make_unique<UpdateStage>(&_txn, updateParams, ws.get(), coll, qds.release());
-
- // Call work, passing the set up member to the update stage.
- WorkingSetID id = WorkingSet::INVALID_ID;
- ASSERT_THROWS(updateStage->work(&id), WriteConflictException);
- }
-};
-
class All : public Suite {
public:
All() : Suite("query_stage_update") {}
@@ -656,7 +599,6 @@ public:
add<QueryStageUpdateReturnOldDoc>();
add<QueryStageUpdateReturnNewDoc>();
add<QueryStageUpdateSkipOwnedObjects>();
- add<QueryStageUpdateShouldRetryConflictsForFindAndModify>();
}
};