0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00
mongodb/jstests/sharding/database_versioning_safe_secondary_reads.js
2019-07-27 11:02:23 -04:00

239 lines
12 KiB
JavaScript

/**
* Tests that shards' cached in-memory and on-disk database versions are updated on primary and
* secondary nodes when:
* - the node does not have a cached in-memory version
* - the node's cached in-memory version is lower than the version sent by a client
* - the movePrimary critical section is entered on the primary node
*/
(function() {
"use strict";
load("jstests/libs/database_versioning.js");
const dbName = "test";
const st = new ShardingTest({
mongos: 2,
rs0: {nodes: [{rsConfig: {votes: 1}}, {rsConfig: {priority: 0}}]},
rs1: {nodes: [{rsConfig: {votes: 1}}, {rsConfig: {priority: 0}}]},
verbose: 2
});
// Before creating the database, none of the nodes have a cached entry for the database either
// in memory or on disk.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, undefined);
// Manually insert an entry for 'dbName' into config.databases so that we can choose shard1 as
// the primary shard.
assert.commandWorked(st.s.getDB("config").getCollection("databases").insert({
_id: dbName,
primary: st.rs1.name,
partitioned: false,
version: {uuid: new UUID(), lastMod: NumberInt(1)},
}));
// Check that a command that attaches databaseVersion returns empty results, even though the
// database does not actually exist on any shard.
assert.commandWorked(st.s.getDB(dbName).runCommand({listCollections: 1}));
// Ensure the current primary shard's primary has written the new database entry to disk.
st.rs1.getPrimary().adminCommand({_flushDatabaseCacheUpdates: dbName, syncFromConfig: false});
// Ensure the database entry on the current primary shard has replicated to the secondary.
st.rs1.awaitReplication();
const dbEntry0 = st.s.getDB("config").getCollection("databases").findOne({_id: dbName});
// The primary shard's primary should have refreshed its in-memory and on-disk caches. The
// primary shard's secondary should have refreshed its on-disk entry.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0.version);
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0);
assert.commandWorked(st.s.getDB(dbName).runCommand(
{listCollections: 1, $readPreference: {mode: "secondary"}, readConcern: {level: "local"}}));
// The primary shard's secondary should have refreshed its in-memory cache (its on-disk cache
// was updated when the primary refreshed, above).
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0.version);
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0.version);
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0);
// Use 'movePrimary' to ensure shard0 is the primary shard.
st.ensurePrimaryShard(dbName, st.shard0.shardName);
const dbEntry1 = st.s.getDB("config").getCollection("databases").findOne({_id: dbName});
assert.eq(dbEntry1.version.uuid, dbEntry0.version.uuid);
assert.eq(dbEntry1.version.lastMod, dbEntry0.version.lastMod + 1);
// Ensure the database actually gets created on the primary shard by creating a collection in
// it.
assert.commandWorked(st.s.getDB(dbName).runCommand({create: "foo"}));
// Run a command that attaches databaseVersion to cause the current primary shard's primary to
// refresh its in-memory cached database version.
jsTest.log("About to do listCollections with readPref=primary");
assert.commandWorked(st.s.getDB(dbName).runCommand({listCollections: 1}));
// Ensure the current primary shard's primary has written the new database entry to disk.
st.rs0.getPrimary().adminCommand({_flushDatabaseCacheUpdates: dbName, syncFromConfig: false});
// Ensure the database entry on the current primary shard has replicated to the secondary.
st.rs0.awaitReplication();
// The primary shard's primary should have refreshed its in-memory cache and on-disk entry. The
// primary shard's secondary will have refreshed its on-disk entry.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry1.version);
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0);
// Now run a command that attaches databaseVersion with readPref=secondary to make the current
// primary shard's secondary refresh its in-memory database version from its on-disk entry.
jsTest.log("About to do listCollections with readPref=secondary");
assert.commandWorked(st.s.getDB(dbName).runCommand(
{listCollections: 1, $readPreference: {mode: "secondary"}, readConcern: {level: "local"}}));
// The primary shard's secondary should have refreshed its in memory-cache.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry1.version);
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry1.version);
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0);
// Make "staleMongos" load the stale database info into memory.
const freshMongos = st.s0;
const staleMongos = st.s1;
staleMongos.getDB(dbName).runCommand({listCollections: 1});
// Run movePrimary to ensure the movePrimary critical section clears the in-memory cache on the
// old primary shard.
jsTest.log("About to do movePrimary");
assert.commandWorked(freshMongos.adminCommand({movePrimary: dbName, to: st.shard1.shardName}));
const dbEntry2 = freshMongos.getDB("config").getCollection("databases").findOne({_id: dbName});
assert.eq(dbEntry2.version.uuid, dbEntry1.version.uuid);
assert.eq(dbEntry2.version.lastMod, dbEntry1.version.lastMod + 1);
// Ensure the old primary shard's primary has written the 'enterCriticalSectionSignal' flag to
// its on-disk database entry.
st.rs0.getPrimary().adminCommand({_flushDatabaseCacheUpdates: dbName, syncFromConfig: false});
// Ensure 'enterCriticalSectionSignal' flag has replicated to the secondary.
st.rs0.awaitReplication();
// The in-memory cached version should have been cleared on the old primary shard's primary and
// secondary nodes.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry0);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry1);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry0);
// Run listCollections with readPref=secondary from the stale mongos. First, this should cause
// the old primary shard's secondary to provoke the old primary shard's primary to refresh. Then
// once the stale mongos refreshes, it should cause the new primary shard's secondary to provoke
// the new primary shard's primary to refresh.
jsTest.log("About to do listCollections with readPref=secondary after movePrimary");
assert.commandWorked(staleMongos.getDB(dbName).runCommand(
{listCollections: 1, $readPreference: {mode: "secondary"}, readConcern: {level: "local"}}));
// All nodes should have refreshed.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry2.version);
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry2.version);
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry2.version);
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry2.version);
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry2);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, dbEntry2);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry2);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, dbEntry2);
// Drop the database
jsTest.log("About to drop database from the cluster");
assert.commandWorked(freshMongos.getDB(dbName).runCommand({dropDatabase: 1}));
// Ensure the drop has replicated to all nodes.
st.rs0.awaitReplication();
st.rs1.awaitReplication();
// The in-memory caches will be cleared on both shard's primary nodes and the on-disk database
// entries will be cleared on all shards.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, undefined);
// Manually insert an entry for 'dbName' into config.databases so that we can choose shard0 as
// the primary shard.
assert.commandWorked(st.s.getDB("config").getCollection("databases").insert({
_id: dbName,
primary: st.rs0.name,
partitioned: false,
version: {uuid: new UUID(), lastMod: NumberInt(1)},
}));
const dbEntry = st.s.getDB("config").getCollection("databases").findOne({_id: dbName});
assert.commandWorked(st.s.getDB(dbName).runCommand({listCollections: 1}));
// Ensure the current primary shard's primary has written the new database entry to disk.
st.rs0.getPrimary().adminCommand({_flushDatabaseCacheUpdates: dbName, syncFromConfig: false});
// Ensure the database entry on the current primary shard has replicated to the secondary.
st.rs0.awaitReplication();
// The primary shard's primary should have refreshed its in-memory and on-disk caches.
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry.version);
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, undefined);
assert.commandWorked(st.s.getDB(dbName).runCommand(
{listCollections: 1, $readPreference: {mode: "secondary"}, readConcern: {level: "local"}}));
// The primary shard's secondary should have refreshed its in-memory cache (its on-disk cache
// was already updated when the primary refreshed).
checkInMemoryDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry.version);
checkInMemoryDatabaseVersion(st.rs1.getPrimary(), dbName, {});
checkInMemoryDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry.version);
checkInMemoryDatabaseVersion(st.rs1.getSecondary(), dbName, {});
checkOnDiskDatabaseVersion(st.rs0.getPrimary(), dbName, dbEntry);
checkOnDiskDatabaseVersion(st.rs1.getPrimary(), dbName, undefined);
checkOnDiskDatabaseVersion(st.rs0.getSecondary(), dbName, dbEntry);
checkOnDiskDatabaseVersion(st.rs1.getSecondary(), dbName, undefined);
st.stop();
})();