Problem
A session created with cluster.connect("ks") stores its keyspace:
|
def connect(self, keyspace=None, wait_for_all_pools=False): |
|
""" |
|
Creates and returns a new :class:`~.Session` object. |
|
|
|
If `keyspace` is specified, that keyspace will be the default keyspace for |
|
operations on the ``Session``. |
|
def __init__(self, cluster, hosts, keyspace=None): |
|
self.cluster = cluster |
|
self.hosts = hosts |
|
self.keyspace = keyspace |
When no node pool is available, fallback sends directly through the shared control connection without applying that keyspace:
|
def _query_control_connection(self, message=None, cb=None, connection=None, host=None): |
|
self._control_connection_query_attempted = True |
|
|
|
if message is None: |
|
message = self.message |
|
|
|
if connection is None: |
|
control_connection = self.session.cluster.control_connection |
|
connection = control_connection._connection if control_connection else None |
|
if not connection: |
|
self._errors['control connection'] = ConnectionException("Control connection is not connected") |
|
return None |
|
|
|
if host is None: |
|
host = self.session.cluster.get_control_connection_host() or connection.endpoint |
|
self._current_host = host |
|
|
|
request_id = None |
|
request_sent = False |
|
try: |
|
request_id = self._borrow_control_connection(connection) |
|
self._connection = connection |
|
result_meta = self._bound_result_metadata |
|
if cb is None: |
|
cb = partial(self._set_result, host, connection, None) |
|
cb = partial(self._handle_control_connection_response, connection, cb) |
|
|
|
log.debug("No usable node pools; falling back to control connection for host %s", host) |
|
self.request_encoded_size = connection.send_msg(message, request_id, cb=cb, |
|
encoder=self._protocol_handler.encode_message, |
|
decoder=self._protocol_handler.decode_message, |
|
result_metadata=result_meta) |
Unqualified queries and prepares can therefore fail or execute against stale keyspace state on the shared connection.
The control connection is multiplexed between concurrent requests, while USE changes connection-wide state. It cannot safely serve fallback requests from sessions using different keyspaces without serializing complete request lifetimes or introducing separate connections.
Expected behavior
Keep control-connection fallback deliberately single-keyspace:
- The first session that uses fallback binds application use of the shared control connection to its keyspace, including no keyspace.
- The binding lasts for the lifetime of the
Cluster and survives physical control-connection replacement.
- Other sessions may use fallback only when their keyspace matches the binding. A session using a different keyspace is rejected immediately with
InvalidRequest rather than risking execution in the wrong keyspace.
- Before the first application request on a fresh or reconnected physical control connection, select the bound keyspace when one is configured.
- Reject an explicit
USE statement on the fallback path, including the statement issued by Session.set_keyspace(), because it would mutate connection-wide state beneath every session sharing the fallback.
- Requests served by ordinary node pools keep their existing behavior.
Add coverage for same-keyspace sharing, conflicting keyspaces, keyspace-less sessions, explicit USE, prepared statements, and control-connection replacement. No fallback request may execute using keyspace state established by another session.
Problem
A session created with
cluster.connect("ks")stores its keyspace:python-driver/cassandra/cluster.py
Lines 1744 to 1749 in b666e5c
python-driver/cassandra/cluster.py
Lines 2619 to 2622 in b666e5c
When no node pool is available, fallback sends directly through the shared control connection without applying that keyspace:
python-driver/cassandra/cluster.py
Lines 5065 to 5096 in b666e5c
Unqualified queries and prepares can therefore fail or execute against stale keyspace state on the shared connection.
The control connection is multiplexed between concurrent requests, while
USEchanges connection-wide state. It cannot safely serve fallback requests from sessions using different keyspaces without serializing complete request lifetimes or introducing separate connections.Expected behavior
Keep control-connection fallback deliberately single-keyspace:
Clusterand survives physical control-connection replacement.InvalidRequestrather than risking execution in the wrong keyspace.USEstatement on the fallback path, including the statement issued bySession.set_keyspace(), because it would mutate connection-wide state beneath every session sharing the fallback.Add coverage for same-keyspace sharing, conflicting keyspaces, keyspace-less sessions, explicit
USE, prepared statements, and control-connection replacement. No fallback request may execute using keyspace state established by another session.