[SERVER-41007] recoverFromOplogAsStandalone doesn't work with prepared transactions Created: 03/May/19  Updated: 29/Oct/23  Resolved: 27/Jun/19

Status: Closed
Project: Core Server
Component/s: Replication
Affects Version/s: None
Fix Version/s: 4.2.0-rc3, 4.3.1

Type: Bug Priority: Major - P3
Reporter: Judah Schvimer Assignee: Vesselina Ratcheva (Inactive)
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Backports
Backwards Compatibility: Fully Compatible
Operating System: ALL
Backport Requested:
v4.2
Steps To Reproduce:

A modified version of standalone_replication_recovery.js

/*
 * Tests that a standalone succeeds when passed the 'recoverFromOplogAsStandalone' parameter.
 *
 * This test only makes sense for storage engines that support recover to stable timestamp.
 * @tags: [requires_wiredtiger, requires_persistence, requires_journaling, requires_replication,
 * requires_majority_read_concern]
 */
 
(function() {
    "use strict";
    load("jstests/replsets/rslib.js");
    load("jstests/libs/write_concern_util.js");
    load("jstests/core/txns/libs/prepare_helpers.js");
 
    const name = 'standalone_replication_recovery';
    const dbName = name;
    const collName = 'srr_coll';
    const logLevel = tojson({storage: {recovery: 3}});
 
    const rst = new ReplSetTest({
        nodes: 2,
    });
 
    function getColl(conn) {
        return conn.getDB(dbName)[collName];
    }
 
    function assertDocsInColl(node, nums) {
        let results = getColl(node).find().sort({_id: 1}).toArray();
        let expected = nums.map((i) => ({_id: i}));
        if (!friendlyEqual(results, expected)) {
            rst.dumpOplog(node, {}, 100);
        }
        assert.eq(results, expected, "actual (left) != expected (right)");
    }
 
    jsTestLog("Initiating as a replica set.");
    // Restart as a replica set node without the flag so we can add operations to the oplog.
    let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}});
    let node = nodes[0];
    let secondary = nodes[1];
    rst.initiate({
        _id: name,
        members: [{_id: 0, host: node.host}, {_id: 2, host: secondary.host, priority: 0}]
    });
 
    // Create the collection with w:majority and then perform a clean restart to ensure that
    // the collection is in a stable checkpoint.
    assert.commandWorked(node.getDB(dbName).runCommand({
        create: collName,
        writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS}
    }));
    assertDocsInColl(node, []);
    node = rst.restart(node, {"noReplSet": false});
    reconnect(node);
    assert.eq(rst.getPrimary(), node);
 
    const session = node.startSession();
    const sessionDb = session.getDatabase(dbName);
    const sessionColl = sessionDb.getCollection(collName + "2");
    assert.commandWorked(sessionColl.insert({_id: 1}));
    session.startTransaction({readConcern: {level: "snapshot"}, writeConcern: {w: 1, j: 1}});
    assert.commandWorked(sessionColl.insert({_id: 123457}));
    assert.commandWorked(sessionColl.insert({_id: 123458}));
    let prepareTimestamp = PrepareHelpers.prepareTransaction(session);
 
    // Keep node 0 the primary, but prevent it from committing any writes.
    stopServerReplication(secondary);
 
    assert.commandWorked(getColl(node).insert({_id: 3}, {writeConcern: {w: 1, j: 1}}));
    assert.commandWorked(getColl(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}}));
    assert.commandWorked(getColl(node).insert({_id: 5}, {writeConcern: {w: 1, j: 1}}));
    assertDocsInColl(node, [3, 4, 5]);
 
    assert.commandWorked(PrepareHelpers.commitTransaction(session, prepareTimestamp));
 
    jsTestLog("Test that on restart with the flag set we play recovery.");
    node = rst.restart(node, {
        noReplSet: true,
        setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
    });
    reconnect(node);
    assertDocsInColl(node, [3, 4, 5]);
 
    restartServerReplication(secondary);
 
    // Skip checking db hashes since we do a write as a standalone.
    TestData.skipCheckDBHashes = true;
    rst.stopSet();
})();

Sprint: Repl 2019-06-03, Repl 2019-06-17, Repl 2019-07-01
Participants:

 Description   

It invariants expecting a prepared transaction's inner ops to have a timestamp field. I'm not sure why this isn't a problem with transactions that commit without going through prepare.



 Comments   
Comment by Vesselina Ratcheva (Inactive) [ 27/Jun/19 ]

Author:

{'email': 'vesselina.ratcheva@10gen.com', 'name': 'Vesselina Ratcheva', 'username': 'vessy-mongodb'}

Message: SERVER-41007 Add support for 'recoverFromOplogAsStandalone' with prepared transactions
Branch: master
https://github.com/mongodb/mongo/commit/fd8538074a35666a8f18f54ffae798095cbc43f9

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