def validate(self, stats=False): """ Validate the ring. This is a safety function to try to catch any bugs in the building process. It ensures partitions have been assigned to real devices, aren't doubly assigned, etc. It can also optionally check the even distribution of partitions across devices. :param stats: if True, check distribution of partitions across devices :returns: if stats is True, a tuple of (device_usage, worst_stat), else (None, None). device_usage[dev_id] will equal the number of partitions assigned to that device. worst_stat will equal the number of partitions the worst device is skewed from the number it should have. :raises RingValidationError: problem was found with the ring. """ if sum(d['parts'] for d in self._iter_devs()) != \ self.parts * self.replicas: raise exceptions.RingValidationError( 'All partitions are not double accounted for: %d != %d' % (sum(d['parts'] for d in self._iter_devs()), self.parts * self.replicas)) if stats: # dev_usage[dev_id] will equal the number of partitions assigned to # that device. dev_usage = array('I', (0 for _junk in xrange(len(self.devs)))) for part2dev in self._replica2part2dev: for dev_id in part2dev: dev_usage[dev_id] += 1 for part in xrange(self.parts): for replica in xrange(self.replicas): dev_id = self._replica2part2dev[replica][part] if dev_id >= len(self.devs) or not self.devs[dev_id]: raise exceptions.RingValidationError( "Partition %d, replica %d was not allocated " "to a device." % (part, replica)) if stats: weight_of_one_part = self.weight_of_one_part() worst = 0 for dev in self._iter_devs(): if not dev['weight']: if dev_usage[dev['id']]: # If a device has no weight, but has partitions, then # its overage is considered "infinity" and therefore # always the worst possible. We show 999.99 for # convenience. worst = 999.99 break continue skew = abs(100.0 * dev_usage[dev['id']] / (dev['weight'] * weight_of_one_part) - 100.0) if skew > worst: worst = skew return dev_usage, worst return None, None
def validate(self, stats=False): """ Validate the ring. This is a safety function to try to catch any bugs in the building process. It ensures partitions have been assigned to distinct zones, aren't doubly assigned, etc. It can also optionally check the even distribution of partitions across devices. :param stats: if True, check distribution of partitions across devices :returns: if stats is True, a tuple of (device usage, worst stat), else (None, None) :raises RingValidationError: problem was found with the ring. """ if sum(d['parts'] for d in self.devs if d is not None) != \ self.parts * self.replicas: raise exceptions.RingValidationError( 'All partitions are not double accounted for: %d != %d' % (sum(d['parts'] for d in self.devs if d is not None), self.parts * self.replicas)) if stats: dev_usage = array('I', (0 for _junk in xrange(len(self.devs)))) for part in xrange(self.parts): zones = {} for replica in xrange(self.replicas): dev_id = self._replica2part2dev[replica][part] if stats: dev_usage[dev_id] += 1 zone = self.devs[dev_id]['zone'] if zone in zones: raise exceptions.RingValidationError( 'Partition %d not in %d distinct zones. ' \ 'Zones were: %s' % (part, self.replicas, [self.devs[self._replica2part2dev[r][part]]['zone'] for r in xrange(self.replicas)])) zones[zone] = True if stats: weighted_parts = self.weighted_parts() worst = 0 for dev in self.devs: if dev is None: continue if not dev['weight']: if dev_usage[dev['id']]: worst = 999.99 break continue skew = abs(100.0 * dev_usage[dev['id']] / (dev['weight'] * weighted_parts) - 100.0) if skew > worst: worst = skew return dev_usage, worst return None, None
def validate_replicas_by_tier(replicas, replicas_by_tier): """ Validate the sum of the replicas at each tier. The sum of the replicas at each tier should be less than or very close to the upper limit indicated by replicas :param replicas: float,the upper limit of replicas :param replicas_by_tier: defaultdict,the replicas by tier """ tiers = ['cluster', 'regions', 'zones', 'servers', 'devices'] for i, tier_name in enumerate(tiers): replicas_at_tier = sum(replicas_by_tier[t] for t in replicas_by_tier if len(t) == i) if abs(replicas - replicas_at_tier) > 1e-10: raise exceptions.RingValidationError( '%s != %s at tier %s' % (replicas_at_tier, replicas, tier_name))
def validate(self, stats=False): """ Validate the ring. This is a safety function to try to catch any bugs in the building process. It ensures partitions have been assigned to real devices, aren't doubly assigned, etc. It can also optionally check the even distribution of partitions across devices. :param stats: if True, check distribution of partitions across devices :returns: if stats is True, a tuple of (device_usage, worst_stat), else (None, None). device_usage[dev_id] will equal the number of partitions assigned to that device. worst_stat will equal the number of partitions the worst device is skewed from the number it should have. :raises RingValidationError: problem was found with the ring. """ # "len" showed up in profiling, so it's just computed once. dev_len = len(self.devs) parts_on_devs = sum(d['parts'] for d in self._iter_devs()) if not self._replica2part2dev: raise exceptions.RingValidationError( '_replica2part2dev empty; did you forget to rebalance?') parts_in_map = sum(len(p2d) for p2d in self._replica2part2dev) if parts_on_devs != parts_in_map: raise exceptions.RingValidationError( 'All partitions are not double accounted for: %d != %d' % (parts_on_devs, parts_in_map)) if stats: # dev_usage[dev_id] will equal the number of partitions assigned to # that device. dev_usage = array('I', (0 for _junk in xrange(dev_len))) for part2dev in self._replica2part2dev: for dev_id in part2dev: dev_usage[dev_id] += 1 for part, replica in self._each_part_replica(): dev_id = self._replica2part2dev[replica][part] if dev_id >= dev_len or not self.devs[dev_id]: raise exceptions.RingValidationError( "Partition %d, replica %d was not allocated " "to a device." % (part, replica)) for dev in self._iter_devs(): if not isinstance(dev['port'], int): raise exceptions.RingValidationError( "Device %d has port %r, which is not an integer." % (dev['id'], dev['port'])) if stats: weight_of_one_part = self.weight_of_one_part() worst = 0 for dev in self._iter_devs(): if not dev['weight']: if dev_usage[dev['id']]: # If a device has no weight, but has partitions, then # its overage is considered "infinity" and therefore # always the worst possible. We show MAX_BALANCE for # convenience. worst = MAX_BALANCE break continue skew = abs(100.0 * dev_usage[dev['id']] / (dev['weight'] * weight_of_one_part) - 100.0) if skew > worst: worst = skew return dev_usage, worst return None, None