Skip to content

Commit 601f065

Browse files
committed
perf: avoid redundant tablet lookup in shard-aware connection selection
When tablets are in use, get_tablet_for_key() was called twice per request: once in TokenAwarePolicy.make_query_plan() to find the replica, and again in HostConnection._get_connection_for_routing_key() to determine the shard. Stash the tablet found during query planning on the query object (query._tablet) and pass it through to borrow_connection(), which skips the second lookup when a tablet is already available. This eliminates redundant bisect_left calls and associated dict lookups.
1 parent 8e395c7 commit 601f065

3 files changed

Lines changed: 22 additions & 10 deletions

File tree

cassandra/cluster.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4613,7 +4613,10 @@ def _query(self, host, message=None, cb=None):
46134613
try:
46144614
# TODO get connectTimeout from cluster settings
46154615
if self.query:
4616-
connection, request_id = pool.borrow_connection(timeout=2.0, routing_key=self.query.routing_key, keyspace=self.query.keyspace, table=self.query.table)
4616+
connection, request_id = pool.borrow_connection(
4617+
timeout=2.0, routing_key=self.query.routing_key,
4618+
keyspace=self.query.keyspace, table=self.query.table,
4619+
tablet=getattr(self.query, '_tablet', None))
46174620
else:
46184621
connection, request_id = pool.borrow_connection(timeout=2.0)
46194622
self._connection = connection

cassandra/policies.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ def make_query_plan(self, working_keyspace=None, query=None):
511511
child_plan = child.make_query_plan(keyspace, query)
512512

513513
replicas = [host for host in child_plan if host.host_id in replica_dict]
514+
# Stash the tablet so that downstream shard-aware
515+
# connection selection can reuse it instead of
516+
# repeating the bisect lookup.
517+
query._tablet = tablet
514518
else:
515519
replicas = self._cluster_metadata.get_replicas(keyspace, query.routing_key)
516520

cassandra/pool.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def __init__(self, host, host_distance, session):
442442

443443
log.debug("Finished initializing connection for host %s", self.host)
444444

445-
def _get_connection_for_routing_key(self, routing_key=None, keyspace=None, table=None):
445+
def _get_connection_for_routing_key(self, routing_key=None, keyspace=None, table=None, tablet=None):
446446
if self.is_shutdown:
447447
raise ConnectionException(
448448
"Pool for %s is shutdown" % (self.host,), self.host)
@@ -456,13 +456,18 @@ def _get_connection_for_routing_key(self, routing_key=None, keyspace=None, table
456456

457457
shard_id = None
458458
if self.tablets_routing_v1 and table is not None:
459-
if keyspace is None:
460-
keyspace = self._keyspace
461-
462-
tablet = self._session.cluster.metadata._tablets.get_tablet_for_key(keyspace, table, t)
463-
459+
# Reuse tablet from query planning if available, avoiding
460+
# a redundant bisect lookup in the tablet map.
464461
if tablet is not None:
465462
shard_id = tablet._replica_dict.get(self.host.host_id)
463+
else:
464+
if keyspace is None:
465+
keyspace = self._keyspace
466+
467+
tablet = self._session.cluster.metadata._tablets.get_tablet_for_key(keyspace, table, t)
468+
469+
if tablet is not None:
470+
shard_id = tablet._replica_dict.get(self.host.host_id)
466471

467472
if shard_id is None:
468473
shard_id = self.host.sharding_info.shard_id_from_token(t.value)
@@ -505,15 +510,15 @@ def _get_connection_for_routing_key(self, routing_key=None, keyspace=None, table
505510
return random.choice(active_connections)
506511
return random.choice(list(self._connections.values()))
507512

508-
def borrow_connection(self, timeout, routing_key=None, keyspace=None, table=None):
509-
conn = self._get_connection_for_routing_key(routing_key, keyspace, table)
513+
def borrow_connection(self, timeout, routing_key=None, keyspace=None, table=None, tablet=None):
514+
conn = self._get_connection_for_routing_key(routing_key, keyspace, table, tablet)
510515
start = time.time()
511516
remaining = timeout
512517
last_retry = False
513518
while True:
514519
if conn.is_closed:
515520
# The connection might have been closed in the meantime - if so, try again
516-
conn = self._get_connection_for_routing_key(routing_key, keyspace, table)
521+
conn = self._get_connection_for_routing_key(routing_key, keyspace, table, tablet)
517522
with conn.lock:
518523
if (not conn.is_closed or last_retry) and conn.in_flight < conn.max_request_id:
519524
# On last retry we ignore connection status, since it is better to return closed connection than

0 commit comments

Comments
 (0)