1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
/**
* Tests that find, count, distinct, (non-writing) aggregation, (non-writing) mapReduce,
* listCollection and listIndexes commands can run while a MODE_X collection lock is held.
*
* @tags: [
* ]
*/
(function() {
'use strict';
load("jstests/libs/fail_point_util.js");
load('jstests/libs/parallel_shell_helpers.js');
let conn = MongoRunner.runMongod({});
assert(conn);
const dbName = 'testDatabase';
const collName = 'testCollection';
const db = conn.getDB(dbName);
const coll = db.getCollection(collName);
// Set up data to read.
assert.commandWorked(coll.insert({_id: 1, topGroupId: "A", subGroupCount: 4}));
assert.commandWorked(coll.insert({_id: 2, topGroupId: "B", subGroupCount: 7}));
assert.commandWorked(coll.insert({_id: 3, topGroupId: "A", subGroupCount: 11}));
jsTestLog("Setting failpoint to block collMod operation after lock acquisition.");
const collModFailPointName = 'hangAfterDatabaseLock'; // Takes a coll MODE_X, database MODE_IX
let collModFailPoint = configureFailPoint(conn, collModFailPointName);
jsTestLog("Starting collMod that will hang after lock acquisition.");
const awaitBlockingCollMod = startParallelShell(() => {
// Runs a no-op collMod command.
assert.commandWorked(db.getSiblingDB('testDatabase').runCommand({collMod: 'testCollection'}));
}, conn.port);
jsTestLog("Waiting for collMod to acquire a database lock.");
collModFailPoint.wait();
jsTestLog("Starting lock-free find command.");
// Set a batch size of 1, so there are more documents for the subsequent getMore command to fetch.
const findResult = db.runCommand({find: collName, batchSize: 1});
assert.commandWorked(findResult);
assert.eq(1, findResult.cursor.firstBatch.length);
jsTestLog("Starting lock-free getMore command");
const getMoreResult =
db.runCommand({getMore: NumberLong(findResult.cursor.id), collection: collName, batchSize: 1});
assert.commandWorked(getMoreResult);
assert.eq(1, getMoreResult.cursor.nextBatch.length);
jsTestLog("Starting lock-free count command.");
const countResult = coll.find().count();
assert.eq(3, countResult);
jsTestLog("Starting lock-free distinct command.");
const distinctResult = coll.distinct("topGroupId");
assert.eq(["A", "B"], distinctResult.sort());
jsTestLog("Starting lock-free aggregation command.");
const aggregationResult = coll.aggregate([
{$match: {topGroupId: "A"}},
{$group: {_id: "$topGroupId", totalTopGroupCount: {$sum: "$subGroupCount"}}}
]);
const aggregationDocuments = aggregationResult.toArray();
assert.eq(1, aggregationDocuments.length);
assert.eq(15, aggregationDocuments[0].totalTopGroupCount);
jsTestLog("Starting lock-free mapReduce command.");
const mapReduceResult = coll.mapReduce(
function() {
emit(this.topGroupId, this.subGroupCount);
},
function(key, values) {
return Array.sum(values);
},
// Return the results to the user rather than taking a collection IX lock to write them to a
// collection.
{out: {inline: 1}});
assert.commandWorked(mapReduceResult);
assert.eq(2, mapReduceResult.results.length);
jsTestLog("Starting lock-free listCollections command.");
const listCollectionsResult = db.runCommand({listCollections: 1});
assert.commandWorked(listCollectionsResult);
assert.eq(1, listCollectionsResult.cursor.firstBatch.length);
jsTestLog("Starting lock-free listIndexes command.");
const listIndexesResult = db.runCommand({listIndexes: collName});
assert.commandWorked(listIndexesResult);
assert.eq(1, listIndexesResult.cursor.firstBatch.length);
jsTestLog("Turning off failpoint.");
collModFailPoint.off();
jsTestLog("Waiting for unstalled collMod operation to finish.");
awaitBlockingCollMod();
jsTestLog("Done.");
MongoRunner.stopMongod(conn);
})();
|