mirror of https://github.com/mongodb/mongo
273 lines
11 KiB
JavaScript
273 lines
11 KiB
JavaScript
/*
|
|
* Tests the setFCV command creates/removes the partial config.transactions index for retryable
|
|
* transactions on upgrade/downgrade.
|
|
*
|
|
* @tags: [uses_transactions]
|
|
*/
|
|
(function() {
|
|
"use strict";
|
|
|
|
// This test does direct writes to config.transactions which requires not using a session.
|
|
TestData.disableImplicitSessions = true;
|
|
|
|
load("jstests/replsets/rslib.js");
|
|
|
|
// Verifies both the _id index and partial parent_lsid index exists for config.transactions.
|
|
function assertPartialIndexExists(node) {
|
|
const configDB = node.getDB("config");
|
|
const indexSpecs = assert.commandWorked(configDB.runCommand({"listIndexes": "transactions"}))
|
|
.cursor.firstBatch;
|
|
indexSpecs.sort((index0, index1) => index0.name > index1.name);
|
|
assert.eq(indexSpecs.length, 2);
|
|
const idIndexSpec = indexSpecs[0];
|
|
assert.eq(idIndexSpec.key, {"_id": 1});
|
|
const partialIndexSpec = indexSpecs[1];
|
|
assert.eq(partialIndexSpec.name, "parent_lsid");
|
|
assert.eq(partialIndexSpec.key, {"parentLsid": 1, "_id.txnNumber": 1, "_id": 1});
|
|
assert.eq(partialIndexSpec.partialFilterExpression, {"parentLsid": {"$exists": true}});
|
|
}
|
|
|
|
// Verifies only the _id index exists for config.transactions.
|
|
function assertPartialIndexDoesNotExist(node) {
|
|
const configDB = node.getDB("config");
|
|
const indexSpecs = assert.commandWorked(configDB.runCommand({"listIndexes": "transactions"}))
|
|
.cursor.firstBatch;
|
|
assert.eq(indexSpecs.length, 1);
|
|
const idIndexSpec = indexSpecs[0];
|
|
assert.eq(idIndexSpec.key, {"_id": 1});
|
|
}
|
|
|
|
/**
|
|
* Verifies the partial index is dropped/created on FCV transitions and retryable writes work in all
|
|
* FCVs.
|
|
*/
|
|
function runTest(
|
|
setFCVConn, modifyIndexConns, verifyIndexConns, rst, alwaysCreateFeatureFlagEnabled) {
|
|
// Start at latest FCV which should have the index.
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexExists(conn);
|
|
});
|
|
|
|
// Downgrade to last LTS removes index.
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexDoesNotExist(conn);
|
|
});
|
|
|
|
assert.commandWorked(setFCVConn.getDB("foo").runCommand(
|
|
{insert: "bar", documents: [{x: 1}], lsid: {id: UUID()}, txnNumber: NumberLong(11)}));
|
|
|
|
if (rst) {
|
|
// On step up to primary the index should not be created. Note this tests the empty
|
|
// collection case when alwaysCreateFeatureFlagEnabled is true.
|
|
|
|
let primary = rst.getPrimary();
|
|
// Clear the collection so we'd try to create the index.
|
|
assert.commandWorked(primary.getDB("config").transactions.remove({}));
|
|
assert.commandWorked(
|
|
primary.adminCommand({replSetStepDown: ReplSetTest.kForeverSecs, force: true}));
|
|
assert.commandWorked(primary.adminCommand({replSetFreeze: 0}));
|
|
|
|
setFCVConn = rst.getPrimary();
|
|
modifyIndexConns = [rst.getPrimary()];
|
|
rst.awaitReplication();
|
|
verifyIndexConns.forEach(conn => {
|
|
reconnect(conn);
|
|
if (alwaysCreateFeatureFlagEnabled) {
|
|
assertPartialIndexExists(conn);
|
|
} else {
|
|
assertPartialIndexDoesNotExist(conn);
|
|
}
|
|
});
|
|
|
|
if (alwaysCreateFeatureFlagEnabled) {
|
|
// The test expects no index after this block, so remove it.
|
|
modifyIndexConns.forEach(conn => {
|
|
assert.commandWorked(
|
|
conn.getCollection("config.transactions").dropIndex("parent_lsid"));
|
|
});
|
|
}
|
|
rst.awaitReplication();
|
|
}
|
|
|
|
assert.commandWorked(setFCVConn.getDB("foo").runCommand(
|
|
{insert: "bar", documents: [{x: 1}], lsid: {id: UUID()}, txnNumber: NumberLong(11)}));
|
|
|
|
// Upgrade from last LTS to latest adds index.
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexExists(conn);
|
|
});
|
|
|
|
assert.commandWorked(setFCVConn.getDB("foo").runCommand(
|
|
{insert: "bar", documents: [{x: 1}], lsid: {id: UUID()}, txnNumber: NumberLong(11)}));
|
|
|
|
// Downgrade from latest to last continuous removes index.
|
|
assert.commandWorked(
|
|
setFCVConn.adminCommand({setFeatureCompatibilityVersion: lastContinuousFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexDoesNotExist(conn);
|
|
});
|
|
|
|
if (rst) {
|
|
// On step up to primary the index should not be created. Note this tests the non-empty
|
|
// collection case when alwaysCreateFeatureFlagEnabled is true.
|
|
|
|
let primary = rst.getPrimary();
|
|
// Clear the collection so we'd try to create the index. Skip if the always create index
|
|
// feature flag is on because we'd try to create the index anyway.
|
|
if (!alwaysCreateFeatureFlagEnabled) {
|
|
assert.commandWorked(primary.getDB("config").transactions.remove({}));
|
|
}
|
|
assert.commandWorked(
|
|
primary.adminCommand({replSetStepDown: ReplSetTest.kForeverSecs, force: true}));
|
|
assert.commandWorked(primary.adminCommand({replSetFreeze: 0}));
|
|
|
|
setFCVConn = rst.getPrimary();
|
|
modifyIndexConns = [rst.getPrimary()];
|
|
rst.awaitReplication();
|
|
verifyIndexConns.forEach(conn => {
|
|
reconnect(conn);
|
|
if (alwaysCreateFeatureFlagEnabled) {
|
|
assertPartialIndexExists(conn);
|
|
} else {
|
|
assertPartialIndexDoesNotExist(conn);
|
|
}
|
|
});
|
|
|
|
if (alwaysCreateFeatureFlagEnabled) {
|
|
// The test expects no index after this block, so remove it.
|
|
modifyIndexConns.forEach(conn => {
|
|
assert.commandWorked(
|
|
conn.getCollection("config.transactions").dropIndex("parent_lsid"));
|
|
});
|
|
}
|
|
rst.awaitReplication();
|
|
}
|
|
|
|
assert.commandWorked(setFCVConn.getDB("foo").runCommand(
|
|
{insert: "bar", documents: [{x: 1}], lsid: {id: UUID()}, txnNumber: NumberLong(11)}));
|
|
|
|
// Upgrade from last continuous to latest LTS adds index.
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexExists(conn);
|
|
});
|
|
|
|
if (rst) {
|
|
// On step up to primary the index should be created.
|
|
|
|
let primary = rst.getPrimary();
|
|
// Clear the collection so we'd try to create the index. Skip if the always create index
|
|
// feature flag is on because we'd try to create the index anyway.
|
|
if (!alwaysCreateFeatureFlagEnabled) {
|
|
assert.commandWorked(primary.getDB("config").transactions.remove({}));
|
|
}
|
|
assert.commandWorked(
|
|
primary.adminCommand({replSetStepDown: ReplSetTest.kForeverSecs, force: true}));
|
|
assert.commandWorked(primary.adminCommand({replSetFreeze: 0}));
|
|
|
|
setFCVConn = rst.getPrimary();
|
|
modifyIndexConns = [rst.getPrimary()];
|
|
rst.awaitReplication();
|
|
verifyIndexConns.forEach(conn => {
|
|
reconnect(conn);
|
|
assertPartialIndexExists(conn);
|
|
});
|
|
}
|
|
|
|
assert.commandWorked(setFCVConn.getDB("foo").runCommand(
|
|
{insert: "bar", documents: [{x: 1}], lsid: {id: UUID()}, txnNumber: NumberLong(11)}));
|
|
|
|
// Verify downgrading ignores IndexNotFound.
|
|
modifyIndexConns.forEach(conn => {
|
|
assert.commandWorked(conn.getCollection("config.transactions").dropIndex("parent_lsid"));
|
|
});
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexDoesNotExist(conn);
|
|
});
|
|
|
|
// Verify upgrading works if the index already exists.
|
|
modifyIndexConns.forEach(conn => {
|
|
assert.commandWorked(conn.getDB("config").runCommand({
|
|
createIndexes: "transactions",
|
|
indexes: [{
|
|
v: 2,
|
|
partialFilterExpression: {parentLsid: {$exists: true}},
|
|
name: "parent_lsid",
|
|
key: {parentLsid: 1, "_id.txnNumber": 1, _id: 1}
|
|
}],
|
|
}));
|
|
});
|
|
assert.commandWorked(setFCVConn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
|
verifyIndexConns.forEach(conn => {
|
|
assertPartialIndexExists(conn);
|
|
});
|
|
}
|
|
|
|
{
|
|
const st = new ShardingTest({shards: 1, rs: {nodes: 2}});
|
|
// Note setFCV always waits for majority write concern so in a two node cluster secondaries will
|
|
// always have replicated the setFCV writes.
|
|
runTest(st.s, [st.rs0.getPrimary(), st.configRS.getPrimary()], [
|
|
st.rs0.getPrimary(),
|
|
st.rs0.getSecondary(),
|
|
st.configRS.getPrimary(),
|
|
st.configRS.getSecondary()
|
|
]);
|
|
st.stop();
|
|
}
|
|
|
|
{
|
|
const rst = new ReplSetTest({nodes: 2});
|
|
rst.startSet();
|
|
rst.initiate();
|
|
// Note setFCV always waits for majority write concern so in a two node cluster secondaries will
|
|
// always have replicated the setFCV writes.
|
|
runTest(rst.getPrimary(), [rst.getPrimary()], [rst.getPrimary(), rst.getSecondary()], rst);
|
|
rst.stopSet();
|
|
}
|
|
|
|
{
|
|
// Enabling featureFlagAlwaysCreateConfigTransactionsPartialIndexOnStepUp should not lead to
|
|
// creating the index if the internal transactions feature flag is not enabled.
|
|
const featureFlagRst = new ReplSetTest({
|
|
nodes: 2,
|
|
nodeOptions:
|
|
{setParameter: "featureFlagAlwaysCreateConfigTransactionsPartialIndexOnStepUp=true"}
|
|
});
|
|
featureFlagRst.startSet();
|
|
featureFlagRst.initiate();
|
|
// Note setFCV always waits for majority write concern so in a two node cluster secondaries will
|
|
// always have replicated the setFCV writes.
|
|
runTest(featureFlagRst.getPrimary(),
|
|
[featureFlagRst.getPrimary()],
|
|
[featureFlagRst.getPrimary(), featureFlagRst.getSecondary()],
|
|
featureFlagRst,
|
|
true /* alwaysCreateFeatureFlagEnabled */);
|
|
featureFlagRst.stopSet();
|
|
}
|
|
|
|
{
|
|
const conn = MongoRunner.runMongod();
|
|
|
|
const configTxnsCollection = conn.getCollection("config.transactions");
|
|
assert(!configTxnsCollection.exists());
|
|
|
|
// Verify each upgrade/downgrade path can succeed and won't implicitly create
|
|
// config.transactions, which doesn't exist on standalone mongods.
|
|
assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV}));
|
|
assert(!configTxnsCollection.exists());
|
|
|
|
assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
|
assert(!configTxnsCollection.exists());
|
|
|
|
assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: lastContinuousFCV}));
|
|
assert(!configTxnsCollection.exists());
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
}
|
|
})();
|