-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-15121: Implement the alterOffsets method in the FileStreamSourceConnector and the FileStreamSinkConnector #13945
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
Changes from 3 commits
ee7c39d
776e360
d60ce9e
45f0e53
cd46163
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 |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ | |
| import org.apache.kafka.common.config.ConfigDef.Type; | ||
| import org.apache.kafka.common.utils.AppInfoParser; | ||
| import org.apache.kafka.connect.connector.Task; | ||
| import org.apache.kafka.connect.errors.ConnectException; | ||
| import org.apache.kafka.connect.source.ExactlyOnceSupport; | ||
| import org.apache.kafka.connect.source.SourceConnector; | ||
| import org.slf4j.Logger; | ||
|
|
@@ -31,6 +32,9 @@ | |
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import static org.apache.kafka.connect.file.FileStreamSourceTask.FILENAME_FIELD; | ||
| import static org.apache.kafka.connect.file.FileStreamSourceTask.POSITION_FIELD; | ||
|
|
||
| /** | ||
| * Very simple source connector that works with stdin or a file. | ||
| */ | ||
|
|
@@ -101,4 +105,50 @@ public ExactlyOnceSupport exactlyOnceSupport(Map<String, String> props) { | |
| : ExactlyOnceSupport.UNSUPPORTED; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean alterOffsets(Map<String, String> connectorConfig, Map<Map<String, ?>, Map<String, ?>> offsets) { | ||
| AbstractConfig config = new AbstractConfig(CONFIG_DEF, connectorConfig); | ||
| String filename = config.getString(FILE_CONFIG); | ||
| if (filename == null || filename.isEmpty()) { | ||
| // If the 'file' configuration is unspecified, stdin is used and no offsets are tracked | ||
| throw new ConnectException("Offsets cannot be modified if the '" + FILE_CONFIG + "' configuration is unspecified. " + | ||
| "This is because stdin is used for input and offsets are not tracked."); | ||
| } | ||
|
C0urante marked this conversation as resolved.
|
||
|
|
||
| // This connector makes use of a single source partition at a time which represents the file that it is configured to read from. | ||
| // However, there could also be source partitions from previous configurations of the connector. | ||
| for (Map.Entry<Map<String, ?>, Map<String, ?>> partitionOffset : offsets.entrySet()) { | ||
| Map<String, ?> partition = partitionOffset.getKey(); | ||
| if (partition == null) { | ||
| throw new ConnectException("Partition objects cannot be null"); | ||
| } | ||
|
|
||
| if (!partition.containsKey(FILENAME_FIELD)) { | ||
| throw new ConnectException("Partition objects should contain the key '" + FILENAME_FIELD + "'"); | ||
| } | ||
|
|
||
| Map<String, ?> offset = partitionOffset.getValue(); | ||
| // null offsets are allowed and represent a deletion of offsets for a partition | ||
| if (offset == null) { | ||
| return true; | ||
|
yashmayya marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| if (!offset.containsKey(POSITION_FIELD)) { | ||
| throw new ConnectException("Offset objects should either be null or contain the key '" + POSITION_FIELD + "'"); | ||
| } | ||
|
|
||
| // The 'position' in the offset represents the position in the file's byte stream and should be a non-negative long value | ||
| try { | ||
| long offsetPosition = Long.parseLong(String.valueOf(offset.get(POSITION_FIELD))); | ||
|
Contributor
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. Isn't this too permissive? Based on the
Contributor
Author
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. Ah, good catch, thanks. I think it might probably be a bit friendlier if we update the task class instead to do similar parsing, WDYT? I'm okay either way, since the most common use case would be copy pasting the output from
Contributor
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. I'd prefer to leave the task parsing the same; less work on our part, and less risk of a regression in existing parts of the code base.
Contributor
Author
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. I found something interesting when experimenting with this. When passing a request body to the the position While this particular case is easily handled by simply accepting Furthermore, just checking whether the offset position is an instance of
Contributor
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. Ah, nice catch! I noticed the discrepancy in numeric types while working on KAFKA-15177 but hadn't even considered the possibility of aligning the types across invocations of I think re-deserializing the offsets before passing them to I still don't love permitting string types for the connector's
Contributor
Author
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. Since the type alignment issue seemed like a broader one (i.e. not scoped to the file connectors being touched here), I've created a separate ticket and PR for it.
I'd argue that it isn't really a workaround and that the current check itself is bad. If the (de)serialization happened to use
Contributor
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. Hmmm... wouldn't that be a pretty serious breaking change if we accidentally switched up how the JSON converter deserializes integer types? Not just for the file source connector, but for plenty of others. It feels like it might be a better use of our time to make note of this possibility and ensure that we have sufficient unit testing in place to prevent that kind of regression (I suspect we already do but haven't verified this yet). Of course, because things aren't interesting enough already--it turns out that there's actually two different scenarios in which tasks observe offsets for their connector. The first, which we're all familiar with, is when they query them using an OffsetStorageReader, which in distributed mode reflects the contents of the offsets topic. The second is when SourceTask::commitRecord is invoked, which carries with it the just-ack'd I don't know if this significantly changes the conversation but it seems subtle and counterintuitive enough to bring up so that we can avoid accidentally breaking connector code that relies on this behavior.
Contributor
Author
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.
Okay, that's fair enough, I've changed the check in
Hm yeah, that's definitely another interesting one to bring up - however, I'd contend that that one kinda makes sense since we're passing the Since the offsets being altered externally would correspond to the ones that the connector / tasks read at startup, I think it makes sense to align the types across invocations to
Contributor
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. 💯 Sounds good, thanks for thinking this through. |
||
| if (offsetPosition < 0) { | ||
| throw new ConnectException("The value for the '" + POSITION_FIELD + "' key in the offset should be a non-negative number"); | ||
| } | ||
| } catch (NumberFormatException e) { | ||
| throw new ConnectException("The value for the '" + POSITION_FIELD + "' key in the offset should be a number"); | ||
| } | ||
| } | ||
|
|
||
| // Let the task check whether the actual value for the offset position is valid for the configured file on startup | ||
| return true; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.