11package cn .leancloud .kafka .consumer ;
22
33import org .apache .kafka .clients .consumer .Consumer ;
4- import org .apache .kafka .clients .consumer .ConsumerRecord ;
54import org .apache .kafka .clients .consumer .OffsetAndMetadata ;
65import org .apache .kafka .common .TopicPartition ;
76import org .apache .kafka .common .errors .RetriableException ;
87
98import java .time .Duration ;
109import java .util .*;
1110
12- import static java .util .stream . Collectors . toSet ;
11+ import static java .util .Collections . emptySet ;
1312
14- abstract class AbstractCommitPolicy <K , V > implements CommitPolicy < K , V > {
13+ abstract class AbstractCommitPolicy <K , V > implements CommitPolicy {
1514 static SleepFunction sleepFunction = Thread ::sleep ;
1615
1716 interface SleepFunction {
@@ -45,116 +44,35 @@ void onError(RetriableException e) {
4544 }
4645
4746 protected final Consumer <K , V > consumer ;
48- private final Map <TopicPartition , Long > topicOffsetHighWaterMark ;
49- private final Map <TopicPartition , CompletedOffsets > completedOffsets ;
5047 private final long syncCommitRetryIntervalMs ;
5148 private final int maxAttemptsForEachSyncCommit ;
5249
5350 AbstractCommitPolicy (Consumer <K , V > consumer , Duration syncCommitRetryInterval , int maxAttemptsForEachSyncCommit ) {
5451 this .consumer = consumer ;
55- this .topicOffsetHighWaterMark = new HashMap <>();
56- this .completedOffsets = new HashMap <>();
5752 this .syncCommitRetryIntervalMs = syncCommitRetryInterval .toMillis ();
5853 this .maxAttemptsForEachSyncCommit = maxAttemptsForEachSyncCommit ;
5954 }
6055
6156 @ Override
62- public void markPendingRecord (ConsumerRecord <K , V > record ) {
63- final TopicPartition topicPartition = new TopicPartition (record .topic (), record .partition ());
64- topicOffsetHighWaterMark .merge (
65- topicPartition ,
66- record .offset () + 1 ,
67- Math ::max );
68-
69- final CompletedOffsets offset = completedOffsets .get (topicPartition );
70- // please note that if offset exists, it could happen for record.offset() >= offset.nextOffsetToCommit()
71- // when there're duplicate records which have lower offset than our next offset to commit consumed from broker
72- if (offset == null ) {
73- completedOffsets .put (topicPartition , new CompletedOffsets (record .offset () - 1L ));
74- }
75- }
76-
77- @ Override
78- public void markCompletedRecord (ConsumerRecord <K , V > record ) {
79- final CompletedOffsets offset = completedOffsets .get (new TopicPartition (record .topic (), record .partition ()));
80- // offset could be null, when the partition of the record was revoked before its processing was done
81- if (offset != null ) {
82- offset .addCompleteOffset (record .offset ());
83- }
84- }
85-
86- @ Override
87- public void revokePartitions (Collection <TopicPartition > partitions ) {
88- clearProcessingRecordStatesFor (partitions );
89- }
90-
91- @ Override
92- public Set <TopicPartition > partialCommitSync () {
93- final Map <TopicPartition , OffsetAndMetadata > offsetsToCommit = completedTopicOffsetsToCommit ();
57+ public Set <TopicPartition > partialCommitSync (ProcessRecordsProgress progress ) {
58+ final Map <TopicPartition , OffsetAndMetadata > offsetsToCommit = progress .completedOffsetsToCommit ();
9459 if (offsetsToCommit .isEmpty ()) {
95- return Collections . emptySet ();
60+ return emptySet ();
9661 }
9762 commitSyncWithRetry (offsetsToCommit );
98- updatePartialCommittedOffsets (offsetsToCommit );
63+ progress . updateCommittedOffsets (offsetsToCommit );
9964
100- return clearProcessingRecordStatesForCompletedPartitions (offsetsToCommit );
65+ return progress . clearCompletedPartitions (offsetsToCommit );
10166 }
10267
103- Set <TopicPartition > fullCommitSync () {
68+ Set <TopicPartition > fullCommitSync (ProcessRecordsProgress progress ) {
10469 commitSyncWithRetry ();
10570
106- final Set <TopicPartition > completePartitions = partitionsForAllRecordsStates ();
107- clearAllProcessingRecordStates ();
71+ final Set <TopicPartition > completePartitions = progress . allPartitions ();
72+ progress . clearAll ();
10873 return completePartitions ;
10974 }
11075
111- @ VisibleForTesting
112- Map <TopicPartition , Long > topicOffsetHighWaterMark () {
113- return topicOffsetHighWaterMark ;
114- }
115-
116- Map <TopicPartition , OffsetAndMetadata > completedTopicOffsetsToCommit () {
117- if (noCompletedOffsets ()) {
118- return Collections .emptyMap ();
119- }
120-
121- final Map <TopicPartition , OffsetAndMetadata > offsets = new HashMap <>();
122- for (Map .Entry <TopicPartition , CompletedOffsets > entry : completedOffsets .entrySet ()) {
123- final CompletedOffsets offset = entry .getValue ();
124- if (offset .hasOffsetToCommit ()) {
125- offsets .put (entry .getKey (), offset .getOffsetToCommit ());
126- }
127- }
128-
129- return offsets ;
130- }
131-
132- boolean noTopicOffsetsToCommit () {
133- if (noCompletedOffsets ()) {
134- return true ;
135- }
136-
137- for (Map .Entry <TopicPartition , CompletedOffsets > entry : completedOffsets .entrySet ()) {
138- final CompletedOffsets offset = entry .getValue ();
139- if (offset .hasOffsetToCommit ()) {
140- return false ;
141- }
142- }
143-
144- return true ;
145- }
146-
147- void updatePartialCommittedOffsets (Map <TopicPartition , OffsetAndMetadata > offsets ) {
148- for (Map .Entry <TopicPartition , OffsetAndMetadata > entry : offsets .entrySet ()) {
149- final CompletedOffsets offset = completedOffsets .get (entry .getKey ());
150- offset .updateCommittedOffset (entry .getValue ().offset ());
151- }
152- }
153-
154- boolean noCompletedOffsets () {
155- return completedOffsets .isEmpty ();
156- }
157-
15876 void commitSyncWithRetry () {
15977 final RetryContext context = context ();
16078 do {
@@ -179,50 +97,6 @@ void commitSyncWithRetry(Map<TopicPartition, OffsetAndMetadata> offsets) {
17997 } while (true );
18098 }
18199
182- Set <TopicPartition > partitionsForAllRecordsStates () {
183- return new HashSet <>(topicOffsetHighWaterMark .keySet ());
184- }
185-
186- void clearAllProcessingRecordStates () {
187- topicOffsetHighWaterMark .clear ();
188- completedOffsets .clear ();
189- }
190-
191- Set <TopicPartition > clearProcessingRecordStatesForCompletedPartitions (Map <TopicPartition , OffsetAndMetadata > committedOffsets ) {
192- final Set <TopicPartition > partitions = partitionsToSafeResume (committedOffsets );
193- clearProcessingRecordStatesFor (partitions );
194- return partitions ;
195- }
196-
197- void clearProcessingRecordStatesFor (Collection <TopicPartition > partitions ) {
198- for (TopicPartition p : partitions ) {
199- topicOffsetHighWaterMark .remove (p );
200- completedOffsets .remove (p );
201- }
202- }
203-
204- Set <TopicPartition > partitionsToSafeResume () {
205- return partitionsToSafeResume (completedTopicOffsetsToCommit ());
206- }
207-
208- Set <TopicPartition > partitionsToSafeResume (Map <TopicPartition , OffsetAndMetadata > completedOffsets ) {
209- return completedOffsets
210- .entrySet ()
211- .stream ()
212- .filter (entry -> topicOffsetMeetHighWaterMark (entry .getKey (), entry .getValue ()))
213- .map (Map .Entry ::getKey )
214- .collect (toSet ());
215- }
216-
217- private boolean topicOffsetMeetHighWaterMark (TopicPartition topicPartition , OffsetAndMetadata offset ) {
218- final Long offsetHighWaterMark = topicOffsetHighWaterMark .get (topicPartition );
219- if (offsetHighWaterMark != null ) {
220- return offset .offset () >= offsetHighWaterMark ;
221- }
222- // maybe this partition revoked before a msg of this partition was processed
223- return true ;
224- }
225-
226100 private RetryContext context () {
227101 return new RetryContext (syncCommitRetryIntervalMs , maxAttemptsForEachSyncCommit );
228102 }
0 commit comments