[SERVER-32078] Creating collection with specific UUID using applyOps can roll back a pending collection drop Created: 22/Nov/17  Updated: 06/Dec/22  Resolved: 30/Nov/17

Status: Closed
Project: Core Server
Component/s: Replication
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: William Schultz (Inactive) Assignee: Backlog - Replication Team
Resolution: Duplicate Votes: 0
Labels: rbfz
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
Duplicate
duplicates SERVER-32098 Disallow operations on drop-pending c... Closed
Assigned Teams:
Replication
Operating System: ALL
Steps To Reproduce:

 
load("jstests/libs/check_log.js");  // For 'checkLog'.
 
// Pause oplog application on a specified node.
function pauseOplogApplication(node) {
    assert.commandWorked(
        node.adminCommand({configureFailPoint: "rsSyncApplyStop", mode: "alwaysOn"}));
    checkLog.contains(node, "rsSyncApplyStop fail point enabled");
}
 
// Resume oplog application on a specified node.
function resumeOplogApplication(node) {
    assert.commandWorked(node.adminCommand({configureFailPoint: "rsSyncApplyStop", mode: "off"}));
}
 
var replTest = new ReplSetTest({name: "applyOpsUUID", nodes: 2});
 
replTest.startSet();
replTest.initiate();
replTest.awaitReplication();
 
// Pause application on secondary so collection drop doesn't commit.
pauseOplogApplication(replTest.getSecondary());
 
// Get connections and collection.
let primary = replTest.getPrimary();
let pdb = primary.getDB("test");
let collName = "coll";
 
// Create collection.
assert.commandWorked(pdb.createCollection(collName));
 
// Get the UUID for 'coll'.
let uuid = pdb.getCollectionInfos()[0].info.uuid;
 
jsTestLog("Dropping collection.");
assert.commandWorked(pdb.runCommand({drop: collName}));
assert.commandWorked(pdb.createCollection("otherColl"));
 
// Create collection with UUID via applyOps.
let ops = [{
    "op": "c",
    "ns": "test.$cmd",
    "ui": uuid,
    "o": {
        "create": collName,
        "idIndex": {"v": 2, "key": {"_id": 1}, "name": "_id_", "ns": "test.coll"}
    }
}];
jsTestLog("Doing 'applyOps' command.");
assert.commandWorked(pdb.adminCommand({applyOps: ops}));
 
// Dump oplog of primary.
jsTestLog("Primary oplog.");
let oplog = primary.getDB("local")["oplog.rs"].find().toArray();
jsTestLog(tojson(oplog));
 
jsTestLog("Resuming oplog application.");
resumeOplogApplication(replTest.getSecondary());
 
// Check data consistency.
replTest.awaitReplication();
replTest.checkReplicatedDataHashes();

Participants:
Linked BF Score: 0

 Description   

If a collection on a primary is in "drop-pending" state, by executing a create collection command via the applyOps command using the original UUID of that collection it is possible to erroneously "roll back" that collection i.e. remove it from the "drop-pending" state, and make it a normal collection again. Presumably, the correct thing to do would be to disallow collection creation operations via applyOps on collection UUIDs that are currently in drop-pending state.

This issue can lead to a db hash mismatch between a primary and secondary, since the drop-pending "roll back" gets logged in the oplog as a renameCollection operation. When a secondary applies the initial collection drop (the drop prepare), it might physically drop the collection before it applies the later renameCollection operation from the primary. If it tries to then apply the renameCollection operation on a collection that has been physically dropped, it will presumably fail silently, leaving it permanently missing a collection that the primary still has. See repro script.


Generated at Thu Feb 08 04:29:07 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.