def assign_from_user(self, partitions): """Manually assign a list of TopicPartitions to this consumer. This interface does not allow for incremental assignment and will replace the previous assignment (if there was one). Manual topic assignment through this method does not use the consumer's group management functionality. As such, there will be no rebalance operation triggered when group membership or cluster and topic metadata change. Note that it is not possible to use both manual partition assignment with assign() and group assignment with subscribe(). Arguments: partitions (list of TopicPartition): assignment for this instance. Raises: IllegalStateError: if consumer has already called subscribe() """ if self.subscription is not None: raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) self._user_assignment.clear() self._user_assignment.update(partitions) for partition in partitions: if partition not in self.assignment: self._add_assigned_partition(partition) for tp in set(self.assignment.keys()) - self._user_assignment: del self.assignment[tp] self.needs_partition_assignment = False
def change_subscription(self, topics): """Change the topic subscription. Arguments: topics (list of str): topics for subscription Raises: IllegalStateErrror: if assign_from_user has been used already """ if self._user_assignment: raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) if self.subscription == set(topics): log.warning("subscription unchanged by change_subscription(%s)", topics) return log.info('Updating subscribed topics to: %s', topics) self.subscription = set(topics) self._group_subscription.update(topics) self.needs_partition_assignment = True # Remove any assigned partitions which are no longer subscribed to for tp in set(self.assignment.keys()): if tp.topic not in self.subscription: del self.assignment[tp]
def group_subscribe(self, topics): """Add topics to the current group subscription. This is used by the group leader to ensure that it receives metadata updates for all topics that any member of the group is subscribed to. Arguments: topics (list of str): topics to add to the group subscription """ if self._user_assignment: raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) self._group_subscription.update(topics)
def subscribe(self, topics=(), pattern=None, listener=None): """Subscribe to a list of topics, or a topic regex pattern. Partitions will be dynamically assigned via a group coordinator. Topic subscriptions are not incremental: this list will replace the current assignment (if there is one). This method is incompatible with assign_from_user() Arguments: topics (list): List of topics for subscription. pattern (str): Pattern to match available topics. You must provide either topics or pattern, but not both. listener (ConsumerRebalanceListener): Optionally include listener callback, which will be called before and after each rebalance operation. As part of group management, the consumer will keep track of the list of consumers that belong to a particular group and will trigger a rebalance operation if one of the following events trigger: * Number of partitions change for any of the subscribed topics * Topic is created or deleted * An existing member of the consumer group dies * A new member is added to the consumer group When any of these events are triggered, the provided listener will be invoked first to indicate that the consumer's assignment has been revoked, and then again when the new assignment has been received. Note that this listener will immediately override any listener set in a previous call to subscribe. It is guaranteed, however, that the partitions revoked/assigned through this interface are from topics subscribed in this call. """ if self._user_assignment or (topics and pattern): raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) assert topics or pattern, 'Must provide topics or pattern' if pattern: log.info('Subscribing to pattern: /%s/', pattern) self.subscription = set() self.subscribed_pattern = re.compile(pattern) else: self.change_subscription(topics) if listener and not isinstance(listener, ConsumerRebalanceListener): raise TypeError('listener must be a ConsumerRebalanceListener') self.listener = listener
def assign_from_subscribed(self, assignments): """Update the assignment to the specified partitions This method is called by the coordinator to dynamically assign partitions based on the consumer's topic subscription. This is different from assign_from_user() which directly sets the assignment from a user-supplied TopicPartition list. Arguments: assignments (list of TopicPartition): partitions to assign to this consumer instance. """ if self.subscription is None: raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) for tp in assignments: if tp.topic not in self.subscription: raise ValueError( "Assigned partition %s for non-subscribed topic." % tp) self.assignment.clear() for tp in assignments: self._add_assigned_partition(tp) self.needs_partition_assignment = False log.info("Updated partition assignment: %s", assignments)
def mark_for_reassignment(self): if self._user_assignment: raise IllegalStateError(self._SUBSCRIPTION_EXCEPTION_MESSAGE) assert self.subscription is not None, 'Subscription required' self._group_subscription.intersection_update(self.subscription) self.needs_partition_assignment = True