Files
mongo/jstests/sharding/set_user_write_block_mode.js

282 lines
11 KiB
JavaScript

/**
* Tests sharding specific functionality of the setUserWriteBlockMode command. Non sharding specific
* aspects of this command should be checked on jstests/noPassthrough/set_user_write_block_mode.js
* instead.
*
* @tags: [
* requires_fcv_60,
* ]
*/
(function() {
"use strict";
load("jstests/libs/fail_point_util.js");
load('jstests/libs/parallel_shell_helpers.js');
load('jstests/sharding/libs/remove_shard_util.js');
const st = new ShardingTest({shards: 2});
const dbName = "test";
const collName = "foo";
const ns = dbName + "." + collName;
assert.commandWorked(
st.s.adminCommand({enableSharding: dbName, primaryShard: st.shard0.shardName}));
let db = st.s.getDB(dbName);
let coll = db[collName];
const newShardName = 'newShard';
const newShard = new ReplSetTest({name: newShardName, nodes: 1});
newShard.startSet({shardsvr: ''});
newShard.initiate();
// Test addShard sets the proper user writes blocking state on the new shard.
{
// Create a collection on the new shard before adding it to the cluster.
const newShardDB = 'newShardDB';
const newShardColl = 'newShardColl';
const newShardCollDirect = newShard.getPrimary().getDB(newShardDB).getCollection(newShardColl);
assert.commandWorked(newShardCollDirect.insert({x: 1}));
const newShardCollMongos = st.s.getDB(newShardDB).getCollection(newShardColl);
// Start blocking user writes.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
// Add a new shard.
assert.commandWorked(st.s.adminCommand({addShard: newShard.getURL(), name: newShardName}));
// Check that we cannot write on the new shard.
assert.commandFailedWithCode(newShardCollMongos.insert({x: 2}), ErrorCodes.UserWritesBlocked);
// Now unblock and check we can write to the new shard.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
assert.commandWorked(newShardCollMongos.insert({x: 2}));
// Block again and see we can remove the shard even when write blocking is enabled. Before
// removing the shard we first need to drop the dbs for which 'newShard' is the db-primary
// shard.
assert.commandWorked(st.s.getDB(newShardDB).dropDatabase());
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
removeShard(st, newShardName);
// Disable write blocking while 'newShard' is not part of the cluster.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
// Add the shard back and check that user write blocking is disabled.
assert.commandWorked(st.s.adminCommand({addShard: newShard.getURL(), name: newShardName}));
assert.commandWorked(newShardCollDirect.insert({x: 10}));
}
// Test addShard serializes with setUserWriteBlockMode.
{
// Start setUserWriteBlockMode and make it hang during the SetUserWriteBlockModeCoordinator
// execution.
let hangInShardsvrSetUserWriteBlockModeFailPoint =
configureFailPoint(st.shard0, "hangInShardsvrSetUserWriteBlockMode");
let awaitShell = startParallelShell(() => {
assert.commandWorked(db.adminCommand({setUserWriteBlockMode: 1, global: true}));
}, st.s.port);
hangInShardsvrSetUserWriteBlockModeFailPoint.wait();
assert.commandFailedWithCode(
st.s.adminCommand({addShard: newShard.getURL(), name: newShardName, maxTimeMS: 1000}),
ErrorCodes.MaxTimeMSExpired);
hangInShardsvrSetUserWriteBlockModeFailPoint.off();
awaitShell();
assert.commandWorked(st.s.adminCommand({addShard: newShard.getURL(), name: newShardName}));
assert.commandWorked(st.s.adminCommand({removeShard: newShardName}));
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
}
// Test chunk migrations work even if user writes are blocked
{
coll.drop();
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
// Insert a document to the chunk that will be migrated to ensure that the recipient will at
// least insert one document as part of the migration.
coll.insert({_id: 1});
// Create an index to check that the recipient, upon receiving its first chunk, can create it.
const indexKey = {x: 1};
assert.commandWorked(coll.createIndex(indexKey));
// Start blocking user writes.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
// Move one chunk to shard1.
assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
assert.commandWorked(
st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard1.shardName}));
assert.eq(1, coll.find({_id: 1}).itcount());
assert.eq(1, st.shard1.getDB(dbName)[collName].find({_id: 1}).itcount());
// Check that the index has been created on recipient.
ShardedIndexUtil.assertIndexExistsOnShard(st.shard1, dbName, collName, indexKey);
// Check that orphans are deleted from the donor.
assert.soon(() => {
return st.shard0.getDB(dbName)[collName].find({_id: 1}).itcount() === 0;
});
// Create an extra index on shard1. This index is not present in the shard0, thus shard1 will
// drop it when it receives its first chunk.
{
// Leave shard1 without any chunk.
assert.commandWorked(
st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard0.shardName}));
// Create an extra index on shard1.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
const extraIndexKey = {y: 1};
assert.commandWorked(st.shard1.getDB(dbName)[collName].createIndex(extraIndexKey));
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
// Move a chunk to shard1.
assert.commandWorked(
st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard1.shardName}));
// Check the mismatched index was dropped.
ShardedIndexUtil.assertIndexDoesNotExistOnShard(st.shard1, dbName, collName, extraIndexKey);
}
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
}
// Test movePrimary works while user writes are blocked.
{
// Create an unsharded collection so that its data needs to be cloned to the new db-primary.
const unshardedCollName = 'unshardedColl';
const unshardedColl = db[unshardedCollName];
assert.commandWorked(unshardedColl.createIndex({x: 1}));
unshardedColl.insert({x: 1});
// Start blocking user writes
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
const fromShard = st.getPrimaryShard(dbName);
const toShard = st.getOther(fromShard);
assert.commandWorked(st.s.adminCommand({movePrimary: dbName, to: toShard.name}));
// Check that the new primary has cloned the data.
assert.eq(1, toShard.getDB(dbName)[unshardedCollName].find().itcount());
// Check that the collection has been removed from the former primary.
assert.eq(0,
fromShard.getDB(dbName)
.runCommand({listCollections: 1, filter: {name: unshardedCollName}})
.cursor.firstBatch.length);
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
}
// Test setAllowMigrations works while user writes are blocked.
{
coll.drop();
assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
// Start blocking user writes.
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: true}));
// Disable migrations for 'ns'.
assert.commandWorked(st.s.adminCommand({setAllowMigrations: ns, allowMigrations: false}));
const fromShard = st.getPrimaryShard(dbName);
const toShard = st.getOther(fromShard);
assert.commandFailedWithCode(
st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: toShard.shardName}),
ErrorCodes.ConflictingOperationInProgress);
// Reenable migrations for 'ns'.
assert.commandWorked(st.s.adminCommand({setAllowMigrations: ns, allowMigrations: true}));
assert.commandWorked(st.s.adminCommand({setUserWriteBlockMode: 1, global: false}));
}
{
const db2Name = 'db2';
assert.commandWorked(
st.s.adminCommand({enableSharding: db2Name, primaryShard: st.shard0.shardName}));
const db2 = st.s.getDB(db2Name);
let lsid = assert.commandWorked(st.s.getDB("admin").runCommand({startSession: 1})).id;
// Send _shardsvrSetUserWriteBlockMode commands to directly to shard0 so that it starts blocking
// user writes.
assert.commandWorked(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: true,
phase: 'prepare',
lsid: lsid,
txnNumber: NumberLong(1),
writeConcern: {w: "majority"}
}));
assert.commandWorked(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: true,
phase: 'complete',
lsid: lsid,
txnNumber: NumberLong(2),
writeConcern: {w: "majority"}
}));
// Check shard0 is now blocking writes.
assert.commandFailed(db2.bar.insert({x: 1}));
// Send _shardsvrSetUserWriteBlockMode commands to directly to shard0 so that it stops blocking
// user writes.
assert.commandWorked(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: false,
phase: 'prepare',
lsid: lsid,
txnNumber: NumberLong(3),
writeConcern: {w: "majority"}
}));
assert.commandWorked(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: false,
phase: 'complete',
lsid: lsid,
txnNumber: NumberLong(4),
writeConcern: {w: "majority"}
}));
// Check shard0 is no longer blocking writes.
assert.commandWorked(db2.bar.insert({x: 1}));
// Replay the first two _shardsvrSetUserWriteBlockMode commands (as if it was due to a duplicate
// network packet). These messages should fail to process, so write blocking should not be
// re-enabled.
assert.commandFailedWithCode(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: true,
phase: 'prepare',
lsid: lsid,
txnNumber: NumberLong(1),
writeConcern: {w: "majority"}
}),
ErrorCodes.TransactionTooOld);
assert.commandFailedWithCode(st.shard0.adminCommand({
_shardsvrSetUserWriteBlockMode: 1,
global: true,
phase: 'complete',
lsid: lsid,
txnNumber: NumberLong(2),
writeConcern: {w: "majority"}
}),
ErrorCodes.TransactionTooOld);
// Check shard0 is not blocking writes.
assert.commandWorked(db2.bar.insert({x: 2}));
}
st.stop();
newShard.stopSet();
})();