-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-10694: Implement zero copy for FetchSnapshot #9819
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 all commits
3f604b4
7f58dd4
0ccd53d
d2025a2
2e76f4b
6b28107
79c1209
2ead903
a3b29e7
d75d65b
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 |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.kafka.common.record; | ||
|
|
||
| import org.apache.kafka.common.network.TransferableChannel; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * Represents a record set which can be transferred to a channel | ||
| * @see Records | ||
| * @see UnalignedRecords | ||
| */ | ||
| public interface TransferableRecords extends BaseRecords { | ||
|
|
||
| /** | ||
| * Attempts to write the contents of this buffer to a channel. | ||
| * @param channel The channel to write to | ||
| * @param position The position in the buffer to write from | ||
| * @param length The number of bytes to write | ||
| * @return The number of bytes actually written | ||
| * @throws IOException For any IO errors | ||
| */ | ||
| long writeTo(TransferableChannel channel, long position, int length) throws IOException; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.kafka.common.record; | ||
|
|
||
| import org.apache.kafka.common.network.TransferableChannel; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.channels.FileChannel; | ||
|
|
||
| /** | ||
| * Represents a file record set which is not necessarily offset-aligned | ||
| */ | ||
| public class UnalignedFileRecords implements UnalignedRecords { | ||
|
|
||
| private final FileChannel channel; | ||
| private final long position; | ||
| private final int size; | ||
|
|
||
| public UnalignedFileRecords(FileChannel channel, long position, int size) { | ||
| this.channel = channel; | ||
| this.position = position; | ||
| this.size = size; | ||
| } | ||
|
|
||
| @Override | ||
| public int sizeInBytes() { | ||
| return size; | ||
| } | ||
|
|
||
| @Override | ||
| public long writeTo(TransferableChannel destChannel, long previouslyWritten, int remaining) throws IOException { | ||
|
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. Do we have test coverage for this?
Member
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. Add UnalignedFileRecordsTest
Member
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. Hmm. The semantic between the in-memory implementation vs the file channel implementation is slightly different. The in-memory version throws an exception if there are not enough bytes to send I suspect that we want to throw in both cases else the calling code won't know why
Member
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. @jsancio , I think you are right, I also find that the implementation of |
||
| long position = this.position + previouslyWritten; | ||
| long count = Math.min(remaining, sizeInBytes() - previouslyWritten); | ||
| return destChannel.transferFrom(channel, position, count); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.kafka.common.record; | ||
|
|
||
| import org.apache.kafka.common.network.TransferableChannel; | ||
| import org.apache.kafka.common.utils.Utils; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * Represents a memory record set which is not necessarily offset-aligned | ||
| */ | ||
| public class UnalignedMemoryRecords implements UnalignedRecords { | ||
|
|
||
| private final ByteBuffer buffer; | ||
|
|
||
| public UnalignedMemoryRecords(ByteBuffer buffer) { | ||
| this.buffer = Objects.requireNonNull(buffer); | ||
| } | ||
|
|
||
| public ByteBuffer buffer() { | ||
| return buffer.duplicate(); | ||
| } | ||
|
|
||
| @Override | ||
| public int sizeInBytes() { | ||
| return buffer.remaining(); | ||
| } | ||
|
|
||
| @Override | ||
| public long writeTo(TransferableChannel channel, long position, int length) throws IOException { | ||
| if (position > Integer.MAX_VALUE) | ||
| throw new IllegalArgumentException("position should not be greater than Integer.MAX_VALUE: " + position); | ||
| if (position + length > buffer.limit()) | ||
| throw new IllegalArgumentException("position+length should not be greater than buffer.limit(), position: " | ||
| + position + ", length: " + length + ", buffer.limit(): " + buffer.limit()); | ||
| return Utils.tryWriteTo(channel, (int) position, length, buffer); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.kafka.common.record; | ||
|
|
||
| /** | ||
| * Represents a record set which is not necessarily offset-aligned, and is | ||
| * only used when fetching raft snapshot | ||
| */ | ||
| public interface UnalignedRecords extends TransferableRecords { | ||
|
|
||
| @Override | ||
| default RecordsSend<? extends BaseRecords> toSend() { | ||
| return new DefaultRecordsSend<>(this, sizeInBytes()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| import java.util.TreeSet; | ||
| import org.apache.kafka.common.KafkaException; | ||
| import org.apache.kafka.common.config.ConfigException; | ||
| import org.apache.kafka.common.network.TransferableChannel; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
|
|
@@ -1077,7 +1078,7 @@ public static void readFully(FileChannel channel, ByteBuffer destinationBuffer, | |
| * | ||
| * @throws IOException If an I/O error occurs | ||
| */ | ||
| public static final void readFully(InputStream inputStream, ByteBuffer destinationBuffer) throws IOException { | ||
| public static void readFully(InputStream inputStream, ByteBuffer destinationBuffer) throws IOException { | ||
| if (!destinationBuffer.hasArray()) | ||
| throw new IllegalArgumentException("destinationBuffer must be backed by an array"); | ||
| int initialOffset = destinationBuffer.arrayOffset() + destinationBuffer.position(); | ||
|
|
@@ -1098,6 +1099,29 @@ public static void writeFully(FileChannel channel, ByteBuffer sourceBuffer) thro | |
| channel.write(sourceBuffer); | ||
| } | ||
|
|
||
| /** | ||
| * Trying to write data in source buffer to a {@link TransferableChannel}, we may need to call this method multiple | ||
| * times since this method doesn't ensure the data in the source buffer can be fully written to the destination channel. | ||
| * | ||
| * @param destChannel The destination channel | ||
| * @param position From which the source buffer will be written | ||
| * @param length The max size of bytes can be written | ||
| * @param sourceBuffer The source buffer | ||
| * | ||
| * @return The length of the actual written data | ||
| * @throws IOException If an I/O error occurs | ||
| */ | ||
| public static long tryWriteTo(TransferableChannel destChannel, | ||
|
dengziming marked this conversation as resolved.
|
||
| int position, | ||
| int length, | ||
| ByteBuffer sourceBuffer) throws IOException { | ||
|
Member
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. How about removing the IO from this method with a signature like: public static ByteBuffer relativeSlice(ByteBuffer buffer, int position, int length);The caller of this method can then |
||
|
|
||
| ByteBuffer dup = sourceBuffer.duplicate(); | ||
| dup.position(position); | ||
| dup.limit(position + length); | ||
|
Member
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 Having said that I find it strange that we want to set the absolute position and limit. Shouldn't this be set relative to the current position? For example think about
Member
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. There are many occurrences of |
||
| return destChannel.write(dup); | ||
| } | ||
|
|
||
| /** | ||
| * Write the contents of a buffer to an output stream. The bytes are copied from the current position | ||
| * in the buffer. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.