Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1047,25 +1047,7 @@ public Map<TopicPartition, Long> endOffsets(Collection<TopicPartition> partition
public OptionalLong currentLag(TopicPartition topicPartition) {
acquireAndEnsureOpen();
try {
final Long lag = subscriptions.partitionLag(topicPartition, isolationLevel);

// if the log end offset is not known and hence cannot return lag and there is
// no in-flight list offset requested yet,
// issue a list offset request for that partition so that next time
// we may get the answer; we do not need to wait for the return value
// since we would not try to poll the network client synchronously
if (lag == null) {
if (subscriptions.partitionEndOffset(topicPartition, isolationLevel) == null &&
!subscriptions.partitionEndOffsetRequested(topicPartition)) {
log.info("Requesting the log end offset for {} in order to compute lag", topicPartition);
subscriptions.requestPartitionEndOffset(topicPartition);
offsetFetcher.endOffsets(Collections.singleton(topicPartition), time.timer(0L));
}

return OptionalLong.empty();
}

return OptionalLong.of(lag);
return offsetFetcher.currentLag(topicPartition);
} finally {
release();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
Expand Down Expand Up @@ -126,7 +127,7 @@ public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartitio

try {
Map<TopicPartition, ListOffsetData> fetchedOffsets = fetchOffsetsByTimes(timestampsToSearch,
timer, true).fetchedOffsets;
timer, true, false).fetchedOffsets;

return buildOffsetsForTimesResult(timestampsToSearch, fetchedOffsets);
} finally {
Expand All @@ -136,7 +137,8 @@ public Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartitio

private ListOffsetResult fetchOffsetsByTimes(Map<TopicPartition, Long> timestampsToSearch,
Timer timer,
boolean requireTimestamps) {
boolean requireTimestamps,
boolean updatePartitionEndOffsetsFlag) {
ListOffsetResult result = new ListOffsetResult();
if (timestampsToSearch.isEmpty())
return result;
Expand All @@ -153,11 +155,17 @@ public void onSuccess(ListOffsetResult value) {
remainingToSearch.keySet().retainAll(value.partitionsToRetry);

offsetFetcherUtils.updateSubscriptionState(value.fetchedOffsets, isolationLevel);

if (updatePartitionEndOffsetsFlag)
offsetFetcherUtils.clearPartitionEndOffsetRequests(remainingToSearch.keySet());
}
}

@Override
public void onFailure(RuntimeException e) {
if (updatePartitionEndOffsetsFlag)
offsetFetcherUtils.clearPartitionEndOffsetRequests(remainingToSearch.keySet());

if (!(e instanceof RetriableException)) {
throw future.exception();
}
Expand Down Expand Up @@ -185,23 +193,49 @@ public void onFailure(RuntimeException e) {
}

public Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions, Timer timer) {
return beginningOrEndOffset(partitions, ListOffsetsRequest.EARLIEST_TIMESTAMP, timer);
return beginningOrEndOffset(partitions, ListOffsetsRequest.EARLIEST_TIMESTAMP, timer, false);
}

public Map<TopicPartition, Long> endOffsets(Collection<TopicPartition> partitions, Timer timer) {
return beginningOrEndOffset(partitions, ListOffsetsRequest.LATEST_TIMESTAMP, timer);
return beginningOrEndOffset(partitions, ListOffsetsRequest.LATEST_TIMESTAMP, timer, false);
}

public OptionalLong currentLag(TopicPartition topicPartition) {
final Long lag = subscriptions.partitionLag(topicPartition, isolationLevel);

// if the log end offset is not known and hence cannot return lag and there is
// no in-flight list offset requested yet,
// issue a list offset request for that partition so that next time
// we may get the answer; we do not need to wait for the return value
// since we would not try to poll the network client synchronously
if (lag == null) {
if (subscriptions.partitionEndOffset(topicPartition, isolationLevel) == null &&
offsetFetcherUtils.maybeSetPartitionEndOffsetRequest(topicPartition)) {
beginningOrEndOffset(
Set.of(topicPartition),
ListOffsetsRequest.LATEST_TIMESTAMP,
time.timer(0L),
true
);
}

return OptionalLong.empty();
}

return OptionalLong.of(lag);
}

private Map<TopicPartition, Long> beginningOrEndOffset(Collection<TopicPartition> partitions,
long timestamp,
Timer timer) {
Timer timer,
boolean updatePartitionEndOffsetsFlag) {
metadata.addTransientTopics(topicsForPartitions(partitions));
try {
Map<TopicPartition, Long> timestampsToSearch = partitions.stream()
.distinct()
.collect(Collectors.toMap(Function.identity(), tp -> timestamp));

ListOffsetResult result = fetchOffsetsByTimes(timestampsToSearch, timer, false);
ListOffsetResult result = fetchOffsetsByTimes(timestampsToSearch, timer, false, updatePartitionEndOffsetsFlag);

return result.fetchedOffsets.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().offset));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,47 @@ void updateSubscriptionState(Map<TopicPartition, OffsetFetcherUtils.ListOffsetDa
log.trace("Updating high watermark for partition {} to {}", partition, offset);
subscriptionState.updateHighWatermark(partition, offset);
}
} else {
if (isolationLevel == IsolationLevel.READ_COMMITTED) {
log.warn("Not updating last stable offset for partition {} as it is no longer assigned", partition);
} else {
log.warn("Not updating high watermark for partition {} as it is no longer assigned", partition);
}
}
}
}

