-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Narrow down the scope of waiting for pending tasks to per partition #191
Changes from 2 commits
c4bbbc1
fe7f2be
90bc937
24ebdef
b55869f
aa59181
5659ec7
929bcc5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -28,7 +28,10 @@ | |||
import java.util.Map.Entry; | ||||
import java.util.Set; | ||||
import java.util.concurrent.atomic.AtomicBoolean; | ||||
import java.util.concurrent.atomic.AtomicInteger; | ||||
import java.util.concurrent.locks.ReentrantLock; | ||||
import java.util.function.Function; | ||||
import java.util.stream.Collectors; | ||||
|
||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata; | ||||
import org.apache.kafka.common.TopicPartition; | ||||
|
@@ -53,6 +56,7 @@ public class PartitionContexts implements OffsetsStore, AssignmentStore, Partiti | |||
private final Map<TopicPartition, PartitionContext> contexts; | ||||
|
||||
private final AtomicBoolean reloadRequested; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still need this reloadRequested flag? Since we manage reloading states in reloadStates hash map There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, IMO having duplicated info (with per-partition reload) could be an error prone when we maintain this code in the future, while iterating over partitions unlikely becomes a bottleneck (since we already doing iterating partitions for updating high watermarks, checking partition needs pause/resume) so the benefit to have |
||||
private final ReentrantLock lock; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's rename to something like |
||||
|
||||
public PartitionContexts(SubscriptionScope scope, Processors<?> processors) { | ||||
this.scope = scope; | ||||
|
@@ -63,6 +67,7 @@ public PartitionContexts(SubscriptionScope scope, Processors<?> processors) { | |||
maxPendingRecords = scope.props().get(ProcessorProperties.CONFIG_MAX_PENDING_RECORDS).value(); | ||||
contexts = new HashMap<>(); | ||||
reloadRequested = new AtomicBoolean(false); | ||||
lock = new ReentrantLock(); | ||||
|
||||
scope.props().get(ProcessorProperties.CONFIG_PARTITION_CONCURRENCY).listen((oldVal, newVal) -> { | ||||
// This listener will be called at listener registration. | ||||
|
@@ -71,8 +76,16 @@ public PartitionContexts(SubscriptionScope scope, Processors<?> processors) { | |||
return; | ||||
} | ||||
|
||||
if (!reloadRequested.getAndSet(true)) { | ||||
logger.info("Requested reload partition.concurrency oldValue={}, newValue={}", oldVal, newVal); | ||||
lock.lock(); | ||||
try { | ||||
ocadaruma marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
if (!reloadRequested.getAndSet(true)) { | ||||
for (PartitionContext context: contexts.values()) { | ||||
context.reloading(true); | ||||
} | ||||
logger.info("Requested reload partition.concurrency oldValue={}, newValue={}", oldVal, newVal); | ||||
} | ||||
} finally { | ||||
lock.unlock(); | ||||
} | ||||
}); | ||||
} | ||||
|
@@ -200,7 +213,7 @@ PartitionContext instantiateContext(TopicPartition tp) { | |||
|
||||
// visible for testing | ||||
boolean pausingAllProcessing() { | ||||
return processingRateProp.value() == RateLimiter.PAUSED || reloadRequested.get(); | ||||
return processingRateProp.value() == RateLimiter.PAUSED; | ||||
} | ||||
|
||||
@Override | ||||
|
@@ -250,23 +263,30 @@ public void partitionsResumed(List<TopicPartition> partitions) { | |||
*/ | ||||
public void maybeHandlePropertyReload() { | ||||
if (reloadRequested.get()) { | ||||
if (totalPendingTasks() > 0) { | ||||
logger.debug("Waiting pending tasks for property reload."); | ||||
return; | ||||
lock.lock(); | ||||
try { | ||||
List<TopicPartition> reloadableTopicPartitions = contexts.entrySet() | ||||
.stream() | ||||
.filter(entry -> entry.getValue().reloading() && entry.getValue().pendingTasksCount() == 0) | ||||
.map(entry -> entry.getKey()) | ||||
.collect(Collectors.toList()); | ||||
reloadContexts(reloadableTopicPartitions); | ||||
long reloadingPartitions = contexts.values() | ||||
.stream() | ||||
.filter(PartitionContext::reloading) | ||||
.count(); | ||||
if (reloadingPartitions == 0) { | ||||
reloadRequested.set(false); | ||||
logger.info("Completed reloading all partition contexts"); | ||||
} | ||||
} finally { | ||||
lock.unlock(); | ||||
} | ||||
// it's ok to check-and-set reloadRequested without synchronization | ||||
// because this field is set to false only in this method, and this method is called from only subscription thread. | ||||
reloadRequested.set(false); | ||||
logger.info("Completed waiting pending tasks. Start reloading partition contexts"); | ||||
reloadContexts(); | ||||
} | ||||
} | ||||
|
||||
private void reloadContexts() { | ||||
// Save current topicPartitions into copy to update contexts map while iterating over this copy. | ||||
Set<TopicPartition> topicPartitions = new HashSet<>(contexts.keySet()); | ||||
|
||||
logger.info("Start dropping partition contexts"); | ||||
private void reloadContexts(Collection<TopicPartition> topicPartitions) { | ||||
logger.info("Start dropping partition context({})", topicPartitions); | ||||
removePartition(topicPartitions); | ||||
logger.info("Finished dropping partition contexts. Start recreating partition contexts"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nits] This log and this line Line 316 in 5659ec7
topicPartitions
|
||||
Map<TopicPartition, AssignmentConfig> configs = topicPartitions.stream().collect( | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pausing here by PartitionContext itself doesn't work to pause fetching actually.
Decaton's fetch-pause works like:
So, if we pause here by PartitionContext itself, it will not be included in (1) (because it's already "paused").
Correct approach here may be holding per-partition
reloading
flag in PartitionContexts, and include them inpartitionsNeedsPause