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
|
/**
* Ensures that a createIndexes command request inside a transaction immediately errors if an
* existing index build of a duplicate index is already in progress outside of the transaction.
* @tags: [
* uses_transactions,
* ]
*/
(function() {
"use strict";
load("jstests/libs/parallel_shell_helpers.js");
load('jstests/libs/fail_point_util.js');
load('jstests/libs/test_background_ops.js');
const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();
const dbName = "test";
const collName = "create_indexes_waits_for_already_in_progress";
const primary = rst.getPrimary();
const testDB = primary.getDB(dbName);
const testColl = testDB.getCollection(collName);
const indexSpecB = {
key: {b: 1},
name: "the_b_1_index"
};
const indexSpecC = {
key: {c: 1},
name: "the_c_1_index"
};
assert.commandWorked(testDB.runCommand({create: collName}));
const runSuccessfulIndexBuild = function(dbName, collName, indexSpec, requestNumber) {
jsTest.log("Index build request " + requestNumber + " starting...");
const res = db.getSiblingDB(dbName).runCommand({createIndexes: collName, indexes: [indexSpec]});
jsTest.log("Index build request " + requestNumber +
", expected to succeed, result: " + tojson(res));
assert.commandWorked(res);
};
const runFailedIndexBuildInTxn = function(dbName, collName, indexSpec, requestNumber) {
const session = db.getMongo().startSession();
const sessionDB = session.getDatabase(dbName);
const sessionColl = sessionDB[collName];
jsTest.log("Index build request " + requestNumber + " starting in a transaction...");
session.startTransaction();
const res = sessionColl.runCommand({createIndexes: collName, indexes: [indexSpec]});
jsTest.log("Index build request " + requestNumber +
", expected to fail, result: " + tojson(res));
assert.commandFailedWithCode(res, ErrorCodes.IndexBuildAlreadyInProgress);
assert.commandFailedWithCode(session.abortTransaction_forTesting(),
ErrorCodes.NoSuchTransaction);
};
// Insert document into collection to avoid optimization for index creation on an empty collection.
// This allows us to pause index builds on the collection using a fail point.
assert.commandWorked(testColl.insert({a: 1}));
const failPoint = configureFailPoint(testDB, 'hangAfterSettingUpIndexBuild');
let joinFirstIndexBuild;
let joinSecondIndexBuild;
try {
jsTest.log("Starting a parallel shell to run first index build request...");
joinFirstIndexBuild = startParallelShell(
funWithArgs(runSuccessfulIndexBuild, dbName, collName, indexSpecB, 1), primary.port);
jsTest.log("Waiting for first index build to get started...");
failPoint.wait();
jsTest.log(
"Starting a parallel shell to run a transaction with a second index build request...");
joinSecondIndexBuild = startParallelShell(
funWithArgs(runFailedIndexBuildInTxn, dbName, collName, indexSpecB, 2), primary.port);
// We wait to observe the second attempt to build the index fails while the
// hangAfterSettingUpIndexBuild is preventing the first attempt from completing successfully.
joinSecondIndexBuild();
} finally {
failPoint.off();
}
joinFirstIndexBuild();
// We should have the _id index and the 'the_b_1_index' index just built.
assert.eq(testColl.getIndexes().length, 2);
rst.stopSet();
})();
|