From fb1a67fe6bc919002e9a33aaf73a973fc48b7828 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu <niuzhenguo@huawei.com> Date: Fri, 16 Oct 2015 17:18:12 +0800 Subject: [PATCH] Move hash_ring refresh logic out of sync_local_state As sync_local_state periodic task can be disabled, which will lose out on hash ring refresh, so the refresh logic should be independent of it. this patch adds a updated_at to HashRingManager to track of when the ring was last refreshed, and when checks the ring is set, also check the age and rebuild if older than the new config option hash_ring_reset_interval which is default to 180s. Change-Id: Ie46dbf93b920543f99e11774a29878aaf27c3400 Closes-Bug: #1506657 --- etc/ironic/ironic.conf.sample | 4 ++++ ironic/common/hash_ring.py | 12 ++++++++++-- ironic/conductor/manager.py | 1 - ironic/tests/unit/common/test_hash_ring.py | 12 ++++++++++++ ironic/tests/unit/conductor/test_manager.py | 2 -- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index c1362dbf5a..5415fabdf4 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -67,6 +67,10 @@ # (integer value) #hash_distribution_replicas=1 +# Interval (in seconds) between hash ring resets. (integer +# value) +#hash_ring_reset_interval=180 + # # Options defined in ironic.common.images diff --git a/ironic/common/hash_ring.py b/ironic/common/hash_ring.py index b345251caa..baeb957432 100644 --- a/ironic/common/hash_ring.py +++ b/ironic/common/hash_ring.py @@ -16,6 +16,7 @@ import bisect import hashlib import threading +import time from oslo_config import cfg import six @@ -47,6 +48,9 @@ hash_opts = [ 'conductor services to prepare deployment environments ' 'and potentially allow the Ironic cluster to recover ' 'more quickly if a conductor instance is terminated.')), + cfg.IntOpt('hash_ring_reset_interval', + default=180, + help=_('Interval (in seconds) between hash ring resets.')), ] CONF = cfg.CONF @@ -166,17 +170,21 @@ class HashRingManager(object): def __init__(self): self.dbapi = dbapi.get_instance() + self.updated_at = time.time() @property def ring(self): + interval = CONF.hash_ring_reset_interval + limit = time.time() - interval # Hot path, no lock - if self._hash_rings is not None: + if self._hash_rings is not None and self.updated_at >= limit: return self._hash_rings with self._lock: - if self._hash_rings is None: + if self._hash_rings is None or self.updated_at < limit: rings = self._load_hash_rings() self.__class__._hash_rings = rings + self.updated_at = time.time() return self._hash_rings def _load_hash_rings(self): diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py index 0f01ec5baf..9312ea8e2d 100644 --- a/ironic/conductor/manager.py +++ b/ironic/conductor/manager.py @@ -1406,7 +1406,6 @@ class ConductorManager(periodic_task.PeriodicTasks): The ensuing actions could include preparing a PXE environment, updating the DHCP server, and so on. """ - self.ring_manager.reset() filters = {'reserved': False, 'maintenance': False, 'provision_state': states.ACTIVE} diff --git a/ironic/tests/unit/common/test_hash_ring.py b/ironic/tests/unit/common/test_hash_ring.py index 832c975b28..b5a36c2d9a 100644 --- a/ironic/tests/unit/common/test_hash_ring.py +++ b/ironic/tests/unit/common/test_hash_ring.py @@ -14,6 +14,7 @@ # under the License. import hashlib +import time import mock from oslo_config import cfg @@ -249,3 +250,14 @@ class HashRingManagerTestCase(db_base.DbTestCase): self.assertRaises(exception.DriverNotFound, self.ring_manager.__getitem__, 'driver1') + + def test_hash_ring_manager_refresh(self): + CONF.set_override('hash_ring_reset_interval', 30) + # Initialize the ring manager to make _hash_rings not None, then + # hash ring will refresh only when time interval exceeded. + self.assertRaises(exception.DriverNotFound, + self.ring_manager.__getitem__, + 'driver1') + self.register_conductors() + self.ring_manager.updated_at = time.time() - 30 + self.ring_manager.__getitem__('driver1') diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py index f86026bb30..942249fd45 100644 --- a/ironic/tests/unit/conductor/test_manager.py +++ b/ironic/tests/unit/conductor/test_manager.py @@ -3830,7 +3830,6 @@ class ManagerSyncLocalStateTestCase(_CommonMixIn, tests_db_base.DbTestCase): self._assert_get_nodeinfo_args(get_nodeinfo_mock) mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver) self.assertFalse(acquire_mock.called) - self.service.ring_manager.reset.assert_called_once_with() def test_already_mapped(self, get_nodeinfo_mock, mapped_mock, acquire_mock): @@ -3846,7 +3845,6 @@ class ManagerSyncLocalStateTestCase(_CommonMixIn, tests_db_base.DbTestCase): self._assert_get_nodeinfo_args(get_nodeinfo_mock) mapped_mock.assert_called_once_with(self.node.uuid, self.node.driver) self.assertFalse(acquire_mock.called) - self.service.ring_manager.reset.assert_called_once_with() def test_good(self, get_nodeinfo_mock, mapped_mock, acquire_mock): get_nodeinfo_mock.return_value = self._get_nodeinfo_list_response()