Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions core/configs/src/server_config/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ const MAX_VIEW_PROBE_ATTEMPTS: u32 = 100;
/// static-asserts it equal to `shard::REPAIR_CHUNK_MAX`.
pub const DEFAULT_REPAIR_CHUNK_MAX: usize = 128;

/// Maximum number of replicas supported by the VSR implementation.
///
/// Duplicated here so `core/configs` does not depend on `core/consensus`;
/// `core/server` pins the values together at compile time.
pub const MAX_CLUSTER_REPLICAS: usize = 32;
Comment thread
Radiancebobo marked this conversation as resolved.
Resolved

/// `size_of::<StateChunkHeader>()`. Duplicated here for the same reason as
/// [`DEFAULT_REPAIR_CHUNK_MAX`]; `core/server`'s bootstrap static-asserts it
/// against the real header. Used to reject a `[message_bus] max_message_size`
Expand Down Expand Up @@ -1227,6 +1233,14 @@ impl Validatable<ConfigurationError> for ClusterConfig {
return Err(ConfigurationError::InvalidConfigurationValue);
}

if self.nodes.len() > MAX_CLUSTER_REPLICAS {
Comment thread
Radiancebobo marked this conversation as resolved.
Resolved
eprintln!(
"Invalid cluster configuration: cluster.nodes contains {} replicas, but the VSR implementation supports at most {MAX_CLUSTER_REPLICAS}",
self.nodes.len()
);
return Err(ConfigurationError::InvalidConfigurationValue);
}

// VSR needs every replica to have a stable, unique id strictly
// less than the total replica count. Duplicate ids would split the
// cluster into two replicas claiming the same slot; out-of-range
Expand Down Expand Up @@ -2467,6 +2481,18 @@ mod cluster_validate_tests {
assert!(c.validate().is_ok());
}

#[test]
fn validate_accepts_the_vsr_replica_limit() {
let nodes = (0..32).map(|id| node(&format!("n{id}"), id)).collect();
assert!(cfg(nodes).validate().is_ok());
}

#[test]
fn validate_rejects_rosters_above_the_vsr_replica_limit() {
let nodes = (0..33).map(|id| node(&format!("n{id}"), id)).collect();
assert!(cfg(nodes).validate().is_err());
}

#[test]
fn validate_skips_checks_when_disabled() {
let mut c = cfg(vec![]);
Expand Down
2 changes: 2 additions & 0 deletions core/server/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,8 @@ skip_shard_zero_for_clients = false
# node's identity is resolved at launch from the '--replica-id <N>' CLI
# flag, which selects the entry in this list that describes the current
# node. All other entries are remote peers.
# VSR currently supports at most 32 nodes in this roster; larger rosters
# are rejected during server startup.
#
# 'ip' is the node's roster address. Replica-to-replica traffic and
# follower-to-primary HTTP forwarding use it. It is not the bind interface for
Expand Down
1 change: 1 addition & 0 deletions core/server/src/boot/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ const _: () = assert!(
);
const _: () =
assert!(configs::cluster::DEFAULT_VIEW_PROBE_ATTEMPTS_MAX == consensus::PROBE_ATTEMPTS_MAX);
const _: () = assert!(configs::cluster::MAX_CLUSTER_REPLICAS == consensus::REPLICAS_MAX);
const _: () =
assert!(configs::partition::DEFAULT_EVICTED_RING_CAPACITY == partitions::EVICTED_RING_CAPACITY);
const _: () = assert!(
Expand Down
Loading