Skip to content

Commit fe8a842

Browse files
committed
Don't modify the Metadatas' hosts structure while iterating over it
On Python 3.x, dict.values() returns a view object and not a list of the values. Updating the content of the original dict while iterating over the values may raise a RuntimeError exception, which is the case in the Cluster._refresh_node_list_and_token_map() method to remove discovered hosts which are not part of the specified cluster contact points. The bug doesn't happen using Python 2.x as dict.values() returns a new list containing the values of the dictionary, without any other reference to original dictionary object. This fix forces the original Python 2.x behavior to be the same in Python 3.x as well. PYTHON-572
1 parent acae4c7 commit fe8a842

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

cassandra/metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ def all_hosts(self):
324324
Returns a list of all known :class:`.Host` instances in the cluster.
325325
"""
326326
with self._hosts_lock:
327-
return self._hosts.values()
327+
return list(self._hosts.values())
328328

329329

330330
REPLICATION_STRATEGY_CLASS_PREFIX = "org.apache.cassandra.locator."

tests/unit/test_metadata.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
LocalStrategy, protect_name,
3131
protect_names, protect_value, is_valid_name,
3232
UserType, KeyspaceMetadata, get_schema_parser,
33-
_UnknownStrategy, ColumnMetadata, TableMetadata, IndexMetadata, Function, Aggregate)
33+
_UnknownStrategy, ColumnMetadata, TableMetadata,
34+
IndexMetadata, Function, Aggregate,
35+
Metadata)
3436
from cassandra.policies import SimpleConvictionPolicy
3537
from cassandra.pool import Host
3638

@@ -474,3 +476,17 @@ def test_aggregate(self):
474476
def test_user_type(self):
475477
um = UserType(self.name, self.name, [self.name, self.name], [u'int', u'text'])
476478
um.export_as_string()
479+
480+
481+
class HostsTests(unittest.TestCase):
482+
def test_iterate_all_hosts_and_modify(self):
483+
metadata = Metadata()
484+
metadata.add_or_return_host(Host('dc1.1', SimpleConvictionPolicy))
485+
metadata.add_or_return_host(Host('dc1.2', SimpleConvictionPolicy))
486+
487+
assert len(metadata.all_hosts()) == 2
488+
489+
for host in metadata.all_hosts():
490+
metadata.remove_host(host)
491+
492+
assert len(metadata.all_hosts()) == 0

0 commit comments

Comments
 (0)