Example #1
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.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)
Example #2
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.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)
Example #3
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)
Example #4
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)
Example #5
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))
        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)
Example #6
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.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)
Example #7
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)
Example #8
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.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)
Example #9
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, 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)
Example #10
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)