Skip to content

Commit f505e82

Browse files
toto-devEvergreen Agent
authored andcommitted
SERVER-81966 Avoid modification of previous ChunkMap instances during refresh
1 parent 31922a5 commit f505e82

File tree

2 files changed

+58
-84
lines changed

2 files changed

+58
-84
lines changed

src/mongo/s/chunk_manager.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,17 @@ void ChunkMap::_mergeAndCommitUpdatedChunkVector(ChunkVectorMap::const_iterator
238238
auto prevVectorPtr = _chunkVectorMap.extract(std::prev(pos)).mapped();
239239
auto mergeVectorPtr = std::make_shared<ChunkVector>();
240240
mergeVectorPtr->reserve(prevVectorPtr->size() + smallVectorPtr->size());
241-
// fill initial part of merged vector with a copy of oldVector
242-
mergeVectorPtr->insert(mergeVectorPtr->end(),
243-
std::make_move_iterator(prevVectorPtr->begin()),
244-
std::make_move_iterator(prevVectorPtr->end()));
241+
242+
// Fill initial part of merged vector with a copy of old vector (prevVectorPtr)
243+
// Note that the old vector is potentially shared with previous ChunkMap instances,
244+
// thus we copy rather than moving elements to maintain its integrity.
245+
mergeVectorPtr->insert(mergeVectorPtr->end(), prevVectorPtr->begin(), prevVectorPtr->end());
246+
247+
// Fill the rest of merged vector with the small updated vector
245248
mergeVectorPtr->insert(mergeVectorPtr->end(),
246249
std::make_move_iterator(smallVectorPtr->begin()),
247250
std::make_move_iterator(smallVectorPtr->end()));
251+
248252
_chunkVectorMap.emplace_hint(
249253
pos, mergeVectorPtr->back()->getMaxKeyString(), std::move(mergeVectorPtr));
250254
}

src/mongo/s/chunk_map_test.cpp

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,44 @@ std::vector<std::shared_ptr<ChunkInfo>> toChunkInfoPtrVector(
106106
return chunkPtrs;
107107
}
108108

109+
void validateChunkMap(const ChunkMap& chunkMap,
110+
const std::vector<std::shared_ptr<ChunkInfo>>& chunkInfoVector) {
111+
112+
// The chunkMap should contain all the chunks
113+
ASSERT_EQ(chunkInfoVector.size(), chunkMap.size());
114+
115+
// Check collection version
116+
const auto expectedShardVersions = calculateShardVersions(chunkInfoVector);
117+
const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
118+
ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
119+
120+
size_t i = 0;
121+
chunkMap.forEach([&](const auto& chunkPtr) {
122+
const auto& expectedChunkPtr = chunkInfoVector[i++];
123+
// Check that the chunk pointer is valid
124+
ASSERT_NOT_EQUALS(chunkPtr.get(), nullptr);
125+
assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
126+
return true;
127+
});
128+
129+
// Validate all shard versions
130+
const auto shardVersions = getShardVersionMap(chunkMap);
131+
ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
132+
for (const auto& mapIt : shardVersions) {
133+
ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.placementVersion);
134+
}
135+
136+
// Check that vectors are balanced in size
137+
auto maxVectorSize = static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() * 1.5));
138+
auto minVectorSize = std::min(
139+
chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
140+
141+
for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
142+
ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
143+
ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
144+
}
145+
}
146+
109147
class ChunkMapTest : public unittest::Test {
110148
public:
111149
const KeyPattern& getShardKeyPattern() const {
@@ -156,44 +194,16 @@ TEST_F(ChunkMapTest, TestAddChunk) {
156194
auto newChunkMap = makeChunkMap({chunk});
157195

158196
ASSERT_EQ(newChunkMap.size(), 1);
197+
198+
validateChunkMap(newChunkMap, {chunk});
159199
}
160200

161201
TEST_F(ChunkMapTest, ConstructChunkMapRandom) {
162202
auto chunkVector = toChunkInfoPtrVector(genRandomChunkVector());
163203

164-
const auto expectedShardVersions = calculateShardVersions(chunkVector);
165-
const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
166-
167204
const auto chunkMap = makeChunkMap(chunkVector);
168205

169-
// Check that it contains all the chunks
170-
ASSERT_EQ(chunkVector.size(), chunkMap.size());
171-
// Check collection version
172-
ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
173-
174-
size_t i = 0;
175-
chunkMap.forEach([&](const auto& chunkPtr) {
176-
const auto& expectedChunkPtr = chunkVector[i++];
177-
assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
178-
return true;
179-
});
180-
181-
// Validate all shard versions
182-
const auto shardVersions = getShardVersionMap(chunkMap);
183-
ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
184-
for (const auto& mapIt : shardVersions) {
185-
ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.placementVersion);
186-
}
187-
188-
// Check that vectors are balanced in size
189-
auto maxVectorSize = static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() * 1.5));
190-
auto minVectorSize = std::min(
191-
chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
192-
193-
for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
194-
ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
195-
ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
196-
}
206+
validateChunkMap(chunkMap, chunkVector);
197207
}
198208

