class HashRing: # NodeDict (sorted by key): int -> string __node_target_dict = None # dict: string -> list(int) __target_nodes_dict = None __change_event = None __check_func = None __invalid_target_set = None __timer = None __builded = None __destroyed = None def __init__(self, check_func, shadow_number=1000): self.__change_event = threading.Event() self.__change_event.set() self.__builded = threading.Event() self.__destroyed = threading.Event() self.build(check_func, shadow_number) def build(self, check_func, shadow_number=1000): if self.__builded.isSet(): get_logger().info("Please destroy hash ring before rebuilding.") return self.__change_event.wait() self.__change_event.clear() try: self.__builded.set() self.__check_func = check_func if shadow_number > 0: self._shadow_number = shadow_number else: self._shadow_number = 1000 self.__node_target_dict = NodeDict() self.__target_nodes_dict = {} self.start_checking() self.__destroyed.clear() get_logger().info("The hash ring is builded.") finally: self.__change_event.set() def destroy(self): if self.__destroyed.isSet(): get_logger().info("The hash ring has been destroyed. IGNORE the destroy operation.") return self.__change_event.wait() self.__change_event.clear() try: self.__destroyed.set() self.stop_checking() self.__node_target_dict = None self.__target_nodes_dict = None self.__builded.clear() get_logger().info("The hash ring is destroyed.") finally: self.__change_event.set() def start_checking(self): if not self.__check_func: get_logger().info("The check function is None. IGNORE the checking operation.") else: if not self.__timer: self.__invalid_target_set = set([]) self.__timer = RepeatTimer(2, self.check) get_logger().info("Starting node checking timer...") self.__timer.start() def check(self): if not self.__check_func: get_logger().debug("The check function is None. IGNORE checking.") return get_logger().debug("Checking nodes in hash ring...") self.__change_event.wait() targets = self.__target_nodes_dict.keys() for t in targets: result = self.__check_func(t) if not result: get_logger().info("The target '{0}' is INVALID. Delete it from hash ring.".format(t)) self.__remove_target(t) self.__invalid_target_set = self.__invalid_target_set | set([t]) get_logger().debug("Checking the nodes which have been added but invalid...") for it in self.__invalid_target_set: result = self.__check_func(it) if result: get_logger().info("The invalid target '{0}' is OK. Add it to hash ring.".format(it)) self.__add_target(it) self.__invalid_target_set = self.__invalid_target_set - set([it]) def stop_checking(self): if self.__timer: self.__timer.cancel() self.__timer = None def __add_target(self, target): self.__change_event.wait() self.__change_event.clear() try: target_shadows = [] target_nodes = [] current_node_target_dict = {} for i in range(0, self._shadow_number): target_shadows.append("%s-%s" % (target, i)) for target_shadow in target_shadows: numbers = get_ketama_numbers(target_shadow) for number in numbers: current_node_target_dict[number] = target target_nodes.append(number) self.__target_nodes_dict[target] = target_nodes self.__node_target_dict.update(current_node_target_dict) get_logger().info("The target '{0}' is added successfully. The number of node: {1}.".format(target, len(target_nodes))) get_logger().debug("The nodes of target '{0}' is {1}.".format(target, target_nodes)) finally: self.__change_event.set() def __remove_target(self, target): self.__change_event.wait() self.__change_event.clear() try: if self.__node_target_dict: invalid_node_target_dict = {k:v for k, v in self.__node_target_dict.items() if v == target} self.__node_target_dict.delete(invalid_node_target_dict) if self.__target_nodes_dict: if target in self.__target_nodes_dict: del self.__target_nodes_dict[target] get_logger().info("The target '{0}' is removed successfully.".format(target)) finally: self.__change_event.set() def parse_targets(self, targets): target_set = None if is_seq(targets): target_set = set(targets) elif isinstance(targets, str): target_set = set([targets]) else: raise HashRingError("The type of parameter 'targets' is UNSUPPORTED! %s" % type(targets)) return target_set def add_targets(self, targets): target_set = self.parse_targets(targets) for target in target_set: self.__add_target(target) def remove_targets(self, targets): target_set = self.parse_targets(targets) for target in target_set: self.__remove_target(target) def get_target(self, value): results = self.get_targets(value, 1) if len(results) == 0: return None else: return results[0] def get_targets(self, value, number): results = [] if not value: return results if number <= 0: number = 1 self.__change_event.wait() target_number = len(self.__target_nodes_dict) if number > target_number: number = target_number h = get_hash_for_key(str(value)) currentHash = h while len(results) < number: node = self.__node_target_dict.to_node(currentHash) if node in self.__node_target_dict: target = self.__node_target_dict[node] if target not in results: results.append(target) currentHash = node + 1 get_logger().debug("The targets of key '{0}' ({1}) is {2}.".format(value, h, results)) return results