blob: edf292ca42ea121359d7042e3367fc6cb4b0afc6 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <cstddef>
#include <cstdint>
#include <random>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <boost/optional/optional.hpp>
#include "kudu/rebalance/cluster_status.h"
#include "kudu/util/status.h"
namespace kudu {
namespace rebalance {
struct ClusterInfo;
struct TableReplicaMove;
struct TabletsPlacementInfo;
// Sub-set of fields from ClusterResult which are relevant to the rebalancing.
struct ClusterRawInfo {
std::vector<cluster_summary::ServerHealthSummary> tserver_summaries;
std::vector<cluster_summary::TableSummary> table_summaries;
std::vector<cluster_summary::TabletSummary> tablet_summaries;
};
// A class implementing logic for Kudu cluster rebalancing.
// A Rebalancer object encapsulates structs with cluster information
// and functions needed to calculate and implement replica moves.
class Rebalancer {
public:
// Configuration parameters for the rebalancer aggregated into a struct.
struct Config {
static constexpr double kLoadImbalanceThreshold = 1.0;
// NOLINTNEXTLINE(google-explicit-constructor)
Config(std::vector<std::string> ignored_tservers_param = {},
std::vector<std::string> master_addresses = {},
std::vector<std::string> table_filters = {},
size_t max_moves_per_server = 5,
size_t max_staleness_interval_sec = 300,
int64_t max_run_time_sec = 0,
bool move_replicas_from_ignored_tservers = false,
bool move_rf1_replicas = false,
bool output_replica_distribution_details = false,
bool run_policy_fixer = true,
bool run_cross_location_rebalancing = true,
bool run_intra_location_rebalancing = true,
double load_imbalance_threshold = kLoadImbalanceThreshold);
// UUIDs of ignored servers. If empty, run the rebalancing on
// all tablet servers in the cluster only when all tablet servers
// in cluster are healthy. If not empty, specified tablet servers
// (including their health state and replicas on them) will be
// ignored by the rebalancer tool.
std::unordered_set<std::string> ignored_tservers;
// Kudu masters' RPC endpoints.
std::vector<std::string> master_addresses;
// Names of tables to balance. If empty, every table and the whole cluster
// will be balanced.
std::vector<std::string> table_filters;
// Maximum number of move operations to run concurrently on one server.
// An 'operation on a server' means a move operation where either source or
// destination replica is located on the specified server.
size_t max_moves_per_server;
// Maximum duration of the 'staleness' interval, when the rebalancer cannot
// make any progress in scheduling new moves and no prior scheduled moves
// are left, even if re-synchronizing against the cluster's state again and
// again. Such a staleness usually happens in case of a persistent problem
// with the cluster or when some unexpected concurrent activity is present
// (such as automatic recovery of failed replicas, etc.).
size_t max_staleness_interval_sec;
// Maximum run time, in seconds.
int64_t max_run_time_sec;
// Whether to move replicas from ignored tservers to other tservers.
// If true, replicas on healthy ignored tservers will be moved to other tservers
// before running the rebalancing.
// If false, ignored tservers and replicas on them will be ignored by the
// rebalancer tool.
bool move_replicas_from_ignored_tservers;
// Whether to move replicas of tablets with replication factor of one.
bool move_rf1_replicas;
// Whether Rebalancer::PrintStats() should output per-table and per-server
// replica distribution details.
bool output_replica_distribution_details;
// In case of multi-location cluster, whether to detect and fix placement
// policy violations. Fixing placement policy violations involves moving
// tablet replicas across different locations in the cluster.
// This setting is applicable to multi-location clusters only.
bool run_policy_fixer;
// In case of multi-location cluster, whether to move tablet replicas
// between locations in attempt to spread tablet replicas among location
// evenly (equalizing loads of locations throughout the cluster).
// This setting is applicable to multi-location clusters only.
bool run_cross_location_rebalancing;
// In case of multi-location cluster, whether to rebalance tablet replica
// distribution within each location.
// This setting is applicable to multi-location clusters only.
bool run_intra_location_rebalancing;
// The per-table location load imbalance threshold for the cross-location
// balancing algorithm.
double load_imbalance_threshold;
};
// Represents a concrete move of a replica from one tablet server to another.
// Formed logically from a TableReplicaMove by specifying a tablet for the move.
struct ReplicaMove {
std::string tablet_uuid;
std::string ts_uuid_from;
std::string ts_uuid_to;
boost::optional<int64_t> config_opid_idx; // for CAS-enabled Raft changes
};
enum class RunStatus {
UNKNOWN,
CLUSTER_IS_BALANCED,
TIMED_OUT,
};
// A helper type: key is tablet UUID which corresponds to value.tablet_uuid.
typedef std::unordered_map<std::string, ReplicaMove> MovesInProgress;
explicit Rebalancer(Config config);
// Filter the list of candidate tablets to make sure the location
// of the destination server would not contain the majority of replicas
// after the move. The 'tablet_ids' is an in-out parameter.
static Status FilterCrossLocationTabletCandidates(
const std::unordered_map<std::string, std::string>& location_by_ts_id,
const TabletsPlacementInfo& placement_info,
const TableReplicaMove& move,
std::vector<std::string>* tablet_ids);
// Given high-level move-some-tablet-replica-for-a-table information from the
// rebalancing algorithm, find appropriate tablet replicas to move between the
// specified tablet servers. The set of result tablet UUIDs is output
// into the 'tablet_ids' container (note: the container is first cleared).
// The source and destination replicas can then be determined by the elements
// of the 'tablet_ids' container and tablet server UUIDs TableReplicaMove::from
// and TableReplica::to correspondingly. If no suitable tablet replicas are found,
// 'tablet_ids' will be empty.
static void FindReplicas(const TableReplicaMove& move,
const ClusterRawInfo& raw_info,
std::vector<std::string>* tablet_ids);
// Convert the 'raw' information about the cluster into information suitable
// for the input of the high-level rebalancing algorithm.
// The 'moves_in_progress' parameter contains information on the replica moves
// which have been scheduled by a caller and still in progress: those are
// considered as successfully completed and applied to the 'raw_info' when
// building ClusterBalanceInfo for the specified 'raw_info' input. The idea
// is to prevent the algorithm outputting the same moves again while some
// of the moves recommended at prior steps are still in progress.
// The result cluster balance information is output into the 'info' parameter.
// The 'info' output parameter cannot be null.
Status BuildClusterInfo(const ClusterRawInfo& raw_info,
const MovesInProgress& moves_in_progress,
ClusterInfo* info) const;
protected:
// Helper class to find and schedule next available rebalancing move operation
// and track already scheduled ones.
class Runner {
public:
virtual ~Runner() = default;
// Initialize instance of Runner so it can run against Kudu cluster with
// the 'master_addresses' RPC endpoints.
virtual Status Init(std::vector<std::string> master_addresses) = 0;
// Load information on the prescribed replica movement operations. Also,
// populate helper containers and other auxiliary run-time structures
// used by ScheduleNextMove(). This method is called with every batch
// of move operations output by the rebalancing algorithm once previously
// loaded moves have been scheduled.
virtual void LoadMoves(std::vector<Rebalancer::ReplicaMove> replica_moves) = 0;
// Schedule next replica move. Returns 'true' if replica move operation
// has been scheduled successfully; otherwise returns 'false' and sets
// the 'has_errors' and 'timed_out' parameters accordingly.
virtual bool ScheduleNextMove(bool* has_errors, bool* timed_out) = 0;
// Update statuses and auxiliary information on in-progress replica move
// operations.
// The 'timed_out' parameter is set to 'true' if not all in-progress
// operations were processed by the deadline specified by the 'deadline_'
// member field.
// The 'has_errors' parameter is set to 'true' if there were errors while
// trying to get the statuses of in-progress operations.
// The 'has_pending_moves' parameter is set to 'true' if there were any
// in-progress operations and we could check their statuses.
// The method returns 'true' if at least one in-progress move operation was
// completed (success or failure).
virtual bool UpdateMovesInProgressStatus(bool* has_errors,
bool* timed_out,
bool* has_pending_moves) = 0;
virtual Status GetNextMoves(bool* has_moves) = 0;
virtual uint32_t moves_count() const = 0;
}; // class Runner
friend class KsckResultsToClusterBalanceInfoTest;
// Filter move operations in 'replica_moves': remove all operations that would
// involve moving replicas of tablets which are in 'scheduled_moves'. The
// 'replica_moves' cannot be null.
static void FilterMoves(const MovesInProgress& scheduled_moves,
std::vector<ReplicaMove>* replica_moves);
// Configuration for the rebalancer.
const Config config_;
};
// Hold information about a tablet's replication factor and replicas.
struct TabletExtraInfo {
int replication_factor;
int num_voters;
};
// Populate a 'tablet_id' --> 'target tablet replication factor' map.
void BuildTabletExtraInfoMap(
const ClusterRawInfo& raw_info,
std::unordered_map<std::string, TabletExtraInfo>* extra_info_by_tablet_id);
// For a given table, find a tablet replica on a specified tserver to move
// to another. Given the requested 'move', shuffle the table's tablet_ids
// and select the first tablet that doesn't currently have any replicas in
// 'tablets_in_move'. Add the tablet's id to 'tablets_in_move' and add
// information about this move to 'replica_moves'. If the chosen tablet is
// overreplicated, no destination tserver is specified, in case it is better
// to just remove it from the replica distribution entirely.
Status SelectReplicaToMove(
const TableReplicaMove& move,
const std::unordered_map<std::string, TabletExtraInfo>& extra_info_by_tablet_id,
std::mt19937* random_generator,
std::vector<std::string> tablet_ids,
std::unordered_set<std::string>* tablets_in_move,
std::vector<Rebalancer::ReplicaMove>* replica_moves);
} // namespace rebalance
} // namespace kudu