/**
* The {@code LIST_OFFSETS} lag lookup is serialized, so if there's an inflight request it must finish before
* another request can be issued. This serialization mechanism is controlled by the 'end offset requested'
* flag in {@link SubscriptionState}.
*
* @return {@code true} if the partition's end offset can be requested, {@code false} if there's already an
* in-flight request
*/
boolean maybeSetPartitionEndOffsetRequest(TopicPartition partition) {
if (subscriptionState.partitionEndOffsetRequested(partition)) {
log.info("Not requesting the log end offset for {} to compute lag as an outstanding request already exists", partition);
return false;
} else {
log.info("Requesting the log end offset for {} in order to compute lag", partition);
subscriptionState.requestPartitionEndOffset(partition);
return true;
}
}

/**
* If any of the given partitions are assigned, this will clear the partition's 'end offset requested' flag so
* that the next attempt to look up the lag will properly issue another <code>LIST_OFFSETS</code> request. This
* is only intended to be called when <code>LIST_OFFSETS</code> fails. Successful <code>LIST_OFFSETS</code> calls
* should use {@link #updateSubscriptionState(Map, IsolationLevel)}.
*
* @param partitions Partitions for which the 'end offset requested' flag should be cleared (if still assigned)
*/
void clearPartitionEndOffsetRequests(Collection<TopicPartition> partitions) {
for (final TopicPartition partition : partitions) {
if (subscriptionState.maybeClearPartitionEndOffsetRequested(partition)) {
log.trace("Clearing end offset requested for partition {}", partition);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,17 @@ public synchronized boolean partitionEndOffsetRequested(TopicPartition tp) {
return topicPartitionState.endOffsetRequested();
}

public synchronized boolean maybeClearPartitionEndOffsetRequested(TopicPartition tp) {
TopicPartitionState topicPartitionState = assignedStateOrNull(tp);

if (topicPartitionState != null && topicPartitionState.endOffsetRequested()) {
topicPartitionState.clearEndOffset();
return true;
} else {
return false;
}
}

synchronized Long partitionLead(TopicPartition tp) {
TopicPartitionState topicPartitionState = assignedState(tp);
return topicPartitionState.logStartOffset == null ? null : topicPartitionState.position.offset - topicPartitionState.logStartOffset;
Expand Down Expand Up @@ -982,6 +993,10 @@ public void requestEndOffset() {
endOffsetRequested = true;
}

public void clearEndOffset() {
endOffsetRequested = false;
}

private void transitionState(FetchState newState, Runnable runIfTransitioned) {
FetchState nextState = this.fetchState.transitionTo(newState);
if (nextState.equals(newState)) {
Expand Down
Loading
Loading