def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.1', 'port': 10000, 'device': 'sda1'}) rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'}) rb.add_dev({'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10002, 'device': 'sda1'}) rb.rebalance(seed=10) self.assertEqual(rb.dispersion, 39.84375) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 39.84375) # Each node should store 256 partitions to avoid multiple replicas # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [ ['r1z1', 2, '0', '154', '102'], ['r1z1-127.0.0.1:10001', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10001/sda1', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10002', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10002/sda1', 1, '205', '51', '0']] def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } expected = [ ['r1z1', build_tier_report( 2, 256, 39.84375, [0, 0, 154, 102])], ['r1z1-127.0.0.1:10001', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ['r1z1-127.0.0.1:10001/sda1', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ['r1z1-127.0.0.1:10002', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ['r1z1-127.0.0.1:10002/sda1', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ] report = dispersion_report(rb, 'r1z1.*', verbose=True) graph = report['graph'] for i in range(len(expected)): self.assertEqual(expected[i][0], graph[i][0]) self.assertEqual(expected[i][1], graph[i][1]) # overcompensate in r1z0 rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.1', 'port': 10003, 'device': 'sda1'}) rb.rebalance(seed=10) report = dispersion_report(rb) self.assertEqual(rb.dispersion, 40.234375) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.1:10003') self.assertEqual(report['max_dispersion'], 30.078125)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({ 'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1' }) rb.add_dev({ 'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1' }) rb.add_dev({ 'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1' }) rb.add_dev({ 'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1' }) rb.add_dev({ 'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1' }) rb.add_dev({ 'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1' }) rb.add_dev({ 'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1' }) rb.add_dev({ 'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1' }) rb.add_dev({ 'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1' }) rb.add_dev({ 'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1' }) rb.add_dev({ 'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1' }) rb.add_dev({ 'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1' }) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 39.84375) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 39.84375) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # Each node should store 256 partitions to avoid multiple replicas # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [ ['r1z1', build_tier_report(2, 256, 39.84375, [0, 0, 154, 102])], [ 'r1z1-127.0.0.1', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], [ 'r1z1-127.0.0.2', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual((key, report), (expected_key, expected_report)) # overcompensate in r1z0 rb.add_dev({ 'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1' }) rb.add_dev({ 'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1' }) rb.add_dev({ 'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1' }) rb.add_dev({ 'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1' }) # when the biggest tier has the smallest devices things get ugly # can't move all the part-replicas in one rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 9.375) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.1') self.assertEqual(report['max_dispersion'], 7.18562874251497) # do a sencond rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 50.0) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 50.0) # ... but overload can square it rb.set_overload(rb.get_required_overload()) rb.rebalance() self.assertEqual(rb.dispersion, 0.0)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({ 'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1' }) rb.add_dev({ 'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1' }) rb.add_dev({ 'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1' }) rb.add_dev({ 'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1' }) rb.add_dev({ 'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1' }) rb.add_dev({ 'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1' }) rb.add_dev({ 'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1' }) rb.add_dev({ 'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1' }) rb.add_dev({ 'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1' }) rb.add_dev({ 'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1' }) rb.add_dev({ 'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1' }) rb.add_dev({ 'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1' }) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 18.489583333333332) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.1') self.assertEqual(report['max_dispersion'], 22.68370607028754) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # every partition has at least two replicas in this zone, unfortunately # sometimes they're both on the same server. expected = [ [ 'r1z1', build_tier_report(2, 627, 18.341307814992025, [0, 0, 141, 115]) ], [ 'r1z1-127.0.0.1', build_tier_report(1, 313, 22.68370607028754, [14, 171, 71, 0]) ], [ 'r1z1-127.0.0.2', build_tier_report(1, 314, 22.611464968152866, [13, 172, 71, 0]) ], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual((key, report), (expected_key, expected_report)) # overcompensate in r1z0 rb.add_dev({ 'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1' }) rb.add_dev({ 'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1' }) rb.add_dev({ 'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1' }) rb.add_dev({ 'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1' }) # when the biggest tier has the smallest devices things get ugly # can't move all the part-replicas in one rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 3.90625) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.2') self.assertEqual(report['max_dispersion'], 8.152173913043478) # do a sencond rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 16.666666666666668) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 33.333333333333336) # ... but overload can square it rb.set_overload(rb.get_required_overload()) rb.rebalance() self.assertEqual(rb.dispersion, 0.0)
def dispersion(): """ swift-ring-builder <builder_file> dispersion <search_filter> [options] Output report on dispersion. --verbose option will display dispersion graph broken down by tier You can filter which tiers are evaluated to drill down using a regex in the optional search_filter arguemnt. i.e. swift-ring-builder <builder_file> dispersion "r\d+z\d+$" -v ... would only display rows for the zone tiers swift-ring-builder <builder_file> dispersion ".*\-[^/]*$" -v ... would only display rows for the server tiers The reports columns are: Tier : the name of the tier parts : the total number of partitions with assignment in the tier % : the percentage of parts in the tier with replicas over assigned max : maximum replicas a part should have assigned at the tier 0 - N : the number of parts with that many replicas assigned e.g. Tier: parts % max 0 1 2 3 r1z1 1022 79.45 1 2 210 784 28 r1z1 has 1022 total parts assigned, 79% of them have more than the recommend max replica count of 1 assigned. Only 2 parts in the ring are *not* assigned in this tier (0 replica count), 210 parts have the recommend replica count of 1, 784 have 2 replicas, and 28 sadly have all three replicas in this tier. """ status = EXIT_SUCCESS if not builder._replica2part2dev: print('Specified builder file \"%s\" is not rebalanced yet. ' 'Please rebalance first.' % builder_file) exit(EXIT_ERROR) usage = Commands.dispersion.__doc__.strip() parser = optparse.OptionParser(usage) parser.add_option('-v', '--verbose', action='store_true', help='Display dispersion report for tiers') options, args = parser.parse_args(argv) if args[3:]: search_filter = args[3] else: search_filter = None report = dispersion_report(builder, search_filter=search_filter, verbose=options.verbose) print('Dispersion is %.06f, Balance is %.06f, Overload is %0.2f%%' % ( builder.dispersion, builder.get_balance(), builder.overload * 100)) print('Required overload is %.6f%%' % ( builder.get_required_overload() * 100)) if report['worst_tier']: status = EXIT_WARNING print('Worst tier is %.06f (%s)' % (report['max_dispersion'], report['worst_tier'])) if report['graph']: replica_range = range(int(math.ceil(builder.replicas + 1))) part_count_width = '%%%ds' % max(len(str(builder.parts)), 5) replica_counts_tmpl = ' '.join(part_count_width for i in replica_range) tiers = (tier for tier, _junk in report['graph']) tier_width = max(max(map(len, tiers)), 30) header_line = ('%-' + str(tier_width) + 's ' + part_count_width + ' %6s %6s ' + replica_counts_tmpl) % tuple( ['Tier', 'Parts', '%', 'Max'] + replica_range) underline = '-' * len(header_line) print(underline) print(header_line) print(underline) for tier_name, dispersion in report['graph']: replica_counts_repr = replica_counts_tmpl % tuple( dispersion['replicas']) template = ''.join([ '%-', str(tier_width), 's ', part_count_width, ' %6.02f %6d %s', ]) args = ( tier_name, dispersion['placed_parts'], dispersion['dispersion'], dispersion['max_replicas'], replica_counts_repr, ) print(template % args) exit(status)
def dispersion(): """ swift-ring-builder <builder_file> dispersion <search_filter> [options] Output report on dispersion. --verbose option will display dispersion graph broken down by tier You can filter which tiers are evaluated to drill down using a regex in the optional search_filter arguemnt. i.e. swift-ring-builder <builder_file> dispersion "r\d+z\d+$" -v ... would only display rows for the zone tiers swift-ring-builder <builder_file> dispersion ".*\-[^/]*$" -v ... would only display rows for the server tiers The reports columns are: Tier : the name of the tier parts : the total number of partitions with assignment in the tier % : the percentage of parts in the tier with replicas over assigned max : maximum replicas a part should have assigned at the tier 0 - N : the number of parts with that many replicas assigned e.g. Tier: parts % max 0 1 2 3 r1z1 1022 79.45 1 2 210 784 28 r1z1 has 1022 total parts assigned, 79% of them have more than the recommend max replica count of 1 assigned. Only 2 parts in the ring are *not* assigned in this tier (0 replica count), 210 parts have the recommend replica count of 1, 784 have 2 replicas, and 28 sadly have all three replicas in this tier. """ status = EXIT_SUCCESS if not builder._replica2part2dev: print('Specified builder file \"%s\" is not rebalanced yet. ' 'Please rebalance first.' % builder_file) exit(EXIT_ERROR) usage = Commands.dispersion.__doc__.strip() parser = optparse.OptionParser(usage) parser.add_option('-v', '--verbose', action='store_true', help='Display dispersion report for tiers') options, args = parser.parse_args(argv) if args[3:]: search_filter = args[3] else: search_filter = None report = dispersion_report(builder, search_filter=search_filter, verbose=options.verbose) print('Dispersion is %.06f, Balance is %.06f, Overload is %0.2f%%' % (builder.dispersion, builder.get_balance(), builder.overload * 100)) if report['worst_tier']: status = EXIT_WARNING print('Worst tier is %.06f (%s)' % (report['max_dispersion'], report['worst_tier'])) if report['graph']: replica_range = range(int(math.ceil(builder.replicas + 1))) part_count_width = '%%%ds' % max(len(str(builder.parts)), 5) replica_counts_tmpl = ' '.join(part_count_width for i in replica_range) tiers = (tier for tier, _junk in report['graph']) tier_width = max(max(map(len, tiers)), 30) header_line = ('%-' + str(tier_width) + 's ' + part_count_width + ' %6s %6s ' + replica_counts_tmpl) % tuple( ['Tier', 'Parts', '%', 'Max'] + replica_range) underline = '-' * len(header_line) print(underline) print(header_line) print(underline) for tier_name, dispersion in report['graph']: replica_counts_repr = replica_counts_tmpl % tuple( dispersion['replicas']) template = ''.join([ '%-', str(tier_width), 's ', part_count_width, ' %6.02f %6d %s', ]) args = ( tier_name, dispersion['placed_parts'], dispersion['dispersion'], dispersion['max_replicas'], replica_counts_repr, ) print(template % args) exit(status)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1'}) rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1'}) rb.add_dev({'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1'}) rb.add_dev({'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1'}) rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'}) rb.add_dev({'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1'}) rb.add_dev({'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1'}) rb.add_dev({'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1'}) rb.add_dev({'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1'}) rb.add_dev({'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1'}) rb.add_dev({'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1'}) rb.add_dev({'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1'}) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 39.84375) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 39.84375) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # Each node should store 256 partitions to avoid multiple replicas # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [ ['r1z1', build_tier_report( 2, 256, 39.84375, [0, 0, 154, 102])], ['r1z1-127.0.0.1', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ['r1z1-127.0.0.2', build_tier_report( 1, 256, 19.921875, [0, 205, 51, 0])], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual( (key, report), (expected_key, expected_report) ) # overcompensate in r1z0 rb.add_dev({'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1'}) rb.add_dev({'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1'}) rb.add_dev({'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1'}) rb.add_dev({'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1'}) # when the biggest tier has the smallest devices things get ugly rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 70.3125) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 88.23529411764706) # ... but overload can square it rb.set_overload(rb.get_required_overload()) rb.rebalance() self.assertEqual(rb.dispersion, 0.0)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1'}) rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1'}) rb.add_dev({'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1'}) rb.add_dev({'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1'}) rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'}) rb.add_dev({'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1'}) rb.add_dev({'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1'}) rb.add_dev({'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1'}) rb.add_dev({'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1'}) rb.add_dev({'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1'}) rb.add_dev({'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1'}) rb.add_dev({'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1'}) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 39.0625) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 39.0625) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # Each node should store 256 partitions to avoid multiple replicas # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [ ['r1z1', build_tier_report( 2, 256, 39.0625, [0, 0, 156, 100])], ['r1z1-127.0.0.1', build_tier_report( 1, 256, 19.53125, [0, 206, 50, 0])], ['r1z1-127.0.0.2', build_tier_report( 1, 256, 19.53125, [0, 206, 50, 0])], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual( (key, report), (expected_key, expected_report) ) # overcompensate in r1z0 rb.add_dev({'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1'}) rb.add_dev({'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1'}) rb.add_dev({'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1'}) rb.add_dev({'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1'}) rb.rebalance(seed=10) report = dispersion_report(rb) self.assertEqual(rb.dispersion, 44.53125) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 32.520325203252035)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({ 'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.1', 'port': 10000, 'device': 'sda1' }) rb.add_dev({ 'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1' }) rb.add_dev({ 'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10002, 'device': 'sda1' }) rb.rebalance(seed=10) self.assertEqual(rb.dispersion, 39.84375) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 39.84375) # Each node should store 256 partitions to avoid multiple replicas # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [['r1z1', 2, '0', '154', '102'], ['r1z1-127.0.0.1:10001', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10001/sda1', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10002', 1, '205', '51', '0'], ['r1z1-127.0.0.1:10002/sda1', 1, '205', '51', '0']] def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } expected = [ ['r1z1', build_tier_report(2, 256, 39.84375, [0, 0, 154, 102])], [ 'r1z1-127.0.0.1:10001', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], [ 'r1z1-127.0.0.1:10001/sda1', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], [ 'r1z1-127.0.0.1:10002', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], [ 'r1z1-127.0.0.1:10002/sda1', build_tier_report(1, 256, 19.921875, [0, 205, 51, 0]) ], ] report = dispersion_report(rb, 'r1z1.*', verbose=True) graph = report['graph'] for i in range(len(expected)): self.assertEqual(expected[i][0], graph[i][0]) self.assertEqual(expected[i][1], graph[i][1]) # overcompensate in r1z0 rb.add_dev({ 'id': 3, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.1', 'port': 10003, 'device': 'sda1' }) rb.rebalance(seed=10) report = dispersion_report(rb) self.assertEqual(rb.dispersion, 40.234375) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.1:10003') self.assertEqual(report['max_dispersion'], 30.078125)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1'}) rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1'}) rb.add_dev({'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1'}) rb.add_dev({'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1'}) rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'}) rb.add_dev({'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1'}) rb.add_dev({'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1'}) rb.add_dev({'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1'}) rb.add_dev({'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1'}) rb.add_dev({'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1'}) rb.add_dev({'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1'}) rb.add_dev({'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1'}) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 55.46875) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1') self.assertEqual(report['max_dispersion'], 44.921875) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # Each node should store less than or equal to 256 partitions to # avoid multiple replicas. # 2/5 of total weight * 768 ~= 307 -> 51 partitions on each node in # zone 1 are stored at least twice on the nodes expected = [ ['r1z1', build_tier_report( 2, 256, 44.921875, [0, 0, 141, 115])], ['r1z1-127.0.0.1', build_tier_report( 1, 242, 29.33884297520661, [14, 171, 71, 0])], ['r1z1-127.0.0.2', build_tier_report( 1, 243, 29.218106995884774, [13, 172, 71, 0])], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual( (key, report), (expected_key, expected_report) ) # overcompensate in r1z0 rb.add_dev({'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1'}) rb.add_dev({'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1'}) rb.add_dev({'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1'}) rb.add_dev({'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1'}) # when the biggest tier has the smallest devices things get ugly # can't move all the part-replicas in one rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 11.71875) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.2') self.assertEqual(report['max_dispersion'], 8.875739644970414) # do a sencond rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 50.0) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 50.0) # ... but overload can square it rb.set_overload(rb.get_required_overload()) rb.rebalance() self.assertEqual(rb.dispersion, 0.0)
def test_dispersion_report(self): rb = ring.RingBuilder(8, 3, 0) rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sda1'}) rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdb1'}) rb.add_dev({'id': 4, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdc1'}) rb.add_dev({'id': 5, 'region': 1, 'zone': 0, 'weight': 100, 'ip': '127.0.0.0', 'port': 10000, 'device': 'sdd1'}) rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'}) rb.add_dev({'id': 6, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1'}) rb.add_dev({'id': 7, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdc1'}) rb.add_dev({'id': 8, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.1', 'port': 10001, 'device': 'sdd1'}) rb.add_dev({'id': 2, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sda1'}) rb.add_dev({'id': 9, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdb1'}) rb.add_dev({'id': 10, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdc1'}) rb.add_dev({'id': 11, 'region': 1, 'zone': 1, 'weight': 200, 'ip': '127.0.0.2', 'port': 10002, 'device': 'sdd1'}) # this ring is pretty volatile and the assertions are pretty brittle # so we use a specific seed rb.rebalance(seed=100) rb.validate() self.assertEqual(rb.dispersion, 18.489583333333332) report = dispersion_report(rb) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.1') self.assertEqual(report['max_dispersion'], 22.68370607028754) def build_tier_report(max_replicas, placed_parts, dispersion, replicas): return { 'max_replicas': max_replicas, 'placed_parts': placed_parts, 'dispersion': dispersion, 'replicas': replicas, } # every partition has at least two replicas in this zone, unfortunately # sometimes they're both on the same server. expected = [ ['r1z1', build_tier_report( 2, 627, 18.341307814992025, [0, 0, 141, 115])], ['r1z1-127.0.0.1', build_tier_report( 1, 313, 22.68370607028754, [14, 171, 71, 0])], ['r1z1-127.0.0.2', build_tier_report( 1, 314, 22.611464968152866, [13, 172, 71, 0])], ] report = dispersion_report(rb, 'r1z1[^/]*$', verbose=True) graph = report['graph'] for i, (expected_key, expected_report) in enumerate(expected): key, report = graph[i] self.assertEqual( (key, report), (expected_key, expected_report) ) # overcompensate in r1z0 rb.add_dev({'id': 12, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sda1'}) rb.add_dev({'id': 13, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdb1'}) rb.add_dev({'id': 14, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdc1'}) rb.add_dev({'id': 15, 'region': 1, 'zone': 0, 'weight': 500, 'ip': '127.0.0.3', 'port': 10003, 'device': 'sdd1'}) # when the biggest tier has the smallest devices things get ugly # can't move all the part-replicas in one rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 3.90625) self.assertEqual(report['worst_tier'], 'r1z1-127.0.0.2') self.assertEqual(report['max_dispersion'], 8.152173913043478) # do a sencond rebalance rb.rebalance(seed=100) report = dispersion_report(rb, verbose=True) self.assertEqual(rb.dispersion, 16.666666666666668) self.assertEqual(report['worst_tier'], 'r1z0-127.0.0.3') self.assertEqual(report['max_dispersion'], 33.333333333333336) # ... but overload can square it rb.set_overload(rb.get_required_overload()) rb.rebalance() self.assertEqual(rb.dispersion, 0.0)