Esempio n. 1
0
    def __init__(self, node_config=None, replicas=1):
        self.replicas = replicas
        self.ring = AVLTree()

        if node_config:
            with open(node_config, 'r') as nodes:
                for node in nodes.readlines():
                    node = node.strip()
                    self.add(node)
Esempio n. 2
0
    def test_avl_is_balancing_correctly(self):
        """
        Test that it is balancing correctly
        """
        avl = AVLTree()

        point = VirtualPoint("10.128.20.1", 20)
        avl.insert(20, point)

        point = VirtualPoint("10.128.20.2", 4)
        avl.insert(4, point)

        point = VirtualPoint("10.128.20.3", 3)
        avl.insert(3, point)

        point = VirtualPoint("10.128.20.4", 9)
        avl.insert(9, point)

        point = VirtualPoint("10.128.20.5", 10)
        avl.insert(10, point)

        point = VirtualPoint("10.128.20.6", 15)
        avl.insert(15, point)

        bal = avl.is_balanced()

        self.assertEqual(bal, True)
Esempio n. 3
0
    def test_avl_postorder_traversal(self):
        """
        Test that it traverses tree in post-order correctly
        """

        avl = AVLTree()

        point = VirtualPoint("10.128.20.1", 20)
        avl.insert(20, point)

        point = VirtualPoint("10.128.20.2", 4)
        avl.insert(4, point)

        point = VirtualPoint("10.128.20.3", 3)
        avl.insert(3, point)

        point = VirtualPoint("10.128.20.4", 9)
        avl.insert(9, point)

        point = VirtualPoint("10.128.20.5", 10)
        avl.insert(10, point)

        point = VirtualPoint("10.128.20.6", 15)
        avl.insert(15, point)

        keys = avl.postorder_traverse()

        # Expected order of keys when post-order traversal applied
        expected_key_order = [3, 9, 4, 15, 20, 10]
        self.assertEqual(keys, expected_key_order)
Esempio n. 4
0
    def test_avl_is_removing_and_balancing_correctly(self):
        """
        Test that it is balancing correctly after removal of a node
        """
        avl = AVLTree()

        point = VirtualPoint("10.128.20.1", 20)
        avl.insert(20, point)

        point = VirtualPoint("10.128.20.2", 4)
        avl.insert(4, point)

        point = VirtualPoint("10.128.20.3", 3)
        avl.insert(3, point)

        point = VirtualPoint("10.128.20.4", 9)
        avl.insert(9, point)

        point = VirtualPoint("10.128.20.5", 10)
        avl.insert(10, point)

        point = VirtualPoint("10.128.20.6", 15)
        avl.insert(15, point)

        avl.remove(20)  # Will cause tree to rebalance

        bal = avl.is_balanced()

        self.assertEqual(bal, True)
Esempio n. 5
0
class Ring():
    """
    The `Ring` object represents the consistent hashing ring.

    The `node_config` parameter defaults to `None`, however it
    should be provided by the `Cache` object

    The `replicas` parameter adds replicas for a virtual point
    in the tree. This defaults to `1` and it is not recommended
    to change this.
    """
    def __init__(self, node_config=None, replicas=1):
        self.replicas = replicas
        self.ring = AVLTree()

        if node_config:
            with open(node_config, 'r') as nodes:
                for node in nodes.readlines():
                    node = node.strip()
                    self.add(node)

    def __len__(self):
        return len(self.ring.get_nodes())

    def __str__(self):
        return self.ring.__str__()

    def add(self, node):
        """
        The `add()` method adds a GhostDB node to the
        consistent hashing ring.
        """

        for i in range(self.replicas):
            key = self.key_hash(node, i)
            vp = VirtualPoint(node, key)
            self.ring.insert(key, vp)

    def delete(self, node):
        """
        The `delete()` method removes a GhostDB node from the
        consistent hashing ring. This is typically performed
        if the GhostDB node is unreachable.
        """

        for i in range(self.replicas):
            key = self.key_hash(node, i)
            self.ring.remove(key)

    def get_point_for(self, key):
        """
        The `get_point_for()` method returns the correct 
        GhostDB node to send a request to for a given key.
        """

        if len(self) == 0:
            return None
        key = self.key_hash(key)
        node_key, node_value = self.ring.next_gte_pair(key)
        if not node_value:
            node_key, node_value = self.ring.minimum_pair()

        return node_value

    def get_points(self):
        """
        The `get_points()` method returns all GhostDB nodes
        in the consistent hashing ring.
        """

        return self.ring.get_nodes()

    def key_hash(self, key, index=None):
        """
        The `key_hash()` method generates and returns 
        the unsigned CRC32 hash for a provided key in 
        hexidecimal form.
        """

        if index:
            key = "{:s}:{:d}".format(key, index)
        s = binascii.crc32(bytes(key, 'utf-8'))
        return hex(s)