Skip to content

Commit f763fcd

Browse files
committed
Tablets: handle schema / topology changes
There are some situations where tablet information can become stale. This can have various consequences: - it would prevent old `Node` objects from being destroyed and make them continue to being used in LBP. - some of the replicas may be ignored indefinitely by the driver, increasing load on the rest. To prevent this we need to update tablet info during topology change with newly fetched data about cluster.
1 parent 9fd6f5f commit f763fcd

File tree

2 files changed

+741
-24
lines changed

2 files changed

+741
-24
lines changed

scylla/src/transport/cluster.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use itertools::Itertools;
2020
use scylla_cql::errors::{BadQuery, NewSessionError};
2121
use scylla_cql::frame::response::result::TableSpec;
2222
use scylla_cql::types::serialize::row::SerializedValues;
23-
use std::collections::HashMap;
23+
use std::collections::{HashMap, HashSet};
2424
use std::net::SocketAddr;
2525
use std::sync::Arc;
2626
use std::time::Duration;
@@ -285,7 +285,7 @@ impl ClusterData {
285285
known_peers: &HashMap<Uuid, Arc<Node>>,
286286
used_keyspace: &Option<VerifiedKeyspaceName>,
287287
host_filter: Option<&dyn HostFilter>,
288-
tablets: TabletsInfo,
288+
mut tablets: TabletsInfo,
289289
) -> Self {
290290
// Create new updated known_peers and ring
291291
let mut new_known_peers: HashMap<Uuid, Arc<Node>> =
@@ -345,6 +345,47 @@ impl ClusterData {
345345
}
346346
}
347347

348+
{
349+
let removed_nodes = {
350+
let mut removed_nodes = HashSet::new();
351+
for old_peer in known_peers {
352+
if !new_known_peers.contains_key(old_peer.0) {
353+
removed_nodes.insert(*old_peer.0);
354+
}
355+
}
356+
357+
removed_nodes
358+
};
359+
360+
let table_predicate = |spec: &TableSpec| {
361+
if let Some(ks) = metadata.keyspaces.get(spec.ks_name()) {
362+
ks.tables.contains_key(spec.table_name())
363+
} else {
364+
false
365+
}
366+
};
367+
368+
let recreated_nodes = {
369+
let mut recreated_nodes = HashMap::new();
370+
for (old_peer_id, old_peer_node) in known_peers {
371+
if let Some(new_peer_node) = new_known_peers.get(old_peer_id) {
372+
if !Arc::ptr_eq(old_peer_node, new_peer_node) {
373+
recreated_nodes.insert(*old_peer_id, Arc::clone(new_peer_node));
374+
}
375+
}
376+
}
377+
378+
recreated_nodes
379+
};
380+
381+
tablets.perform_maintenance(
382+
&table_predicate,
383+
&removed_nodes,
384+
&new_known_peers,
385+
&recreated_nodes,
386+
)
387+
}
388+
348389
Self::update_rack_count(&mut datacenters);
349390

350391
let keyspaces = metadata.keyspaces;
@@ -491,7 +532,22 @@ impl ClusterData {
491532
let replica_translator = |uuid: Uuid| self.known_peers.get(&uuid).cloned();
492533

493534
for (table, raw_tablet) in raw_tablets.into_iter() {
494-
let tablet = Tablet::from_raw_tablet(&raw_tablet, replica_translator);
535+
// Should we skip tablets that belong to a keyspace not present in
536+
// self.keyspaces? The keyspace could have been, without driver's knowledge:
537+
// 1. Dropped - in which case we'll remove its info soon (when refreshing
538+
// topology) anyway.
539+
// 2. Created - no harm in storing the info now.
540+
//
541+
// So I think we can safely skip checking keyspace presence.
542+
let tablet = match Tablet::from_raw_tablet(raw_tablet, replica_translator) {
543+
Ok(t) => t,
544+
Err((t, f)) => {
545+
debug!("Nodes ({}) that are replicas for a tablet {{ks: {}, table: {}, range: [{}. {}]}} not present in current ClusterData.known_peers. \
546+
Skipping these replicas until topology refresh",
547+
f.iter().format(", "), table.ks_name(), table.table_name(), t.range().0.value(), t.range().1.value());
548+
t
549+
}
550+
};
495551
self.locator.tablets.add_tablet(table, tablet);
496552
}
497553
}

0 commit comments

Comments
 (0)