def _send_commit_request(self, retry_delay=None, attempt=None): """Send a commit request with our last_processed_offset""" # If there's a _commit_call, and it's not active, clear it, it probably # just called us... if self._commit_call and not self._commit_call.active(): self._commit_call = None # Make sure we only have one outstanding commit request at a time if self._commit_req is not None: raise OperationInProgress(self._commit_req) # Handle defaults if retry_delay is None: retry_delay = self.retry_init_delay if attempt is None: attempt = 1 # Create new OffsetCommitRequest with the latest processed offset commit_offset = self._last_processed_offset commit_request = OffsetCommitRequest( self.topic, self.partition, commit_offset, TIMESTAMP_INVALID, self.commit_metadata) log.debug("Committing off=%d grp=%s tpc=%s part=%s req=%r", self._last_processed_offset, self.consumer_group, self.topic, self.partition, commit_request) # Send the request, add our callbacks self._commit_req = d = self.client.send_offset_commit_request( self.consumer_group, [commit_request]) d.addBoth(self._clear_commit_req) d.addCallbacks( self._update_committed_offset, self._handle_commit_error, callbackArgs=(commit_offset,), errbackArgs=(retry_delay, attempt))
def commit(self): """ Commit the offset of the message we last processed if it is different from what we believe is the last offset committed to Kafka. .. note:: It is possible to commit a smaller offset than Kafka has stored. This is by design, so we can reprocess a Kafka message stream if desired. On error, will retry according to :attr:`request_retry_max_attempts` (by default, forever). If called while a commit operation is in progress, and new messages have been processed since the last request was sent then the commit will fail with :exc:`OperationInProgress`. The :exc:`OperationInProgress` exception wraps a :class:`~twisted.internet.defer.Deferred` which fires when the outstanding commit operation completes. :returns: A :class:`~twisted.internet.defer.Deferred` which resolves with the committed offset when the operation has completed. It will resolve immediately if the current offset and the last committed offset do not differ. """ # Can't commit without a consumer_group if not self.consumer_group: return fail(Failure(InvalidConsumerGroupError( "Bad Group_id:{0!r}".format(self.consumer_group)))) # short circuit if we are 'up to date', or haven't processed anything if ((self._last_processed_offset is None) or (self._last_processed_offset == self._last_committed_offset)): return succeed(self._last_committed_offset) # If we're currently processing a commit we return a failure # with a deferred we'll fire when the in-progress one completes if self._commit_ds: d = Deferred() self._commit_ds.append(d) return fail(OperationInProgress(d)) # Ok, we have processed messages since our last commit attempt, and # we're not currently waiting on a commit request to complete: # Start a new one d = Deferred() self._commit_ds.append(d) # Send the request self._send_commit_request() # Reset the commit_looper here, rather than on success to give # more stability to the commit interval. if self._commit_looper is not None: self._commit_looper.reset() # return the deferred return d