Uh oh!
There was an error while loading. Please reload this page.
Add Private link support - #706
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
e96bab0 to
743efb2Compare
dkropachev
left a comment
There was a problem hiding this comment.
Can you please ask AI to annotate new code
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
118bcc5 to
edef7ebCompare9fe6cc5 to
64cc164CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
dkropachev
left a comment
There was a problem hiding this comment.
- You need to reload add the records on control connection reconnection
- You need to make sure that when one host_id has two records with different connection ids, it sticks to one it had before update, only when old connection id is gone it can migrate from one to another.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
64cc164 to
7bacc52Comparesylwiaszunejko
commented
Mar 6, 2026
I pushed partial fix, I still need to address top comment #706 (review) |
7bacc52 to
82930a3Comparesylwiaszunejko
commented
Mar 9, 2026
@dkropachev I pushed the changes, I should probably split them into smaller commit, because it got to be quite big, but I think you can review to see if the content looks right |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ba8938c to
f6e06eaCompare
dkropachev
left a comment
There was a problem hiding this comment.
Review of the Private Link implementation. Found several issues ranging from bugs to design improvements.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
4feb0fc to
ad72577CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ad72577 to
c4cac93Compare
Lorak-mmk
left a comment
There was a problem hiding this comment.
Apart from other open comments I found just one more problem.
| if self._cluster._client_routes_handler is not None: | ||
| try: | ||
| self._cluster._client_routes_handler.handle_control_connection_reconnect(self) | ||
| except Exception as e: | ||
| log.warning("[control connection] Failed to notify client routes handler of reconnection: %s", e) |
There was a problem hiding this comment.
Now you don't swallow the exception at handle_control_connection_reconnect, but you do it here. Isn't that going to cause the same problem as I previously described?
dkropachev
commented
Mar 17, 2026
@sylwiaszunejko I've implemented the requested functionality to override route addresses with proxy connection_addr when available. The implementation:
This ensures that whenever All existing unit tests continue to pass. The changes are in commit 1dc487e36. |
fbb89e5 to
6e26161Comparesylwiaszunejko
commented
Mar 17, 2026
@dkropachev@Lorak-mmk |
6e26161 to
3a22a40Compare
Lorak-mmk
left a comment
There was a problem hiding this comment.
A few comments, mostly about integration of new system with CC
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| until resolve() is called at connection time. This ensures immediate | ||
| reaction to infrastructure changes and CLIENT_ROUTES_CHANGE events. | ||
| """ | ||
There was a problem hiding this comment.
By defautl it should be disabled, but we should have an option to enabled it through ClientRouteConfig
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Introduce the data layer for Private Link client routes support: - ClientRoutesChangeType enum for CLIENT_ROUTES_CHANGE event types - ClientRouteProxy dataclass and ClientRoutesConfig for user-facing configuration - _Route frozen dataclass for immutable route records - _RouteStore for thread-safe route storage with atomic update/merge and preferred route selection that avoids unnecessary connection_id migration when multiple routes exist for the same host
Add _ClientRoutesHandler which manages the full lifecycle of dynamic address translation via system.client_routes: - initialize(): loads all routes at startup and on control connection reconnect - handle_client_routes_change(): processes CLIENT_ROUTES_CHANGE events with targeted merge or full refresh depending on event data - _query_all_routes_for_connections(): complete refresh query using connection_id IN (...) - _query_routes_for_change_event(): targeted query grouping by connection_id with host_id IN (...) per group - _execute_routes_query(): common query execution and result parsing with proxy address override support - resolve_host(): host_id to (address, port) resolution with DNS lookup
- ClientRoutesEndPointFactory: creates endpoints from system.peers rows by extracting host_id, deferring address translation and DNS resolution until connection time - ClientRoutesEndPoint: endpoint that resolves via _ClientRoutesHandler on each connection attempt, ensuring immediate reaction to route changes and CLIENT_ROUTES_CHANGE events
Cluster: - Add client_routes_config parameter with mutual exclusivity check against endpoint_factory - Create _ClientRoutesHandler and ClientRoutesEndPointFactory when client_routes_config is provided ControlConnection: - Register CLIENT_ROUTES_CHANGE event watcher when handler is present - Forward events to handler via _handle_client_routes_change - Trigger full route re-read on control connection reconnection
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Cover ClientRouteEntry/ClientRoutesConfig validation, _RouteStore get/merge operations, _ClientRoutesHandler initialization, ClientRoutesEndPoint resolution with and without route mappings, and SSL check_hostname rejection with client_routes_config.
Add comprehensive integration tests covering: - TCP proxy and NLB emulator infrastructure for simulating private link connectivity - query_routes filtering with different connection/host ID combinations - Full private-link connectivity verifying all driver connections go exclusively through the NLB proxy - Dynamic route updates via REST API with driver reconnection through new proxy ports
Overview
This PR adds Private Link support to the Python driver via the
system.client_routestable andCLIENT_ROUTES_CHANGECQL protocol events. It enables the driver to dynamically translate node addresses through server-provided route mappings, which is required for Private Link.Architecture
New classes introduced
User-facing configuration (
cassandra/client_routes.py):ClientRouteProxy— Represents a single route endpoint configuration with aconnection_id(string identifying the Private Link connection) and an optionalconnection_addr(used as a seed contact point).ClientRoutesConfig— Bundles a list ofClientRouteEntryobjects. Passed toCluster(client_routes_config=...).Internal route storage (
cassandra/client_routes.py):_Route— Dataclass holding a single row fromsystem.client_routes(connection_id, host_id, address, port, tls_port, datacenter, rack). Stores only the raw table data._RouteStore— Thread-safe storage for routes keyed byhost_id. Uses atomic dict pointer swaps for lock-free reads andRLock-serialized writes. Includes preferred route selection logic: when multiple routes exist for the samehost_id(differentconnection_ids), the currently activeconnection_idis preferred to avoid unnecessary connection migration.Route lifecycle handler (
cassandra/client_routes.py):_ClientRoutesHandler— Central coordinator for the client routes feature. Manages:system.client_routesfor all configuredconnection_ids after the control connection is established.CLIENT_ROUTES_CHANGEevents by re-querying only the affectedconnection_ids andhost_ids, filtering to configured endpoints.resolve_host()translates ahost_idto an(ip, port)pair. DNS results are cached in a separatedictkeyed by hostname (not per-route), with TTL controlled bycache_ttl_seconds. Port selection is TLS-aware (tls_portwhen SSL is enabled).Connection-layer endpoint classes (
cassandra/connection.py):ClientRoutesEndPointFactory—EndPointFactoryimplementation that createsClientRoutesEndPointinstances fromsystem.peersrows. Extracts only thehost_idand stores a reference to the handler; no address translation or DNS resolution happens at creation time.ClientRoutesEndPoint—EndPointimplementation that defers all resolution to connection time. Itsresolve()method delegates to_ClientRoutesHandler.resolve_host(), which looks up the route and resolves DNS. On failure, it falls back toDefaultEndPointbehavior (returning the original address/port fromsystem.peers).Edge cases and error handling
resolve_host()returns None and we fallback to to the original address/port fromsystem.peers.handle_control_connection_reconnect()uses_RouteStore.update()(full replace) instead ofmerge()to remove routes that no longer exist insystem.client_routes._RouteStore._select_preferred_routes()prefers the currently activeconnection_idto avoid unnecessary connection migration. Falls back to the first available route when the current one disappears.CLIENT_ROUTES_CHANGEfiltering: Events includeconnection_ids; the handler only processes those matching its configured endpoints, ignoring irrelevant changes.client_routes_configandendpoint_factorycannot be used together — raisesValueErroratCluster.__init__.Pre-review checklist
./docs/source/.Fixes:annotations to PR description.Fixes: #692