SERVER-125661 Stop bumping the placement version on clearJumboFlag (#52847)
GitOrigin-RevId: 3d1f199270f709bfab13249a783ddd4d7783e33c
This commit is contained in:
parent
df7237aaec
commit
4cb4a16096
@ -45,6 +45,8 @@ last-continuous:
|
||||
ticket: SERVER-121822
|
||||
- test_file: jstests/core/index/index_on_incorrect_collection.js
|
||||
ticket: SERVER-121132
|
||||
- test_file: jstests/noPassthrough/ddl/clear_jumbo.js
|
||||
ticket: SERVER-125661
|
||||
- test_file: jstests/core/ddl/coll_mod_cappedsize_on_view.js
|
||||
ticket: SERVER-124967
|
||||
suites: null
|
||||
@ -730,6 +732,8 @@ last-lts:
|
||||
ticket: SERVER-123567
|
||||
- test_file: jstests/core/clustered/clustered_collection_bounded_scan.js
|
||||
ticket: SERVER-121822
|
||||
- test_file: jstests/noPassthrough/ddl/clear_jumbo.js
|
||||
ticket: SERVER-125661
|
||||
- test_file: jstests/core/ddl/coll_mod_cappedsize_on_view.js
|
||||
ticket: SERVER-124967
|
||||
suites: null
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// requires_fcv_61 since the balancer in v6.0 is still working on the number of chunks,
|
||||
// hence the balancer is not triggered and the chunk is not marked as jumbo
|
||||
// @tags: [requires_fcv_61]
|
||||
/**
|
||||
* Tests that the clearJumboFlag command works correctly.
|
||||
*/
|
||||
|
||||
// Cannot run the filtering metadata check on tests that run clearJumboFlag.
|
||||
TestData.skipCheckShardFilteringMetadata = true;
|
||||
@ -68,11 +68,11 @@ jumboChunk = findChunksUtil.findOneChunkByNs(configDB, testNs, {min: {x: 0}});
|
||||
assert(jumboChunk.jumbo, tojson(jumboChunk));
|
||||
assert.eq(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
|
||||
// Target real jumbo chunk should bump version.
|
||||
// Target real jumbo chunk should clear the flag without bumping the placement version.
|
||||
assert.commandWorked(adminDB.runCommand({clearJumboFlag: testNs, find: {x: 1}}));
|
||||
jumboChunk = findChunksUtil.findOneChunkByNs(configDB, testNs, {min: {x: 0}});
|
||||
assert(!jumboChunk.jumbo, tojson(jumboChunk));
|
||||
assert.lt(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
assert.eq(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
|
||||
// Delete all documents
|
||||
assert.commandWorked(testColl.deleteMany({x: 0}));
|
||||
@ -98,11 +98,11 @@ jumboChunk = findChunksUtil.findOneChunkByNs(configDB, testNs, {min: {x: 0}});
|
||||
assert(jumboChunk.jumbo, tojson(jumboChunk));
|
||||
assert.eq(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
|
||||
// Target real jumbo chunk should bump version.
|
||||
// Target real jumbo chunk should clear the flag without bumping the placement version.
|
||||
assert.commandWorked(adminDB.runCommand({clearJumboFlag: testNs, bounds: [jumboChunk.min, jumboChunk.max]}));
|
||||
jumboChunk = findChunksUtil.findOneChunkByNs(configDB, testNs, {min: {x: 0}});
|
||||
assert(!jumboChunk.jumbo, tojson(jumboChunk));
|
||||
assert.lt(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
assert.eq(jumboMajorVersionBefore, jumboChunk.lastmod.getTime());
|
||||
|
||||
// Ensure clear jumbo flag stores the correct chunk version
|
||||
assert.eq(undefined, jumboChunk.lastmodEpoch);
|
||||
@ -138,8 +138,8 @@ BSONObj ChunkInfo::toBSON() const {
|
||||
return bob.obj();
|
||||
}
|
||||
|
||||
void ChunkInfo::markAsJumbo() {
|
||||
_jumbo.store(true);
|
||||
void ChunkInfo::setJumbo(bool jumbo) {
|
||||
_jumbo.store(jumbo);
|
||||
}
|
||||
|
||||
void Chunk::throwIfMoved() const {
|
||||
|
||||
@ -128,10 +128,7 @@ public:
|
||||
// computed by, say, hashing a given field or projecting to a subset of fields).
|
||||
bool containsKey(const BSONObj& shardKey) const;
|
||||
|
||||
/**
|
||||
* Marks this chunk as jumbo. Only moves from false to true once and is used by the balancer.
|
||||
*/
|
||||
void markAsJumbo();
|
||||
void setJumbo(bool jumbo);
|
||||
|
||||
private:
|
||||
// IMPORTANT: The order of the members here matters,
|
||||
@ -212,11 +209,8 @@ public:
|
||||
return _chunkInfo.containsKey(shardKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this chunk as jumbo. Only moves from false to true once and is used by the balancer.
|
||||
*/
|
||||
void markAsJumbo() {
|
||||
_chunkInfo.markAsJumbo();
|
||||
void setJumbo(bool jumbo) {
|
||||
_chunkInfo.setJumbo(jumbo);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@ -1950,8 +1950,10 @@ void ShardingCatalogManager::clearJumboFlag(OperationContext* opCtx,
|
||||
// under the exclusive _kChunkOpLock happen on the same term.
|
||||
opCtx->setAlwaysInterruptAtStepDownOrUp_UNSAFE();
|
||||
|
||||
// Take _kChunkOpLock in exclusive mode to prevent concurrent chunk modifications and generate
|
||||
// strictly monotonously increasing collection placement versions
|
||||
auto cm = uassertStatusOK(
|
||||
RoutingInformationCache::get(opCtx)->getCollectionPlacementInfoWithRefresh(opCtx, nss));
|
||||
|
||||
// Take _kChunkOpLock in exclusive mode to serialise with concurrent chunk modifications.
|
||||
Lock::ExclusiveLock lk(opCtx, _kChunkOpLock);
|
||||
|
||||
auto findCollResponse = uassertStatusOK(_localConfigShard->exhaustiveFindOnConfig(
|
||||
@ -1968,6 +1970,18 @@ void ShardingCatalogManager::clearJumboFlag(OperationContext* opCtx,
|
||||
!findCollResponse.docs.empty());
|
||||
const CollectionType coll(findCollResponse.docs[0]);
|
||||
|
||||
// Check that current collection epoch and timestamp still matches the one sent by the shard.
|
||||
// This is to spot scenarios in which the collection have been dropped and recreated or had its
|
||||
// shard key refined since the migration began.
|
||||
uassert(StaleEpochInfo(nss),
|
||||
str::stream() << "The epoch of collection '" << nss.toStringForErrorMsg()
|
||||
<< "' has changed. The config server's collection placement version "
|
||||
"epoch is now '"
|
||||
<< coll.getEpoch().toString() << "', but the request's is "
|
||||
<< collectionEpoch.toString() << "'. Aborting clear jumbo on chunk ("
|
||||
<< chunk.toString() << ").",
|
||||
coll.getEpoch() == collectionEpoch);
|
||||
|
||||
BSONObj targetChunkQuery =
|
||||
BSON(ChunkType::min(chunk.getMin())
|
||||
<< ChunkType::max(chunk.getMax()) << ChunkType::collectionUUID << coll.getUuid());
|
||||
@ -1994,63 +2008,33 @@ void ShardingCatalogManager::clearJumboFlag(OperationContext* opCtx,
|
||||
return;
|
||||
}
|
||||
|
||||
const auto allChunksQuery = BSON(ChunkType::collectionUUID << coll.getUuid());
|
||||
|
||||
// Must use local read concern because we will perform subsequent writes.
|
||||
auto findResponse = uassertStatusOK(_localConfigShard->exhaustiveFindOnConfig(
|
||||
opCtx,
|
||||
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
|
||||
repl::ReadConcernLevel::kLocalReadConcern,
|
||||
NamespaceString::kConfigsvrChunksNamespace,
|
||||
allChunksQuery,
|
||||
BSON(ChunkType::lastmod << -1),
|
||||
1));
|
||||
|
||||
const auto chunksVector = std::move(findResponse.docs);
|
||||
uassert(ErrorCodes::IncompatibleShardingMetadata,
|
||||
str::stream() << "Tried to find max chunk version for collection '"
|
||||
<< nss.toStringForErrorMsg() << ", but found no chunks",
|
||||
!chunksVector.empty());
|
||||
|
||||
const auto highestVersionChunk = uassertStatusOK(
|
||||
ChunkType::parseFromConfigBSON(chunksVector.front(), coll.getEpoch(), coll.getTimestamp()));
|
||||
const auto currentCollectionPlacementVersion = highestVersionChunk.getVersion();
|
||||
|
||||
// Check that current collection epoch and timestamp still matches the one sent by the shard.
|
||||
// This is to spot scenarios in which the collection have been dropped and recreated or had its
|
||||
// shard key refined since the migration began.
|
||||
uassert(StaleEpochInfo(nss),
|
||||
str::stream() << "The epoch of collection '" << nss.toStringForErrorMsg()
|
||||
<< "' has changed since the migration began. The config server's "
|
||||
"collection placement version epoch is now '"
|
||||
<< currentCollectionPlacementVersion.epoch().toString()
|
||||
<< "', but the shard's is " << collectionEpoch.toString()
|
||||
<< "'. Aborting clear jumbo on chunk (" << chunk.toString() << ").",
|
||||
currentCollectionPlacementVersion.epoch() == collectionEpoch);
|
||||
|
||||
ChunkVersion newVersion({currentCollectionPlacementVersion.epoch(),
|
||||
currentCollectionPlacementVersion.getTimestamp()},
|
||||
{currentCollectionPlacementVersion.majorVersion() + 1, 0});
|
||||
// Best-effort update of the in-memory routing cache so the balancer's next iteration observes
|
||||
// the cleared flag without waiting for a refresh. This mirrors the asymmetric pattern used by
|
||||
// splitOrMarkJumbo when it sets the flag: the persisted write below intentionally does not
|
||||
// bump the chunk version, so an incremental routing-cache refresh would not pick up the
|
||||
// change. The balancer is the only consumer of the jumbo flag, so updating the configsvr's
|
||||
// own routing entry in place is sufficient. Stale jumbo:true entries on mongos and shard
|
||||
// routing caches are benign because no router or shard reads the field for routing or
|
||||
// filtering decisions. If the cache cannot be obtained or doesn't contain the chunk, the
|
||||
// persisted write below is still correct; the balancer will observe the change on its next
|
||||
// refresh.
|
||||
if (cm.isSharded()) {
|
||||
auto inMemoryChunk = cm.findIntersectingChunkWithSimpleCollation(chunk.getMin());
|
||||
if (inMemoryChunk.getMin().woCompare(chunk.getMin()) == 0 &&
|
||||
inMemoryChunk.getMax().woCompare(chunk.getMax()) == 0) {
|
||||
inMemoryChunk.setJumbo(false);
|
||||
}
|
||||
}
|
||||
|
||||
BSONObj chunkQuery(BSON(ChunkType::min(chunk.getMin())
|
||||
<< ChunkType::max(chunk.getMax()) << ChunkType::collectionUUID
|
||||
<< coll.getUuid()));
|
||||
|
||||
BSONObjBuilder updateBuilder;
|
||||
updateBuilder.append("$unset", BSON(ChunkType::jumbo() << ""));
|
||||
|
||||
// Update the newest chunk to have the new (bumped) version
|
||||
BSONObjBuilder updateVersionClause(updateBuilder.subobjStart("$set"));
|
||||
updateVersionClause.appendTimestamp(ChunkType::lastmod(), newVersion.toLong());
|
||||
updateVersionClause.doneFast();
|
||||
|
||||
auto chunkUpdate = updateBuilder.obj();
|
||||
|
||||
auto didUpdate = uassertStatusOK(
|
||||
_localCatalogClient->updateConfigDocument(opCtx,
|
||||
NamespaceString::kConfigsvrChunksNamespace,
|
||||
chunkQuery,
|
||||
chunkUpdate,
|
||||
BSON("$unset" << BSON(ChunkType::jumbo() << "")),
|
||||
false /* upsert */,
|
||||
kNoWaitWriteConcern));
|
||||
|
||||
@ -2302,7 +2286,6 @@ void ShardingCatalogManager::splitOrMarkJumbo(OperationContext* opCtx,
|
||||
|
||||
if (splitPoints.empty()) {
|
||||
LOGV2(21873, "Marking chunk as jumbo", "chunk"_attr = redact(chunk.toString()));
|
||||
chunk.markAsJumbo();
|
||||
|
||||
// Take _kChunkOpLock in exclusive mode to prevent concurrent chunk modifications. Note
|
||||
// that the operation below doesn't increment the chunk marked as jumbo's version, which
|
||||
@ -2325,6 +2308,16 @@ void ShardingCatalogManager::splitOrMarkJumbo(OperationContext* opCtx,
|
||||
!findCollResponse.docs.empty());
|
||||
const CollectionType coll(findCollResponse.docs[0]);
|
||||
|
||||
// Best-effort update of the in-memory routing cache so the balancer's next iteration
|
||||
// observes the flag update without waiting for a refresh.
|
||||
// The persisted write below intentionally does not bump the chunk version, so an
|
||||
// incremental routing-cache refresh would not pick up the change. The balancer is the
|
||||
// only consumer of the jumbo flag, so updating the configsvr's own routing entry in
|
||||
// place is sufficient.
|
||||
// If the cache cannot be obtained or doesn't contain the chunk, the persisted write
|
||||
// below is still correct; the balancer will observe the change on its next refresh.
|
||||
chunk.setJumbo(true);
|
||||
|
||||
const auto chunkQuery = BSON(ChunkType::collectionUUID()
|
||||
<< coll.getUuid() << ChunkType::min(chunk.getMin()));
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ public:
|
||||
}
|
||||
|
||||
ChunkRange nonJumboChunk() {
|
||||
return ChunkRange(BSON("x" << 0), BSON("x" << BSONType::maxKey));
|
||||
return ChunkRange(BSON("x" << 0), BSON("x" << MAXKEY));
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -101,7 +101,7 @@ protected:
|
||||
NamespaceString::createNamespaceString_forTest("TestDB.TestColl2");
|
||||
};
|
||||
|
||||
TEST_F(ClearJumboFlagTest, ClearJumboShouldBumpVersion) {
|
||||
TEST_F(ClearJumboFlagTest, ClearJumboShouldNotBumpVersion) {
|
||||
auto test = [&](const NamespaceString& nss, const Timestamp& collTimestamp) {
|
||||
const auto collUuid = UUID::gen();
|
||||
const auto collEpoch = OID::gen();
|
||||
@ -113,8 +113,9 @@ TEST_F(ClearJumboFlagTest, ClearJumboShouldBumpVersion) {
|
||||
auto chunkDoc = uassertStatusOK(getChunkDoc(
|
||||
operationContext(), collUuid, jumboChunk().getMin(), collEpoch, collTimestamp));
|
||||
ASSERT_FALSE(chunkDoc.getJumbo());
|
||||
auto chunkVersion = chunkDoc.getVersion();
|
||||
ASSERT_EQ(ChunkVersion({collEpoch, collTimestamp}, {15, 0}), chunkVersion);
|
||||
// The persisted chunk version stays at the original {12, 7} — the clear is observed via
|
||||
// the in-memory routing cache, not via a placement-version bump.
|
||||
ASSERT_EQ(ChunkVersion({collEpoch, collTimestamp}, {12, 7}), chunkDoc.getVersion());
|
||||
};
|
||||
|
||||
test(_nss2, Timestamp(42));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user