Topic callback registry.

        callback func should receive two args: topic and pk, and then process
        the replication job.

        Note: The callback func must return True/False. When passed a list of
        pks, the func should return a list of True/False with the same length
        of pks.

        :param topics: a list of topics
        :param workers: how many workers to process this topic
        :param multi: whether pass multiple pks
        :param queue_limit: when queue size is larger than the limit,
         the worker should run deduplicate procedure

def event(self, *topics, **kwargs):
        """Topic callback registry.

        callback func should receive two args: topic and pk, and then process
        the replication job.

        Note: The callback func must return True/False. When passed a list of
        pks, the func should return a list of True/False with the same length
        of pks.

        :param topics: a list of topics
        :param workers: how many workers to process this topic
        :param multi: whether pass multiple pks
        :param queue_limit: when queue size is larger than the limit,
         the worker should run deduplicate procedure
        """
        workers = kwargs.pop("workers", 1)
        multi = kwargs.pop("multi", False)
        queue_limit = kwargs.pop("queue_limit", 10000)

        def wrapper(func):
            for topic in topics:
                queues = [Queue() for _ in range(workers)]
                hash_ring = ketama.Continuum()
                for q in queues:
                    hash_ring[str(hash(q))] = q
                self.worker_queues[topic] = hash_ring
                self.workers[topic] = WorkerPool(
                    queues, topic, func, multi=multi, queue_limit=queue_limit,
                    logger_name="%s.%s" % (self.name, topic))
                self.socket.setsockopt(zmq.SUBSCRIBE, asbytes(topic))
            return func
        return wrapper