[SERVER-39690] Ensure transaction metrics are valid even if prepareTransaction fails midway Created: 20/Feb/19  Updated: 29/Oct/23  Resolved: 02/Apr/19

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

Type: Bug Priority: Minor - P4
Reporter: Jason Chan Assignee: Lingzhi Deng
Resolution: Fixed Votes: 0
Labels: prepare_diagnostics
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible
Operating System: ALL
Sprint: Repl 2019-04-08
Participants:

 Description   

Currently, if prepareTransaction fails midway and enters the abort guard, abortActiveTransaction is called. At this point, it is possible that the transaction has already transitioned into the kPrepared state, but the transactions metrics observer has not yet been called to increment the current and total number of prepared transactions. At this point, abortActiveTransaction will decrement the currentPrepared while incrementing the TotalPreparedThenAborted metrics. Therefore, it is possible to leave the metrics in a bad state if preparing a transactions fails.

Example test that will pass (incorrectly):

/**
 * Tests that failing while preparing a transaction leaves the server transaction metrics in an illegal state.
 *
 */
(function() {
    "use strict";
    load("jstests/core/txns/libs/prepare_helpers.js");
 
    const dbName = "test";
    const collName = "fail_prepare_transaction";
    const testDB = db.getSiblingDB(dbName);
    const testColl = testDB.getCollection(collName);
 
    testColl.drop({writeConcern: {w: "majority"}});
    assert.commandWorked(testDB.runCommand({create: collName, writeConcern: {w: "majority"}}));
 
    const session = testDB.getMongo().startSession({causalConsistency: false});
    const sessionDB = session.getDatabase(dbName);
    const sessionColl = sessionDB.getCollection(collName);
 
    const doc1 = {_id: 1, x: 1};
 
    session.startTransaction({readConcern: {level: 'snapshot'}});
    assert.commandWorked(sessionColl.insert(doc1));
    // Assuming prepareTransaction was to fail and enter the abortGuard.   
    assert.commandFailed(session.getDatabase('admin').adminCommand({prepareTransaction: 1}));
    let newStatus = assert.commandWorked(testDB.adminCommand({serverStatus: 1}));
 
    // This breaks the invariant that metrics cannot be negative.
    assert.eq(newStatus.transactions["currentPrepared"], -1);
    assert.eq(newStatus.transactions["totalPreparedThenAborted"], 1);
    assert.eq(newStatus.transactions["totalPreparedThenCommitted"], 0);
    assert.eq(newStatus.transactions["totalPrepared"], 0);
    session.endSession();
}());

We should either delay transitioning to kPrepared until the abortGuard is dismissed or increment the current and total prepared metrics earlier.



 Comments   
Comment by Githook User [ 02/Apr/19 ]

Author:

{'name': 'Lingzhi Deng', 'username': 'ldennis', 'email': 'lingzhi.deng@mongodb.com'}

Message: SERVER-39690: Fix prepared transaction metrics after exceptions in prepare

Comment by Judah Schvimer [ 21/Feb/19 ]

Marking as "minor" since while it is required, it can be done towards the end of the project.

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