Consistent Hashing の実装

最近、Consisten Hashing について調べている。 日本語で解説してくれているページはいくつもあって、それが言っていることはわかるのだけれど、やっぱりコードを読んでみないとちゃんとわかっているかわからない。

そこで、Github を漁っていたら、簡単なNode.js 実装を発見した。 それを読んでスッキリ理解できたのだが、自分で実装してみないことには落ち着かない。 そこで、この Node.js の Consistent Hashing 実装を Python に移植してみた。

#-*- coding: utf-8 -*-

import math
from hashlib import sha1


class ConsistentHashing(object):

    def __init__(self, nodes):
        self.replicas = 160
        self.ring = {}
        self.keys = []
        self.nodes = []

        for node in nodes:
            self.add_node(node)

    def add_node(self, node):
        self.nodes.append(node)

        for x in range(self.replicas):
            key = sha1(u'%s:%d' % (node, x)).hexdigest()

            self.keys.append(key)
            self.ring[key] = node

        self.keys.sort()

    def remove_node(self, node):
        while node in self.nodes:
            self.nodes.remove(node)

        for x in range(self.replicas):
            key = sha1(u'%s:%d' % (node, x)).hexdigest()
            del self.ring[key]

            while key in self.keys:
                self.keys.remove(key)

    def get_node(self, key):
        if len(self.ring) is 0:
            return 0

        key_hash = sha1(key).hexdigest()
        position = self.get_node_position(key_hash)

        return self.ring[self.keys[position]]

    def get_node_position(self, key_hash):
        upper = len(self.ring) - 1
        lower = 0
        index = 0
        compare = 0

        if upper is 0:
            return 0

        while lower <= upper:
            index = int(math.floor((lower + upper) / 2))
            compare = self.compare(self.keys[index], key_hash)

            if compare is 0:
                return index
            elif compare is 1:
                upper = index - 1
            else:
                lower = index + 1

        if upper < 0:
            upper = len(self.ring) - 1

        return upper

    def compare(self, v1, v2):
     return 1 if v1 > v2 else -1 if v1 < v2 else 0