-
Notifications
You must be signed in to change notification settings - Fork 60
host: make host_id the canonical identity #1006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ | |
| import time | ||
| import random | ||
| import copy | ||
| import uuid | ||
| from threading import Lock, RLock, Condition | ||
| import weakref | ||
| try: | ||
|
|
@@ -131,11 +132,6 @@ class Host(object): | |
| release_version as queried from the control connection system tables | ||
| """ | ||
|
|
||
| host_id = None | ||
| """ | ||
| The unique identifier of the cassandra node | ||
| """ | ||
|
|
||
| dse_version = None | ||
| """ | ||
| dse_version as queried from the control connection system tables. Only populated when connecting to | ||
|
|
@@ -157,6 +153,7 @@ class Host(object): | |
| Not queried if :attr:`~.Cluster.token_metadata_enabled` is ``False``. | ||
| """ | ||
|
|
||
| _host_id = None | ||
| _datacenter = None | ||
| _rack = None | ||
| _reconnection_handler = None | ||
|
|
@@ -172,11 +169,15 @@ def __init__(self, endpoint, conviction_policy_factory, datacenter=None, rack=No | |
| if conviction_policy_factory is None: | ||
| raise ValueError("conviction_policy_factory may not be None") | ||
|
|
||
| if not isinstance(host_id, uuid.UUID): | ||
| raise TypeError("host_id must be a uuid.UUID") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Minor] 🟡 @sylwiaszunejko's earlier point still stands: Making it required is also what turns tests/unit/test_host_connection_pool.py:224 into a real missing-argument assertion rather than a second hit on this same isinstance branch.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping current signature. Making |
||
| if host_id.int == 0: | ||
| raise ValueError("host_id may not be the nil UUID") | ||
|
|
||
| self.endpoint = endpoint if isinstance(endpoint, EndPoint) else DefaultEndPoint(endpoint) | ||
| self._host_id = host_id | ||
| self._is_removed = False | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Minor] 🟡 Adding |
||
| self.conviction_policy = conviction_policy_factory(self) | ||
|
dkropachev marked this conversation as resolved.
|
||
| if not host_id: | ||
| raise ValueError("host_id may not be None") | ||
| self.host_id = host_id | ||
| self.set_location_info(datacenter, rack) | ||
| self.lock = RLock() | ||
|
|
||
|
|
@@ -188,6 +189,13 @@ def address(self): | |
| # backward compatibility | ||
| return self.endpoint.address | ||
|
|
||
| @property | ||
| def host_id(self): | ||
| """ | ||
| The immutable unique identifier of the Cassandra node. | ||
| """ | ||
| return self._host_id | ||
|
|
||
| @property | ||
| def datacenter(self): | ||
| """ The datacenter the node is in. """ | ||
|
|
@@ -232,23 +240,25 @@ def get_and_set_reconnection_handler(self, new_handler): | |
| self._reconnection_handler = new_handler | ||
| return old | ||
|
|
||
| def _clear_reconnection_handler(self, handler): | ||
| with self.lock: | ||
| if self._reconnection_handler is not handler: | ||
| return False | ||
| self._reconnection_handler = None | ||
| return not self._is_removed | ||
|
|
||
| def __eq__(self, other): | ||
| if isinstance(other, Host): | ||
| return self.endpoint == other.endpoint | ||
| else: # TODO Backward compatibility, remove next major | ||
| return self.endpoint.address == other | ||
| if not isinstance(other, Host): | ||
| return NotImplemented | ||
| return self.host_id == other.host_id | ||
|
|
||
| def __hash__(self): | ||
| return hash(self.endpoint) | ||
| return hash(self.host_id) | ||
|
|
||
| def __lt__(self, other): | ||
| self_is_unix = isinstance(self.endpoint, UnixSocketEndPoint) | ||
| other_is_unix = isinstance(other.endpoint, UnixSocketEndPoint) | ||
| if self_is_unix != other_is_unix: | ||
| # Endpoint comparators assume same-kind operands, so partition | ||
| # Unix and network Hosts before delegating their ordering. | ||
| return self_is_unix | ||
| return self.endpoint < other.endpoint | ||
| if not isinstance(other, Host): | ||
| return NotImplemented | ||
| return self.host_id < other.host_id | ||
|
|
||
| def __str__(self): | ||
| return str(self.endpoint) | ||
|
|
@@ -265,6 +275,7 @@ class _ReconnectionHandler(object): | |
| """ | ||
|
|
||
| _cancelled = False | ||
| _clear_handler_before_reconnection = False | ||
|
|
||
| def __init__(self, scheduler, schedule, callback, *callback_args, **callback_kwargs): | ||
| self.scheduler = scheduler | ||
|
|
@@ -305,15 +316,23 @@ def run(self): | |
| self.scheduler.schedule(next_delay, self.run) | ||
| else: | ||
| if not self._cancelled: | ||
| if (self._clear_handler_before_reconnection and | ||
| not self._release_reconnection_handler()): | ||
| return | ||
| self.on_reconnection(conn) | ||
| self.callback(*(self.callback_args), **(self.callback_kwargs)) | ||
| if not self._clear_handler_before_reconnection: | ||
| self._release_reconnection_handler() | ||
| finally: | ||
| if conn: | ||
| conn.close() | ||
|
|
||
| def cancel(self): | ||
| self._cancelled = True | ||
|
|
||
| def _release_reconnection_handler(self): | ||
| self.callback(*(self.callback_args), **(self.callback_kwargs)) | ||
| return True | ||
|
|
||
| def try_reconnect(self): | ||
| """ | ||
| Subclasses must implement this method. It should attempt to | ||
|
|
@@ -349,6 +368,12 @@ def on_exception(self, exc, next_delay): | |
|
|
||
| class _HostReconnectionHandler(_ReconnectionHandler): | ||
|
|
||
| # Host reconnection callbacks can synchronously start another reconnector | ||
| # when rebuilding pools fails. Clear this handler first so that failure is | ||
| # not suppressed as already reconnecting and post-callback cleanup cannot | ||
| # clear the successor. | ||
| _clear_handler_before_reconnection = True | ||
|
|
||
| def __init__(self, host, connection_factory, is_host_addition, on_add, on_up, *args, **kwargs): | ||
| _ReconnectionHandler.__init__(self, *args, **kwargs) | ||
| self.is_host_addition = is_host_addition | ||
|
|
@@ -360,6 +385,9 @@ def __init__(self, host, connection_factory, is_host_addition, on_add, on_up, *a | |
| def try_reconnect(self): | ||
| return self.connection_factory() | ||
|
|
||
| def _release_reconnection_handler(self): | ||
| return self.host._clear_reconnection_handler(self) | ||
|
|
||
| def on_reconnection(self, connection): | ||
| log.info("Successful reconnection to %s, marking node up if it isn't already", self.host) | ||
| if self.is_host_addition: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Major] 🟠 This makes
host_idrequired and read-only and the basis of__eq__/__lt__/__hash__(250-261), droppingHost == addressand endpoint ordering. CONTRIBUTING.rst:26 says breaking API changes land only in major versions, and the CHANGELOG entry sits under Unreleased "Others".coderabbit raised this and it was acknowledged but nothing changed — downstream
Host(endpoint, policy),host.host_id = xandsorted(hosts)all break on a minor upgrade with no deprecation path. Worth an explicit maintainer decision recorded on the PR.