Always fix devices with multiple part-replica assignments
I've found that given a sufficiently bad replica2part2dev table we can accidently not entirely fix palcement when more than two replicas of a part are assigned to the duplicate devices. It shows up most on > 3 replica rings when you have two *different* devices both holding two replicas. But you can see it on a three replica ring when all three replicas are assigned to the same device. Change-Id: Ieb213c1a259815a2ed657291242919cda568c7b5
This commit is contained in:
parent
dfb1b58f52
commit
b19dc1ddec
@ -978,12 +978,6 @@ class RingBuilder(object):
|
||||
if dev_id == NONE_DEV:
|
||||
continue
|
||||
dev = self.devs[dev_id]
|
||||
# the min part hour check is ignored iff a device has more
|
||||
# than one replica of a part assigned to it - which would have
|
||||
# only been possible on rings built with older version of code
|
||||
if (self._last_part_moves[part] < self.min_part_hours and
|
||||
not replicas_at_tier[dev['tiers'][-1]] > 1):
|
||||
break
|
||||
if all(replicas_at_tier[tier] <=
|
||||
replica_plan[tier]['max']
|
||||
for tier in dev['tiers']):
|
||||
@ -996,8 +990,12 @@ class RingBuilder(object):
|
||||
undispersed_dev_replicas.sort(
|
||||
key=lambda dr: dr[0]['parts_wanted'])
|
||||
for dev, replica in undispersed_dev_replicas:
|
||||
if self._last_part_moves[part] < self.min_part_hours:
|
||||
break
|
||||
# the min part hour check is ignored iff a device has more
|
||||
# than one replica of a part assigned to it - which would have
|
||||
# only been possible on rings built with older version of code
|
||||
if (self._last_part_moves[part] < self.min_part_hours and
|
||||
not replicas_at_tier[dev['tiers'][-1]] > 1):
|
||||
continue
|
||||
dev['parts_wanted'] += 1
|
||||
dev['parts'] -= 1
|
||||
assign_parts[part].append(replica)
|
||||
|
@ -1113,6 +1113,46 @@ class TestRingBuilder(unittest.TestCase):
|
||||
rb.rebalance()
|
||||
self.assertEqual(rb.get_balance(), 0)
|
||||
|
||||
def test_multiple_duplicate_device_assignment(self):
|
||||
rb = ring.RingBuilder(4, 4, 1)
|
||||
devs = [
|
||||
'r1z1-127.0.0.1:33440/d1',
|
||||
'r1z1-127.0.0.1:33441/d2',
|
||||
'r1z1-127.0.0.1:33442/d3',
|
||||
'r1z1-127.0.0.1:33443/d4',
|
||||
'r1z1-127.0.0.2:33440/d5',
|
||||
'r1z1-127.0.0.2:33441/d6',
|
||||
'r1z1-127.0.0.2:33442/d7',
|
||||
'r1z1-127.0.0.2:33442/d8',
|
||||
]
|
||||
for add_value in devs:
|
||||
dev = utils.parse_add_value(add_value)
|
||||
dev['weight'] = 1.0
|
||||
rb.add_dev(dev)
|
||||
rb.rebalance()
|
||||
rb._replica2part2dev = [
|
||||
# these are the relevant one's here
|
||||
# | | | | |
|
||||
# v v v v v
|
||||
array('H', [0, 1, 2, 3, 3, 0, 0, 0, 4, 6, 4, 4, 4, 4, 4, 4]),
|
||||
array('H', [0, 1, 3, 1, 1, 1, 1, 1, 5, 7, 5, 5, 5, 5, 5, 5]),
|
||||
array('H', [0, 1, 2, 2, 2, 2, 2, 2, 4, 6, 6, 6, 6, 6, 6, 6]),
|
||||
array('H', [0, 3, 2, 3, 3, 3, 3, 3, 5, 7, 7, 7, 7, 7, 7, 7])
|
||||
# ^
|
||||
# |
|
||||
# this sort of thing worked already
|
||||
]
|
||||
# fix up bookkeeping
|
||||
new_dev_parts = defaultdict(int)
|
||||
for part2dev_id in rb._replica2part2dev:
|
||||
for dev_id in part2dev_id:
|
||||
new_dev_parts[dev_id] += 1
|
||||
for dev in rb._iter_devs():
|
||||
dev['parts'] = new_dev_parts[dev['id']]
|
||||
rb.pretend_min_part_hours_passed()
|
||||
rb.rebalance()
|
||||
rb.validate()
|
||||
|
||||
def test_region_fullness_with_balanceable_ring(self):
|
||||
rb = ring.RingBuilder(8, 3, 1)
|
||||
rb.add_dev({'id': 0, 'region': 0, 'zone': 0, 'weight': 1,
|
||||
|
Loading…
Reference in New Issue
Block a user