Merge "Various optimizations to RingBuilder.rebalance()"
This commit is contained in:
commit
45b481b364
@ -760,12 +760,15 @@ class RingBuilder(object):
|
|||||||
key=lambda x: x['sort_key'])
|
key=lambda x: x['sort_key'])
|
||||||
|
|
||||||
tier2devs = defaultdict(list)
|
tier2devs = defaultdict(list)
|
||||||
tier2sort_key = defaultdict(list)
|
tier2sort_key = defaultdict(tuple)
|
||||||
|
tier2dev_sort_key = defaultdict(list)
|
||||||
max_tier_depth = 0
|
max_tier_depth = 0
|
||||||
for dev in available_devs:
|
for dev in available_devs:
|
||||||
for tier in tiers_for_dev(dev):
|
dev['tiers'] = tiers_for_dev(dev)
|
||||||
|
for tier in dev['tiers']:
|
||||||
tier2devs[tier].append(dev) # <-- starts out sorted!
|
tier2devs[tier].append(dev) # <-- starts out sorted!
|
||||||
tier2sort_key[tier].append(dev['sort_key'])
|
tier2dev_sort_key[tier].append(dev['sort_key'])
|
||||||
|
tier2sort_key[tier] = dev['sort_key']
|
||||||
if len(tier) > max_tier_depth:
|
if len(tier) > max_tier_depth:
|
||||||
max_tier_depth = len(tier)
|
max_tier_depth = len(tier)
|
||||||
|
|
||||||
@ -778,10 +781,10 @@ class RingBuilder(object):
|
|||||||
new_tiers_list = []
|
new_tiers_list = []
|
||||||
for tier in tiers_list:
|
for tier in tiers_list:
|
||||||
child_tiers = list(tier2children_sets[tier])
|
child_tiers = list(tier2children_sets[tier])
|
||||||
child_tiers.sort(key=lambda t: tier2sort_key[t][-1])
|
child_tiers.sort(key=tier2sort_key.__getitem__)
|
||||||
tier2children[tier] = child_tiers
|
tier2children[tier] = child_tiers
|
||||||
tier2children_sort_key[tier] = map(
|
tier2children_sort_key[tier] = map(
|
||||||
lambda t: tier2sort_key[t][-1], child_tiers)
|
tier2sort_key.__getitem__, child_tiers)
|
||||||
new_tiers_list.extend(child_tiers)
|
new_tiers_list.extend(child_tiers)
|
||||||
tiers_list = new_tiers_list
|
tiers_list = new_tiers_list
|
||||||
depth += 1
|
depth += 1
|
||||||
@ -794,7 +797,7 @@ class RingBuilder(object):
|
|||||||
for replica in self._replicas_for_part(part):
|
for replica in self._replicas_for_part(part):
|
||||||
if replica not in replace_replicas:
|
if replica not in replace_replicas:
|
||||||
dev = self.devs[self._replica2part2dev[replica][part]]
|
dev = self.devs[self._replica2part2dev[replica][part]]
|
||||||
for tier in tiers_for_dev(dev):
|
for tier in dev['tiers']:
|
||||||
other_replicas[tier] += 1
|
other_replicas[tier] += 1
|
||||||
unique_tiers_by_tier_len[len(tier)].add(tier)
|
unique_tiers_by_tier_len[len(tier)].add(tier)
|
||||||
|
|
||||||
@ -828,50 +831,45 @@ class RingBuilder(object):
|
|||||||
# with the largest sort_key value). This lets us
|
# with the largest sort_key value). This lets us
|
||||||
# short-circuit the search while still ensuring we get the
|
# short-circuit the search while still ensuring we get the
|
||||||
# right tier.
|
# right tier.
|
||||||
candidate_tiers = sorted(
|
|
||||||
tier2children[tier],
|
|
||||||
key=lambda tier: tier2devs[tier][-1]['sort_key'],
|
|
||||||
reverse=True)
|
|
||||||
candidates_with_replicas = \
|
candidates_with_replicas = \
|
||||||
unique_tiers_by_tier_len[len(tier) + 1]
|
unique_tiers_by_tier_len[len(tier) + 1]
|
||||||
if len(candidate_tiers) > len(candidates_with_replicas):
|
# Find a tier with the minimal replica count and the
|
||||||
# There exists at least one tier with 0 other
|
# hungriest drive among all the tiers with the minimal
|
||||||
# replicas, so avoid calling the min() below, which is
|
# replica count.
|
||||||
# expensive if you've got thousands of drives.
|
if len(tier2children[tier]) > \
|
||||||
min_replica_count = 0
|
len(candidates_with_replicas):
|
||||||
|
# There exists at least one tier with 0 other replicas
|
||||||
|
tier = max((t for t in tier2children[tier]
|
||||||
|
if other_replicas[t] == 0),
|
||||||
|
key=tier2sort_key.__getitem__)
|
||||||
else:
|
else:
|
||||||
min_replica_count = min(other_replicas[t]
|
tier = max(tier2children[tier],
|
||||||
for t in candidate_tiers)
|
key=lambda t: (-other_replicas[t],
|
||||||
# Find the first tier with the minimal replica count.
|
tier2sort_key[t]))
|
||||||
# Since they're sorted, this will also have the hungriest
|
|
||||||
# drive among all the tiers with the minimal replica
|
|
||||||
# count.
|
|
||||||
for t in candidate_tiers:
|
|
||||||
if other_replicas[t] == min_replica_count:
|
|
||||||
tier = t
|
|
||||||
break
|
|
||||||
depth += 1
|
depth += 1
|
||||||
dev = tier2devs[tier][-1]
|
dev = tier2devs[tier][-1]
|
||||||
dev['parts_wanted'] -= 1
|
dev['parts_wanted'] -= 1
|
||||||
dev['parts'] += 1
|
dev['parts'] += 1
|
||||||
old_sort_key = dev['sort_key']
|
old_sort_key = dev['sort_key']
|
||||||
new_sort_key = dev['sort_key'] = self._sort_key_for(dev)
|
new_sort_key = dev['sort_key'] = self._sort_key_for(dev)
|
||||||
for tier in tiers_for_dev(dev):
|
for tier in dev['tiers']:
|
||||||
other_replicas[tier] += 1
|
other_replicas[tier] += 1
|
||||||
unique_tiers_by_tier_len[len(tier)].add(tier)
|
unique_tiers_by_tier_len[len(tier)].add(tier)
|
||||||
|
|
||||||
index = bisect.bisect_left(tier2sort_key[tier],
|
index = bisect.bisect_left(tier2dev_sort_key[tier],
|
||||||
old_sort_key)
|
old_sort_key)
|
||||||
tier2devs[tier].pop(index)
|
tier2devs[tier].pop(index)
|
||||||
tier2sort_key[tier].pop(index)
|
tier2dev_sort_key[tier].pop(index)
|
||||||
|
|
||||||
new_index = bisect.bisect_left(tier2sort_key[tier],
|
new_index = bisect.bisect_left(tier2dev_sort_key[tier],
|
||||||
new_sort_key)
|
new_sort_key)
|
||||||
tier2devs[tier].insert(new_index, dev)
|
tier2devs[tier].insert(new_index, dev)
|
||||||
tier2sort_key[tier].insert(new_index, new_sort_key)
|
tier2dev_sort_key[tier].insert(new_index, new_sort_key)
|
||||||
|
|
||||||
|
new_last_sort_key = tier2dev_sort_key[tier][-1]
|
||||||
|
tier2sort_key[tier] = new_last_sort_key
|
||||||
|
|
||||||
# Now jiggle tier2children values to keep them sorted
|
# Now jiggle tier2children values to keep them sorted
|
||||||
new_last_sort_key = tier2sort_key[tier][-1]
|
|
||||||
parent_tier = tier[0:-1]
|
parent_tier = tier[0:-1]
|
||||||
index = bisect.bisect_left(
|
index = bisect.bisect_left(
|
||||||
tier2children_sort_key[parent_tier],
|
tier2children_sort_key[parent_tier],
|
||||||
@ -891,19 +889,10 @@ class RingBuilder(object):
|
|||||||
# Just to save memory and keep from accidental reuse.
|
# Just to save memory and keep from accidental reuse.
|
||||||
for dev in self._iter_devs():
|
for dev in self._iter_devs():
|
||||||
del dev['sort_key']
|
del dev['sort_key']
|
||||||
|
dev.pop('tiers', None) # May be absent for devices w/o weight
|
||||||
|
|
||||||
def _sort_key_for(self, dev):
|
def _sort_key_for(self, dev):
|
||||||
# The maximum value of self.parts is 2^32, which is 9 hex
|
return (dev['parts_wanted'], random.randint(0, 0xFFFF), dev['id'])
|
||||||
# digits wide (0x100000000). Using a width of 16 here gives us
|
|
||||||
# plenty of breathing room; you'd need more than 2^28 replicas
|
|
||||||
# to overflow it.
|
|
||||||
# Since the sort key is a string and therefore an ascii sort applies,
|
|
||||||
# the maximum_parts_wanted + parts_wanted is used so negative
|
|
||||||
# parts_wanted end up sorted above positive parts_wanted.
|
|
||||||
return '%016x.%04x.%04x' % (
|
|
||||||
(self.parts * self.replicas) + dev['parts_wanted'],
|
|
||||||
random.randint(0, 0xFFFF),
|
|
||||||
dev['id'])
|
|
||||||
|
|
||||||
def _build_max_replicas_by_tier(self):
|
def _build_max_replicas_by_tier(self):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user