Skip to content

Commit 2372470

Browse files
committed
state/metadata: get and expose cluster version
In cpp-driver, `cass_schema_meta_version` returns the output of `release_version` column from `system.local` query executed on current control connection. I'm not entirely sure if we should expose such API. `ClusterState::get_nodes_info()` would be enough to implement in on the cpp-driver side - we would just choose the version of random/first node. Or maybe return an error if there is a version mismatch (?). This can be discussed. The main thing I hate about this commit is this ugly `fold` expression in `query_peers_and_cluster_version`. I have no idea if there is any cleaner solution to this.
1 parent 757dbae commit 2372470

File tree

5 files changed

+50
-8
lines changed

5 files changed

+50
-8
lines changed

scylla/src/cluster/metadata.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ pub(crate) struct MetadataReader {
118118
pub(crate) struct Metadata {
119119
pub(crate) peers: Vec<Peer>,
120120
pub(crate) keyspaces: HashMap<String, Result<Keyspace, SingleKeyspaceMetadataError>>,
121+
/// The release_version obtained from `system.local` on control connection.
122+
pub(crate) cluster_version: Option<String>,
121123
}
122124

123125
#[non_exhaustive] // <- so that we can add more fields in a backwards-compatible way
@@ -420,6 +422,7 @@ impl Metadata {
420422
Metadata {
421423
peers,
422424
keyspaces: HashMap::new(),
425+
cluster_version: None,
423426
}
424427
}
425428
}
@@ -728,10 +731,10 @@ impl ControlConnection {
728731
keyspace_to_fetch: &[String],
729732
fetch_schema: bool,
730733
) -> Result<Metadata, MetadataError> {
731-
let peers_query = self.query_peers(connect_port);
734+
let peers_query = self.query_peers_and_cluster_version(connect_port);
732735
let keyspaces_query = self.query_keyspaces(keyspace_to_fetch, fetch_schema);
733736

734-
let (peers, keyspaces) = tokio::try_join!(peers_query, keyspaces_query)?;
737+
let ((peers, cluster_version), keyspaces) = tokio::try_join!(peers_query, keyspaces_query)?;
735738

736739
// There must be at least one peer
737740
if peers.is_empty() {
@@ -743,7 +746,11 @@ impl ControlConnection {
743746
return Err(MetadataError::Peers(PeersMetadataError::EmptyTokenLists));
744747
}
745748

746-
Ok(Metadata { peers, keyspaces })
749+
Ok(Metadata {
750+
peers,
751+
keyspaces,
752+
cluster_version,
753+
})
747754
}
748755
}
749756

@@ -779,7 +786,13 @@ impl NodeInfoSource {
779786
const METADATA_QUERY_PAGE_SIZE: i32 = 1024;
780787

781788
impl ControlConnection {
782-
async fn query_peers(&self, connect_port: u16) -> Result<Vec<Peer>, MetadataError> {
789+
/// Returns the vector of peers and the cluster version.
790+
/// Cluster version is the `release_version` column from `system.local` query
791+
/// executed on control connection.
792+
async fn query_peers_and_cluster_version(
793+
&self,
794+
connect_port: u16,
795+
) -> Result<(Vec<Peer>, Option<String>), MetadataError> {
783796
let mut peers_query = Statement::new(
784797
"select host_id, release_version, rpc_address, data_center, rack, tokens from system.peers",
785798
);
@@ -828,7 +841,9 @@ impl ControlConnection {
828841

829842
let translated_peers_futures = untranslated_rows.map(|row_result| async {
830843
match row_result {
831-
Ok((source, row)) => Self::create_peer_from_row(source, row, local_address).await,
844+
Ok((source, row)) => Self::create_peer_from_row(source, row, local_address)
845+
.await
846+
.map(|peer| (source, peer)),
832847
Err(err) => {
833848
warn!(
834849
"system.peers or system.local has an invalid row, skipping it: {}",
@@ -839,12 +854,24 @@ impl ControlConnection {
839854
}
840855
});
841856

842-
let peers = translated_peers_futures
857+
let vec_capacity = translated_peers_futures.size_hint().0;
858+
let (peers, cluster_version) = translated_peers_futures
843859
.buffer_unordered(256)
844860
.filter_map(std::future::ready)
845-
.collect::<Vec<_>>()
861+
.fold(
862+
(Vec::with_capacity(vec_capacity), None),
863+
|(mut peers, cluster_version), (source, peer)| async move {
864+
let new_cluster_version = match (&cluster_version, source) {
865+
(None, NodeInfoSource::Local) => peer.server_version.clone(),
866+
_ => cluster_version,
867+
};
868+
869+
peers.push(peer);
870+
(peers, new_cluster_version)
871+
},
872+
)
846873
.await;
847-
Ok(peers)
874+
Ok((peers, cluster_version))
848875
}
849876

850877
async fn create_peer_from_row(

scylla/src/cluster/state.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub struct ClusterState {
2424
pub(crate) known_peers: HashMap<Uuid, Arc<Node>>, // Invariant: nonempty after Cluster::new()
2525
pub(crate) keyspaces: HashMap<String, Keyspace>,
2626
pub(crate) locator: ReplicaLocator,
27+
pub(crate) cluster_version: Option<String>,
2728
}
2829

2930
/// Enables printing [ClusterState] struct in a neat way, skipping the clutter involved by
@@ -201,6 +202,7 @@ impl ClusterState {
201202
known_peers: new_known_peers,
202203
keyspaces,
203204
locator,
205+
cluster_version: metadata.cluster_version,
204206
}
205207
}
206208

@@ -219,6 +221,16 @@ impl ClusterState {
219221
self.locator.unique_nodes_in_global_ring()
220222
}
221223

224+
/// Returns the cluster version.
225+
///
226+
/// Cluster version is the server application version used by
227+
/// the current control connection node (if such information is provided by the server).
228+
///
229+
/// To check version of each node, we suggest using [`ClusterState::get_nodes_info()`].
230+
pub fn cluster_version(&self) -> Option<&str> {
231+
self.cluster_version.as_deref()
232+
}
233+
222234
/// Compute token of a table partition key
223235
///
224236
/// `partition_key` argument contains the values of all partition key

scylla/src/policies/load_balancing/default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,7 @@ mod tests {
14441444
let info = Metadata {
14451445
peers,
14461446
keyspaces: HashMap::new(),
1447+
cluster_version: None,
14471448
};
14481449

14491450
ClusterState::new(

scylla/src/policies/load_balancing/plan.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ mod tests {
229229
known_peers: Default::default(),
230230
keyspaces: Default::default(),
231231
locator,
232+
cluster_version: None,
232233
};
233234
let routing_info = RoutingInfo::default();
234235
let plan = Plan::new(&policy, &routing_info, &cluster_state);

scylla/src/routing/locator/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ pub(crate) fn mock_metadata_for_token_aware_tests() -> Metadata {
168168
Metadata {
169169
peers: Vec::from(peers),
170170
keyspaces,
171+
cluster_version: None,
171172
}
172173
}
173174

0 commit comments

Comments
 (0)