|
33 | 33 |
|
34 | 34 | #include "mongo/db/s/balancer/balancer_policy.h" |
35 | 35 |
|
| 36 | +#include <random> |
| 37 | + |
36 | 38 | #include "mongo/db/s/balancer/type_migration.h" |
37 | 39 | #include "mongo/s/catalog/type_shard.h" |
38 | 40 | #include "mongo/s/catalog/type_tags.h" |
| 41 | +#include "mongo/util/fail_point_service.h" |
39 | 42 | #include "mongo/util/log.h" |
40 | 43 | #include "mongo/util/str.h" |
41 | 44 |
|
42 | 45 | namespace mongo { |
43 | 46 |
|
| 47 | +MONGO_FAIL_POINT_DEFINE(balancerShouldReturnRandomMigrations); |
| 48 | + |
44 | 49 | using std::map; |
45 | 50 | using std::numeric_limits; |
46 | 51 | using std::set; |
@@ -287,11 +292,81 @@ ShardId BalancerPolicy::_getMostOverloadedShard(const ShardStatisticsVector& sha |
287 | 292 | return worst; |
288 | 293 | } |
289 | 294 |
|
| 295 | +// Returns a random integer in [0, max) using a uniform random distribution. |
| 296 | +int getRandomIndex(int max) { |
| 297 | + std::default_random_engine gen(time(nullptr)); |
| 298 | + std::uniform_int_distribution<int> dist(0, max - 1); |
| 299 | + |
| 300 | + return dist(gen); |
| 301 | +} |
| 302 | + |
| 303 | +// Iterates through the shardStats vector starting from index until it finds an element that has > 0 |
| 304 | +// chunks. It will wrap around at the end and stop at the starting index. If no shards have chunks, |
| 305 | +// it will return the original index value. |
| 306 | +int getNextShardWithChunks(const ShardStatisticsVector& shardStats, |
| 307 | + const DistributionStatus& distribution, |
| 308 | + int index) { |
| 309 | + int retIndex = index; |
| 310 | + |
| 311 | + while (distribution.numberOfChunksInShard(shardStats[retIndex].shardId) == 0) { |
| 312 | + retIndex = (retIndex + 1) % shardStats.size(); |
| 313 | + |
| 314 | + if (retIndex == index) |
| 315 | + return index; |
| 316 | + } |
| 317 | + |
| 318 | + return retIndex; |
| 319 | +} |
| 320 | + |
| 321 | +// Returns a randomly chosen pair of source -> destination shards for testing. |
| 322 | +// The random pair is chosen by the following algorithm: |
| 323 | +// - create an array of indices with values [0, n) |
| 324 | +// - select a random index from this set |
| 325 | +// - advance the chosen index until we encounter a shard with chunks to move |
| 326 | +// - remove the chosen index from the set by swapping it with the last element |
| 327 | +// - select the destination index from the remaining indices |
| 328 | +MigrateInfo chooseRandomMigration(const ShardStatisticsVector& shardStats, |
| 329 | + const DistributionStatus& distribution) { |
| 330 | + std::vector<int> indices(shardStats.size()); |
| 331 | + |
| 332 | + int i = 0; |
| 333 | + std::generate(indices.begin(), indices.end(), [&i] { return i++; }); |
| 334 | + |
| 335 | + int choice = getRandomIndex(indices.size()); |
| 336 | + |
| 337 | + const int sourceIndex = getNextShardWithChunks(shardStats, distribution, indices[choice]); |
| 338 | + const auto& sourceShardId = shardStats[sourceIndex].shardId; |
| 339 | + std::swap(indices[sourceIndex], indices[indices.size() - 1]); |
| 340 | + |
| 341 | + choice = getRandomIndex(indices.size() - 1); |
| 342 | + const int destIndex = indices[choice]; |
| 343 | + const auto& destShardId = shardStats[destIndex].shardId; |
| 344 | + |
| 345 | + LOG(1) << "balancerShouldReturnRandomMigrations: source: " << sourceShardId |
| 346 | + << " dest: " << destShardId; |
| 347 | + |
| 348 | + const auto& chunks = distribution.getChunks(sourceShardId); |
| 349 | + |
| 350 | + return {destShardId, chunks[getRandomIndex(chunks.size())]}; |
| 351 | +} |
| 352 | + |
290 | 353 | vector<MigrateInfo> BalancerPolicy::balance(const ShardStatisticsVector& shardStats, |
291 | 354 | const DistributionStatus& distribution, |
292 | 355 | std::set<ShardId>* usedShards) { |
293 | 356 | vector<MigrateInfo> migrations; |
294 | 357 |
|
| 358 | + if (MONGO_FAIL_POINT(balancerShouldReturnRandomMigrations) && |
| 359 | + !distribution.nss().isConfigDB()) { |
| 360 | + LOG(1) << "balancerShouldReturnRandomMigrations failpoint is set"; |
| 361 | + |
| 362 | + if (shardStats.size() < 2) |
| 363 | + return migrations; |
| 364 | + |
| 365 | + migrations.push_back(chooseRandomMigration(shardStats, distribution)); |
| 366 | + |
| 367 | + return migrations; |
| 368 | + } |
| 369 | + |
295 | 370 | // 1) Check for shards, which are in draining mode |
296 | 371 | { |
297 | 372 | for (const auto& stat : shardStats) { |
|
0 commit comments