Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
dc572ce
change --execute parameter to --dry-run, equivalent to StreamsResetter.
jeqo Oct 27, 2017
30fc499
defining params and where to change reset offset implementation
jeqo Oct 27, 2017
d759684
implementing reset offset scenarios
jeqo Oct 28, 2017
da857ab
adding tests
jeqo Oct 30, 2017
1b7af25
Merge remote-tracking branch 'origin/trunk' into feature/kip-171
jeqo Oct 30, 2017
c275f6b
fix import control
jeqo Oct 30, 2017
7609bd9
validate reset scenarios
jeqo Oct 30, 2017
58d78a6
refactoring enforcing final variables and validating NPE
jeqo Nov 14, 2017
09342e5
split case scenarios
jeqo Nov 14, 2017
cd4dede
fix import control for StreamsResetter
jeqo Nov 14, 2017
34f004f
fix checkstyle
jeqo Nov 14, 2017
9d140b1
final on Exception vars
jeqo Nov 15, 2017
b3843b9
remove unused variables
jeqo Nov 15, 2017
86c60d2
KAFKA-5565: Add a broker metric specifying the number of consumer gro…
cmccabe Oct 9, 2017
d944584
MINOR: Factor out some common group/transactional fields in request o…
hachikuji Oct 9, 2017
fa4209b
KAFKA-6030; Fix Integer overflow in cleanable ratio computation
xli140602 Oct 9, 2017
076da7e
KAFKA-6026; Fix for indefinite wait in KafkaFutureImpl
bartdevylder Oct 10, 2017
b4cafb3
KAFKA-6027; Access to log should throw KafkaStorageException after th…
lindong28 Oct 10, 2017
d554e02
MINOR: fix EOS test race condition
mjsax Oct 10, 2017
f9c9ddd
KAFKA-6016; Make the reassign partitions system test use the idempote…
Oct 10, 2017
d1edd22
MINOR: Avoid some unnecessary collection copies in KafkaApis
hachikuji Oct 10, 2017
6f613f8
KAFKA-6025: small fix for streams tutorial
bbejeck Oct 10, 2017
2e4487e
MINOR: KIP-182 follow up; add deprecation annotations to test classes
mjsax Oct 10, 2017
2ffb742
KAFKA-5541: Follow-up; Try to clean uncleanly upon clean close failur…
mjsax Oct 10, 2017
5eee0bb
HOTFIX: Updates on release.py before 1.0.0
guozhangwang Oct 11, 2017
c9024f9
MINOR: Push JMX metric name mangling into the JmxReporter (KIP-190 fo…
ewencp Oct 11, 2017
4a186ea
MINOR: Redesign of Streams page to include video & logos
Oct 11, 2017
e3e8600
KAFKA-6053; Fix NoSuchMethodError when creating ProducerRecords with …
Oct 12, 2017
5181bd2
KAFKA-6055; Fix typo in KAFKA_JVM_PERFORMANCE_OPTS for Windows
vahidhashemian Oct 12, 2017
0d8fff3
MINOR: Fix log message in AbstractFetcherThread
mimaison Oct 12, 2017
65a502a
MINOR: Use port 0 in ResetIntegrationWithSslTest
tombentley Oct 12, 2017
d5ab717
MINOR: Update `config/consumer.properties` to have new consumer prope…
omkreddy Oct 12, 2017
99cb148
MINOR: Don't register signal handlers if running on Windows
ijuma Oct 12, 2017
27ab94d
MINOR: improve Store parameter checks
mjsax Oct 12, 2017
a344153
MINOR: Update JavaDoc to use new API
bbejeck Oct 12, 2017
a9d5041
MINOR: Bump the request timeout for the transactional message copier
Oct 12, 2017
5f24007
MINOR: add unit test for StateStoreSerdes
mjsax Oct 13, 2017
9418345
MINOR: Merge script improvements
ijuma Oct 13, 2017
ac625e2
MINOR: update comments in config/producer.properties
omkreddy Oct 13, 2017
f0a70d1
MINOR: Include isolationLevel in toString of FetchRequest
jaceklaskowski Oct 13, 2017
ddaaeb3
MINOR: a few web doc and javadoc fixes
guozhangwang Oct 16, 2017
6a95be4
MINOR: update exception message for KIP-120
mjsax Oct 17, 2017
c252c77
MINOR: A few javadoc fixes
hachikuji Oct 17, 2017
6b9174c
MINOR: add equals to SessionWindows
dguy Oct 17, 2017
acb8d7d
KAFKA-4504; Clarify that retention.bytes is a partition level config
omkreddy Oct 17, 2017
239cc74
KAFKA-5163; Support replicas movement between log directories (KIP-113)
lindong28 Oct 17, 2017
caa9b64
KAFKA-6023 ThreadCache#sizeBytes() should check overflow
shivsantham Oct 18, 2017
887af3a
KAFKA-5642; Use async ZookeeperClient in Controller
onurkaraman Oct 18, 2017
2c76084
KAFKA-6051; Close the ReplicaFetcherBlockingSend earlier on shutdown
Oct 18, 2017
bc0b016
MINOR: Remove dead code
ijuma Oct 18, 2017
c495fa1
MINOR: Correct KafkaProducer Javadoc spelling of property 'max.in.fli…
hmcl Oct 19, 2017
334c2e4
KAFKA-6069: Properly tag KafkaStreams metrics with the client id.
Oct 19, 2017
5a2c844
MINOR: Fix var typo in verifiable_consumer assertion
edenhill Oct 19, 2017
b809fbb
MINOR: Update docs with regards to max.in.flight and idempotent producer
ijuma Oct 19, 2017
2117a48
KAFKA-6087: Scanning plugin.path needs to support relative symlinks.
kkonstantine Oct 19, 2017
8ce1e6b
KAFKA-6071; Use ZookeeperClient in LogManager
omkreddy Oct 20, 2017
fa5279d
MINOR: Use ObjectName.quote instead of URL-encoding for JMX metric tags
rajinisivaram Oct 20, 2017
77b21f2
KAFKA-6042: Avoid deadlock between two groups with delayed operations
rajinisivaram Oct 22, 2017
0f6e0cc
MINOR: Controller and async ZookeeperClient improvements
ijuma Oct 22, 2017
d0bf5e3
MINOR: Changed visibility of methods in ClusterConnectionStates to pr…
soenkeliebau Oct 22, 2017
042d79a
MINOR: add hint for setting an uncaught exception handler to JavaDocs
mjsax Oct 23, 2017
d6e0a08
MINOR: Update Scala to 2.12.4
ijuma Oct 23, 2017
96c4b18
KAFKA-6104; Added unit tests for ClusterConnectionStates
soenkeliebau Oct 23, 2017
2bb2003
MINOR: Configure owasp.dependencycheck gradle plugin
ijuma Oct 23, 2017
53afce6
KAFKA-6101; Reconnecting to broker does not exponentially backoff
tedyu Oct 23, 2017
6bde4dd
KAFKA-6070; add ipaddress and enum34 dependencies to docker image
cmccabe Oct 23, 2017
f581ce0
KAFKA-5140: Fix reset integration test
guozhangwang Oct 23, 2017
a17c882
HOTFIX: Poll with zero milliseconds during restoration phase
guozhangwang Oct 23, 2017
499e4c3
MINOR: Fix typo in ConsumerCoordinator comment
jeffwidman Oct 24, 2017
02ffb1c
MINOR: Fix typo dev guide title
Oct 25, 2017
92fdfdf
MINOR: reset state in cleanup, fixes jmx mixin flakiness
xvrl Oct 25, 2017
dafb9ef
MINOR: Rename and change package of async ZooKeeper classes
ijuma Oct 26, 2017
9670278
MINOR: Revert EmbeddedZooKeeper rename
ijuma Oct 26, 2017
aa0eb4d
MINOR: Add missing semicolon to example jaas configuration
omkreddy Oct 26, 2017
a1755cd
KAFKA-6100: Down-grade RocksDB to 5.7.3
guozhangwang Oct 26, 2017
3ed95a8
MINOR: Improve a Windows quickstart instruction
vahidhashemian Oct 26, 2017
331eb59
KAFKA-6131; Use atomic putIfAbsent to create txn marker queues
rajinisivaram Oct 27, 2017
3b750df
KAFKA-6119: Bump epoch when expiring transactions in the TransactionC…
Oct 27, 2017
84b8b71
MINOR: Fix indentation in KafkaApis.handleOffsetFetchRequest
vahidhashemian Oct 27, 2017
e202555
MINOR: Document "high watermark" magic value for delete records request
ppatierno Oct 27, 2017
c99c8ff
MINOR: Update docs wrt topic deletion being enabled by default
omkreddy Oct 27, 2017
a0c30ce
MINOR: Mention "per listener" security overrides in listener.security…
omkreddy Oct 27, 2017
709293d
MINOR: Ensure that the producer in testAlterReplicaLogDirs is always …
ijuma Oct 27, 2017
b4d859d
MINOR: Remove TLS renegotiation code
ijuma Oct 27, 2017
3fdd977
change --execute parameter to --dry-run, equivalent to StreamsResetter.
jeqo Oct 27, 2017
59029de
defining params and where to change reset offset implementation
jeqo Oct 27, 2017
695cee3
implementing reset offset scenarios
jeqo Oct 28, 2017
c87230f
adding tests
jeqo Oct 30, 2017
e92793c
fix import control
jeqo Oct 30, 2017
793b513
validate reset scenarios
jeqo Oct 30, 2017
63ba0e8
refactoring enforcing final variables and validating NPE
jeqo Nov 14, 2017
d9b77c6
split case scenarios
jeqo Nov 14, 2017
f1b09e4
fix import control for StreamsResetter
jeqo Nov 14, 2017
d08e084
fix checkstyle
jeqo Nov 14, 2017
5b7ce9c
final on Exception vars
jeqo Nov 15, 2017
90ece05
remove unused variables
jeqo Nov 15, 2017
5a778b4
Merge branch 'feature/kip-171' of github.com:jeqo/kafka into feature/…
jeqo Nov 16, 2017
cad3f1f
refactor `maybeReset` method
jeqo Nov 21, 2017
51918d7
fix import control
jeqo Nov 21, 2017
9727b9e
unit tests for streamsResetter
jeqo Dec 1, 2017
ffc857e
remove methods already tested by unit test from integration test
jeqo Dec 4, 2017
ebb6844
remove methods already tested by unit test from integration test
jeqo Dec 5, 2017
55f7f3e
add condition for intermediateTopics
jeqo Dec 6, 2017
fab095f
fix indentation
jeqo Dec 6, 2017
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
1 change: 1 addition & 0 deletions checkstyle/import-control-core.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<allow pkg="kafka.consumer" />
<allow pkg="joptsimple" />
<allow pkg="org.apache.kafka.clients.consumer" />
<allow class="javax.xml.datatype.Duration" />
</subpackage>

<subpackage name="coordinator">
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/kafka/admin/ConsumerGroupCommand.scala
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,8 @@ object ConsumerGroupCommand extends Logging {
case "Empty" | "Dead" =>
val partitionsToReset = getPartitionsToReset(groupId)
val preparedOffsets = prepareOffsetsToReset(groupId, partitionsToReset)
val execute = opts.options.has(opts.executeOpt)
if (execute)
val dryRun = opts.options.has(opts.dryRunOpt)
if (!dryRun)
getConsumer().commitSync(preparedOffsets.asJava)
preparedOffsets
case currentState =>
Expand Down Expand Up @@ -727,7 +727,7 @@ object ConsumerGroupCommand extends Logging {
"Has 3 execution options: (default) to plan which offsets to reset, --execute to execute the reset-offsets process, and --export to export the results to a CSV format." + nl +
"Has the following scenarios to choose: --to-datetime, --by-period, --to-earliest, --to-latest, --shift-by, --from-file, --to-current. One scenario must be choose" + nl +
"To define the scope use: --all-topics or --topic. . One scope must be choose, unless you use '--from-file' scenario"
val ExecuteDoc = "Execute operation. Supported operations: reset-offsets."
val DryRunDoc = "Only show results without executing changes on Consumer Groups. Supported operations: reset-offsets."
val ExportDoc = "Export operation execution to a CSV file. Supported operations: reset-offsets."
val ResetToOffsetDoc = "Reset offsets to a specific offset."
val ResetFromFileDoc = "Reset offsets to values defined in CSV file."
Expand Down Expand Up @@ -770,7 +770,7 @@ object ConsumerGroupCommand extends Logging {
.describedAs("command config property file")
.ofType(classOf[String])
val resetOffsetsOpt = parser.accepts("reset-offsets", ResetOffsetsDoc)
val executeOpt = parser.accepts("execute", ExecuteDoc)
val dryRunOpt = parser.accepts("dry-run", DryRunDoc)
val exportOpt = parser.accepts("export", ExportDoc)
val resetToOffsetOpt = parser.accepts("to-offset", ResetToOffsetDoc)
.withRequiredArg()
Expand Down
213 changes: 200 additions & 13 deletions core/src/main/scala/kafka/tools/StreamsResetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,27 @@
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.OptionSpecBuilder;
import kafka.utils.CommandLineUtils;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.DeleteTopicsResult;
import org.apache.kafka.clients.admin.KafkaAdminClient;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndTimestamp;
import org.apache.kafka.common.KafkaFuture;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.annotation.InterfaceStability;
import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.apache.kafka.common.utils.Exit;
import org.apache.kafka.common.utils.Utils;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
Expand Down Expand Up @@ -74,13 +81,21 @@ public class StreamsResetter {
private static OptionSpec<String> applicationIdOption;
private static OptionSpec<String> inputTopicsOption;
private static OptionSpec<String> intermediateTopicsOption;
private static OptionSpec<Long> toOffsetOption;
private static OptionSpec<String> toDatetimeOption;
private static OptionSpec<String> byDurationOption;
private static OptionSpecBuilder toEarliestOption;
private static OptionSpecBuilder toLatestOption;
private static OptionSpec<String> fromFileOption;
private static OptionSpec<Long> shiftByOption;
private static OptionSpecBuilder dryRunOption;
private static OptionSpec<String> commandConfigOption;

private OptionSet options = null;
private final List<String> allTopics = new LinkedList<>();
private boolean dryRun = false;


public int run(final String[] args) {
return run(args, new Properties());
}
Expand Down Expand Up @@ -169,6 +184,24 @@ private void parseArguments(final String[] args) throws IOException {
.ofType(String.class)
.withValuesSeparatedBy(',')
.describedAs("list");
toOffsetOption = optionParser.accepts("to-offset", "Reset offsets to a specific offset.")
.withRequiredArg()
.ofType(Long.class);
toDatetimeOption = optionParser.accepts("to-datetime", "Reset offsets to offset from datetime. Format: 'YYYY-MM-DDTHH:mm:SS.sss'")
.withRequiredArg()
.ofType(String.class);
byDurationOption = optionParser.accepts("by-duration", "Reset offsets to offset by duration from current timestamp. Format: 'PnDTnHnMnS'")
.withRequiredArg()
.ofType(String.class);
toEarliestOption = optionParser.accepts("to-earliest", "Reset offsets to earliest offset.");
toLatestOption = optionParser.accepts("to-latest", "Reset offsets to latest offset.");
fromFileOption = optionParser.accepts("from-file", "Reset offsets to values defined in CSV file.")
.withRequiredArg()
.ofType(String.class);
shiftByOption = optionParser.accepts("shift-by", "Reset offsets shifting current offset by 'n', where 'n' can be positive or negative")
.withRequiredArg()
.describedAs("number-of-offsets")
.ofType(Long.class);
commandConfigOption = optionParser.accepts("config-file", "Property file containing configs to be passed to admin clients and embedded consumer.")
.withRequiredArg()
.ofType(String.class)
Expand All @@ -184,9 +217,26 @@ private void parseArguments(final String[] args) throws IOException {
printHelp(optionParser);
throw e;
}

scala.collection.immutable.HashSet<OptionSpec<?>> allScenarioOptions = new scala.collection.immutable.HashSet<>();
allScenarioOptions.$plus(toOffsetOption);
allScenarioOptions.$plus(toDatetimeOption);
allScenarioOptions.$plus(byDurationOption);
allScenarioOptions.$plus(toEarliestOption);
allScenarioOptions.$plus(toLatestOption);
allScenarioOptions.$plus(fromFileOption);
allScenarioOptions.$plus(shiftByOption);

CommandLineUtils.checkInvalidArgs(optionParser, options, toOffsetOption, allScenarioOptions.$minus(toOffsetOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, toDatetimeOption, allScenarioOptions.$minus(toDatetimeOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, byDurationOption, allScenarioOptions.$minus(byDurationOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, toEarliestOption, allScenarioOptions.$minus(toEarliestOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, toLatestOption, allScenarioOptions.$minus(toLatestOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, fromFileOption, allScenarioOptions.$minus(fromFileOption));
CommandLineUtils.checkInvalidArgs(optionParser, options, shiftByOption, allScenarioOptions.$minus(shiftByOption));
}

private void maybeResetInputAndSeekToEndIntermediateTopicOffsets(final Map consumerConfig) {
private void maybeResetInputAndSeekToEndIntermediateTopicOffsets(final Map consumerConfig) throws Exception {
final List<String> inputTopics = options.valuesOf(inputTopicsOption);
final List<String> intermediateTopics = options.valuesOf(intermediateTopicsOption);

Expand All @@ -202,7 +252,7 @@ private void maybeResetInputAndSeekToEndIntermediateTopicOffsets(final Map consu
}

if (inputTopics.size() != 0) {
System.out.println("Seek-to-beginning for input topics " + inputTopics);
System.out.println("Reset-offsets for input topics " + inputTopics);
}
if (intermediateTopics.size() != 0) {
System.out.println("Seek-to-end for intermediate topics " + intermediateTopics);
Expand Down Expand Up @@ -249,7 +299,7 @@ private void maybeResetInputAndSeekToEndIntermediateTopicOffsets(final Map consu
}
}

maybeSeekToBeginning(client, inputTopicPartitions);
maybeReset(client, inputTopicPartitions);

maybeSeekToEnd(client, intermediateTopicPartitions);

Expand All @@ -274,7 +324,7 @@ private void maybeResetInputAndSeekToEndIntermediateTopicOffsets(final Map consu
}
}

} catch (final RuntimeException e) {
} catch (final Exception e) {
System.err.println("ERROR: Resetting offsets failed.");
throw e;
}
Expand All @@ -300,23 +350,160 @@ private void maybeSeekToEnd(final KafkaConsumer<byte[], byte[]> client,
}
}

private void maybeSeekToBeginning(final KafkaConsumer<byte[], byte[]> client,
final Set<TopicPartition> inputTopicPartitions) {
private void maybeReset(final KafkaConsumer<byte[], byte[]> client,
final Set<TopicPartition> inputTopicPartitions)
throws Exception {

final List<String> inputTopics = options.valuesOf(inputTopicsOption);
final String groupId = options.valueOf(applicationIdOption);

if (inputTopicPartitions.size() > 0) {
System.out.println("Following input topics offsets will be reset to beginning (for consumer group " + groupId + ")");
for (final String topic : inputTopics) {
if (allTopics.contains(topic)) {
System.out.println("Topic: " + topic);
System.out.println("Following input topics offsets will be reset to (for consumer group " + groupId + ")");
if (options.has(toOffsetOption)) {
final Long offset = options.valueOf(toOffsetOption);
final Map<TopicPartition, Long> endOffsets = client.endOffsets(inputTopicPartitions);
final Map<TopicPartition, Long> beginningOffsets = client.beginningOffsets(inputTopicPartitions);

final Map<TopicPartition, Long> topicPartitionsAndOffset = new HashMap<>(inputTopicPartitions.size());
for (final TopicPartition topicPartition : inputTopicPartitions) {
topicPartitionsAndOffset.put(topicPartition, offset);
}
}
if (!dryRun) {

final Map<TopicPartition, Long> validatedTopicPartitionsAndOffset =
checkOffsetRange(topicPartitionsAndOffset, beginningOffsets, endOffsets);

for (final TopicPartition topicPartition : inputTopicPartitions) {
client.seek(topicPartition, validatedTopicPartitionsAndOffset.get(topicPartition));
}
} else if (options.has(toEarliestOption)) {
client.seekToBeginning(inputTopicPartitions);
} else if (options.has(toLatestOption)) {
client.seekToEnd(inputTopicPartitions);
} else if (options.has(shiftByOption)) {
final Long shiftBy = options.valueOf(shiftByOption);
final Map<TopicPartition, Long> endOffsets = client.endOffsets(inputTopicPartitions);
final Map<TopicPartition, Long> beginningOffsets = client.beginningOffsets(inputTopicPartitions);

final Map<TopicPartition, Long> topicPartitionsAndOffset = new HashMap<>(inputTopicPartitions.size());
for (final TopicPartition topicPartition : inputTopicPartitions) {
final Long position = client.position(topicPartition);
final Long offset = position + shiftBy;
topicPartitionsAndOffset.put(topicPartition, offset);
}

final Map<TopicPartition, Long> validatedTopicPartitionsAndOffset =
checkOffsetRange(topicPartitionsAndOffset, beginningOffsets, endOffsets);

for (final TopicPartition topicPartition : inputTopicPartitions) {
client.seek(topicPartition, validatedTopicPartitionsAndOffset.get(topicPartition));
}
} else if (options.has(toDatetimeOption)) {
final String ts = options.valueOf(toDatetimeOption);
final Long timestamp = getDateTime(ts);

final Map<TopicPartition, Long> topicPartitionsAndTimes = new HashMap<>(inputTopicPartitions.size());
for (final TopicPartition topicPartition : inputTopicPartitions) {
topicPartitionsAndTimes.put(topicPartition, timestamp);
}

final Map<TopicPartition, OffsetAndTimestamp> topicPartitionsAndOffset = client.offsetsForTimes(topicPartitionsAndTimes);

for (final TopicPartition topicPartition : inputTopicPartitions) {
final Long offset = topicPartitionsAndOffset.get(topicPartition).offset();
client.seek(topicPartition, offset);
}
} else if (options.has(byDurationOption)) {
final String duration = options.valueOf(byDurationOption);
final Date now = new Date();
final Duration durationParsed = DatatypeFactory.newInstance().newDuration(duration);
durationParsed.negate().addTo(now);
final Long timestamp = now.getTime();

final Map<TopicPartition, Long> topicPartitionsAndTimes = new HashMap<>(inputTopicPartitions.size());
for (final TopicPartition topicPartition : inputTopicPartitions) {
topicPartitionsAndTimes.put(topicPartition, timestamp);
}

final Map<TopicPartition, OffsetAndTimestamp> topicPartitionsAndOffset = client.offsetsForTimes(topicPartitionsAndTimes);

for (final TopicPartition topicPartition : inputTopicPartitions) {
final Long offset = topicPartitionsAndOffset.get(topicPartition).offset();
client.seek(topicPartition, offset);
}
} else if (options.has(fromFileOption)) {
final Map<TopicPartition, Long> endOffsets = client.endOffsets(inputTopicPartitions);
final Map<TopicPartition, Long> beginningOffsets = client.beginningOffsets(inputTopicPartitions);

final String resetPlanPath = options.valueOf(fromFileOption);
final String resetPlanCsv = Utils.readFileAsString(resetPlanPath);
final Map<TopicPartition, Long> topicPartitionsAndOffset = parseResetPlan(resetPlanCsv);

final Map<TopicPartition, Long> validatedTopicPartitionsAndOffset =
checkOffsetRange(topicPartitionsAndOffset, beginningOffsets, endOffsets);

for (final TopicPartition topicPartition : inputTopicPartitions) {
final Long offset = validatedTopicPartitionsAndOffset.get(topicPartition);
client.seek(topicPartition, offset);
}
} else {
client.seekToBeginning(inputTopicPartitions);
}

for (final TopicPartition p : inputTopicPartitions) {
final Long position = client.position(p);
System.out.println("Topic: " + p.topic() + " Partition: " + p.partition() + " Offset: " + position);
}
}
}

public Long getDateTime(String ts) throws ParseException {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be package private? (add comment // visible for testing)

nit: ts -> timestamp

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but test is in another package/module. Maybe once it is moved to streams we can improve visibility of this method. I will add the comment.

if (!(ts.split("T")[1].contains("+") || ts.split("T")[1].contains("-") || ts.split("T")[1].contains("Z"))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could throw a NPE. I think we should guard against this, and throw ParseException if NPE happens

ts.split("T") is redundant and should be extracted into a variable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I will reformat this.

ts = ts + "Z";
}

Date date;
try {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse(ts);
} catch (ParseException e) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add final

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I'll reformat.

date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX").parse(ts);
}

return date.getTime();
}

private Map<TopicPartition, Long> parseResetPlan(String resetPlanCsv) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add final

final Map<TopicPartition, Long> topicPartitionAndOffset = new HashMap<>();
for (final String line : resetPlanCsv.split("\n")) {
final String[] parts = line.split(",");
final String topic = parts[0];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we guard against NPE? (same blow)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap, I'll refactor.

final int partition = Integer.parseInt(parts[1]);
final long offset = Long.parseLong(parts[2]);
final TopicPartition topicPartition = new TopicPartition(topic, partition);
topicPartitionAndOffset.put(topicPartition, offset);
}
return topicPartitionAndOffset;
}

private Map<TopicPartition, Long> checkOffsetRange(Map<TopicPartition, Long> inputTopicPartitionsAndOffset,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final (3x)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

Map<TopicPartition, Long> beginningOffsets,
Map<TopicPartition, Long> endOffsets) {
Map<TopicPartition, Long> validatedTopicPartitionsOffsets = new HashMap<>();
for (final TopicPartition topicPartition : inputTopicPartitionsAndOffset.keySet()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: As key and value is accessed, we might want to iterate throw the .entrySet instead of keySet

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree.

final Long endOffset = endOffsets.get(topicPartition);
final Long offset = inputTopicPartitionsAndOffset.get(topicPartition);
if (offset < endOffset) {
final Long beginningOffset = beginningOffsets.get(topicPartition);
if (offset > beginningOffset) {
validatedTopicPartitionsOffsets.put(topicPartition, offset);
} else {
System.out.println("New offset (" + offset + ") is lower than earliest offset. Value will be set to " + beginningOffset);
validatedTopicPartitionsOffsets.put(topicPartition, beginningOffset);
}
} else {
System.out.println("New offset (" + offset + ") is higher than latest offset. Value will be set to " + endOffset);
validatedTopicPartitionsOffsets.put(topicPartition, endOffset);
}
}
return validatedTopicPartitionsOffsets;
}

private boolean isInputTopic(final String topic) {
Expand Down
Loading