199209
TEST_F(ChunkMapTest, ConstructChunkMapRandomAllChunksSameVersion) {
@@ -212,25 +222,7 @@ TEST_F(ChunkMapTest, ConstructChunkMapRandomAllChunksSameVersion) {
212222
ASSERT_EQ(commonVersion, expectedCollVersion);
213223

214224
const auto chunkMap = makeChunkMap(chunkInfoVector);
215-
216-
// Check that it contains all the chunks
217-
ASSERT_EQ(chunkInfoVector.size(), chunkMap.size());
218-
// Check collection version
219-
ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
220-
221-
size_t i = 0;
222-
chunkMap.forEach([&](const auto& chunkPtr) {
223-
const auto& expectedChunkPtr = chunkInfoVector[i++];
224-
assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
225-
return true;
226-
});
227-
228-
// Validate all shard versions
229-
const auto shardVersions = getShardVersionMap(chunkMap);
230-
ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
231-
for (const auto& mapIt : shardVersions) {
232-
ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.placementVersion);
233-
}
225+
validateChunkMap(chunkMap, chunkInfoVector);
234226
}
235227

236228
/*
@@ -286,8 +278,12 @@ TEST_F(ChunkMapTest, UpdateMapNotLeaveSmallVectors) {
286278
ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
287279
ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
288280
}
281+
282+
// Check original map is sitll valid
283+
validateChunkMap(initialChunkMap, chunkVector);
289284
}
290285

286+
291287
/*
292288
* Check that updating a ChunkMap with chunks that have mismatching timestamp fails.
293289
*/
@@ -355,38 +351,12 @@ TEST_F(ChunkMapTest, UpdateChunkMapRandom) {
355351
}
356352
}
357353

358-
const auto expectedShardVersions = calculateShardVersions(chunksInfo);
359-
const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
354+
// Create updated chunk map and validate it
360355
auto chunkMap = initialChunkMap.createMerged(updatedChunksInfo);
356+
validateChunkMap(chunkMap, chunksInfo);
361357

362-
// Check that it contains all the chunks
363-
ASSERT_EQ(chunksInfo.size(), chunkMap.size());
364-
// Check collection version
365-
ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
366-
367-
size_t i = 0;
368-
chunkMap.forEach([&](const auto& chunkPtr) {
369-
const auto& expectedChunkPtr = chunksInfo[i++];
370-
assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
371-
return true;
372-
});
373-
374-
// Validate all shard versions
375-
const auto shardVersions = getShardVersionMap(chunkMap);
376-
ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
377-
for (const auto& mapIt : shardVersions) {
378-
ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.placementVersion);
379-
}
380-
381-
// Check that vectors are balanced in size
382-
auto maxVectorSize = static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() * 1.5));
383-
auto minVectorSize = std::min(
384-
chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
385-
386-
for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
387-
ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
388-
ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
389-
}
358+
// Check that the initialChunkMap is still valid and usable
359+
validateChunkMap(initialChunkMap, initialChunksInfo);
390360
}
391361

392362
TEST_F(ChunkMapTest, TestEnumerateAllChunks) {

0 commit comments

Comments
 (0)