-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
Copy pathquery_by_uuid_commit_pending.js
268 lines (231 loc) · 11 KB
/
query_by_uuid_commit_pending.js
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/**
* Test query by namespace and uuid while collection changes are in the commit pending state.
*/
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
const afterWTCommitFP = "hangBeforePublishingCatalogUpdates";
const beforeWTCommitFP = "hangAfterPreCommittingCatalogUpdates";
const dbName = jsTestName();
const createCollName = "collection_to_create";
const dropCollName = "collection_to_drop";
const renameSourceCollName = "collection_to_rename_source";
const renameDestinationCollName = "collection_to_rename_destination";
const dummyCollName = "dummy_collection_name"; // Used only to ensure the database is created.
let replTest = new ReplSetTest({
name: jsTestName(),
nodes: 1,
});
replTest.startSet();
replTest.initiate();
let primary = replTest.getPrimary();
let sessionConnection = new Mongo(replTest.getURL()).startSession();
const db = primary.getDB(dbName);
const nDocs = 5;
// This ensures the database exists before performing any commands against it.
assert.commandWorked(db.createCollection(dummyCollName));
function listCollectionsEntryWithFilter(db, collName) {
return assert.commandWorked(db.runCommand({listCollections: 1, filter: {name: collName}}))
.cursor.firstBatch[0];
}
function listCollectionsEntryWithoutFilter(db, collName) {
let listCollectionsCursor = assert.commandWorked(db.runCommand({listCollections: 1}));
let collections = new DBCommandCursor(db, listCollectionsCursor).toArray();
return collections.find((coll) => coll.name == collName);
}
const listCollectionVariants = [listCollectionsEntryWithFilter, listCollectionsEntryWithoutFilter];
function runTestCase(dbName,
collName,
parallelShellCommand,
getUUIDBeforeCommand,
fpName,
getListCollectionsEntryFn) {
jsTest.log(`Test case with: ${getListCollectionsEntryFn.name}`);
let uuid;
if (getUUIDBeforeCommand) {
uuid = getListCollectionsEntryFn(db, collName).info.uuid;
}
// Pause before committing any catalog changes in the collection catalog (but after
// durably committed).
const failPoint = configureFailPoint(primary, fpName, {collectionNS: dbName + '.' + collName});
const awaitResult = startParallelShell(parallelShellCommand, primary.port);
failPoint.wait();
if (!getUUIDBeforeCommand) {
let res = getListCollectionsEntryFn(db, collName);
if (res && res.info) {
uuid = res.info.uuid;
}
}
return {uuid: uuid, fp: failPoint, awaitResult: awaitResult};
}
function setupCreateTest() {
db[createCollName].drop();
}
jsTest.log(
"While a collection is commit pending for creation, we should return an empty version " +
"of the collection. If done inside a transaction, we still return an empty version of the " +
"collection since it's present in the transaction's WT snapshot.");
function testDurableCommitPendingCreate(getListCollectionsEntryFn) {
setupCreateTest();
let res =
runTestCase(dbName,
createCollName,
`{db.getSiblingDB('${dbName}').runCommand({create: '${createCollName}'});}`,
false,
afterWTCommitFP,
getListCollectionsEntryFn);
let nssRes = assert.commandWorked(db.runCommand({find: createCollName}));
let nssDocCount = new DBCommandCursor(db, nssRes).itcount();
assert.eq(nssDocCount, 0);
let uuidRes = assert.commandWorked(db.runCommand({find: res.uuid}));
let uuidDocCount = new DBCommandCursor(db, uuidRes).itcount();
assert.eq(nssDocCount, uuidDocCount);
sessionConnection.startTransaction();
let nssTransactionRes = assert.commandWorked(
sessionConnection.getDatabase(dbName).runCommand({find: createCollName}));
let nssTransactionCount = new DBCommandCursor(db, nssTransactionRes).itcount();
assert.eq(nssTransactionCount, 0);
sessionConnection.commitTransaction();
sessionConnection.startTransaction();
const uuidTransactionRes =
assert.commandWorked(sessionConnection.getDatabase(dbName).runCommand({find: res.uuid}));
const uuidTransactionCount = new DBCommandCursor(db, uuidTransactionRes).itcount();
assert.eq(nssTransactionCount, uuidTransactionCount);
sessionConnection.commitTransaction();
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testDurableCommitPendingCreate);
jsTest.log("Ensure that we cannot see the UUID and thus cannot find by UUID.");
function testNonDurableCommitPendingCreate(getListCollectionsEntryFn) {
setupCreateTest();
let res =
runTestCase(dbName,
createCollName,
`{db.getSiblingDB('${dbName}').runCommand({create: '${createCollName}'});}`,
false,
beforeWTCommitFP,
getListCollectionsEntryFn);
let nssRes = assert.commandWorked(db.runCommand({find: createCollName}));
let nssDocCount = new DBCommandCursor(db, nssRes).itcount();
assert.eq(nssDocCount, 0);
assert.eq(res.uuid, undefined);
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testNonDurableCommitPendingCreate);
function setupDropTest() {
db[dropCollName].drop();
assert.commandWorked(db.createCollection(dropCollName));
for (let i = 0; i < nDocs; i++) {
assert.commandWorked(db.getCollection(dropCollName).insert({x: i}));
}
}
function setupRenameTest() {
db[renameSourceCollName].drop();
db[renameDestinationCollName].drop();
// Setup collection to drop and collection to rename. This also ensures that the database
// exists.
assert.commandWorked(db.createCollection(renameSourceCollName));
// Insert some data so that there is something to find later in the test.
for (let i = 0; i < nDocs; i++) {
assert.commandWorked(db.getCollection(renameSourceCollName).insert({y: i}));
}
}
jsTest.log("Ensure that having a drop with a commit pending entry AFTER it has been made durable " +
"makes the find fail with NamespaceNotFound.");
function testDurableCommitPendingDrop(getListCollectionsEntryFn) {
setupDropTest();
let res = runTestCase(dbName,
dropCollName,
`{db.getSiblingDB('${dbName}').runCommand({drop: '${dropCollName}'});}`,
true,
afterWTCommitFP,
getListCollectionsEntryFn);
// Check that the results of queries are as expected.
let nssRes = assert.commandWorked(db.runCommand({find: dropCollName}));
let nssDocCount = new DBCommandCursor(db, nssRes).itcount();
assert.eq(nssDocCount, 0);
assert.commandFailedWithCode(db.runCommand({find: res.uuid}), ErrorCodes.NamespaceNotFound);
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testDurableCommitPendingDrop);
jsTest.log(
"Check that having a commit pending entry BEFORE it has been made durable doesn't affect find " +
"command when finding by UUID as it's still in a timeline before the drop has been made. That " +
"is, it should see the collection before the drop.");
function testNonDurableCommitPendingDrop(getListCollectionsEntryFn) {
setupDropTest();
let res = runTestCase(dbName,
dropCollName,
`{db.getSiblingDB('${dbName}').runCommand({drop: '${dropCollName}'});}`,
true,
beforeWTCommitFP,
getListCollectionsEntryFn);
// Check that the results of queries are as expected.
let nssRes = assert.commandWorked(db.runCommand({find: dropCollName}));
let nssDocCount = new DBCommandCursor(db, nssRes).itcount();
assert.eq(nssDocCount, nDocs);
assert.commandWorked(db.runCommand({find: res.uuid}));
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testNonDurableCommitPendingDrop);
jsTest.log(
"While a collection is commit pending for rename and it's been made durable, we should " +
"find the new namespace (and the old one should be handled as a drop). By uuid, we should " +
"find the new collection.");
function testDurableCommitPendingRename(getListCollectionsEntryFn) {
setupRenameTest();
let res =
runTestCase(dbName,
renameSourceCollName,
`{db.adminCommand({renameCollection: '${dbName}.${
renameSourceCollName}', to: '${dbName}.${renameDestinationCollName}'});}`,
true,
afterWTCommitFP,
getListCollectionsEntryFn);
// Check that the results of queries are as expected.
let sourceNssRes = assert.commandWorked(db.runCommand({find: renameSourceCollName}));
let sourceNssDocCount = new DBCommandCursor(db, sourceNssRes).itcount();
assert.eq(sourceNssDocCount, 0);
let destinationNssRes = assert.commandWorked(db.runCommand({find: renameDestinationCollName}));
let destinationNssDocCount = new DBCommandCursor(db, destinationNssRes).itcount();
assert.eq(destinationNssDocCount, nDocs);
let uuidRes = assert.commandWorked(db.runCommand({find: res.uuid}));
let uuidDocCount = new DBCommandCursor(db, uuidRes).itcount();
assert.eq(destinationNssDocCount, uuidDocCount);
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testDurableCommitPendingRename);
jsTest.log(
"Ensure that a rename operation that's published changes that are not yet durable doesn't make " +
"them visible to a snapshot taken before WT has committed the changes. Find should access the " +
"old collection rather than the new one by UUID.");
function testNonDurableCommitPendingRename(getListCollectionsEntryFn) {
setupRenameTest();
let res =
runTestCase(dbName,
renameSourceCollName,
`{db.adminCommand({renameCollection: '${dbName}.${
renameSourceCollName}', to: '${dbName}.${renameDestinationCollName}'});}`,
true,
beforeWTCommitFP,
getListCollectionsEntryFn);
// Check that the results of queries are as expected as the snapshot should be from before
// they've been made durable to WT.
let sourceNssRes = assert.commandWorked(db.runCommand({find: renameSourceCollName}));
let sourceNssDocCount = new DBCommandCursor(db, sourceNssRes).itcount();
assert.eq(sourceNssDocCount, nDocs);
let destinationNssRes = assert.commandWorked(db.runCommand({find: renameDestinationCollName}));
let destinationNssDocCount = new DBCommandCursor(db, destinationNssRes).itcount();
assert.eq(destinationNssDocCount, 0);
let uuidRes = assert.commandWorked(db.runCommand({find: res.uuid}));
let uuidDocCount = new DBCommandCursor(db, uuidRes).itcount();
assert.eq(sourceNssDocCount, uuidDocCount);
res.fp.off();
res.awaitResult();
}
listCollectionVariants.forEach(testNonDurableCommitPendingRename);
replTest.stopSet();