Skip to content

control-connection fallback: preserve session keyspace #1013

Description

@dkropachev

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrelease-blockerMust be resolved before the next release.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions