Skip to content

Conversation

@Tim-Brooks
Copy link
Contributor

@Tim-Brooks Tim-Brooks commented Nov 27, 2017

This is related to #27563. In order to interface with java nio, we must
have buffers that are compatible with ByteBuffer. This commit introduces
a basic ByteBufferReference to easily allow transferring bytes off the
wire to usage in the application.

Additionally it introduces an InboundChannelBuffer. This is a buffer
that can internally expand as more space is needed. It is designed to
be integrated with a page recycler so that it can internally reuse pages.
The final piece is moving all of the index work for writing bytes to a
channel into the WriteOperation.

@Tim-Brooks
Copy link
Contributor Author

This is mostly proof of conception. I still need to add some more tests. But I wanted to see if this is more in the direction we wanted to go. @s1monw

In relation to this #27221, the InboundChannelBuffer is the piece that will be hooked up to the PageRecycler. You will see that right now when you request more bytes it is just allocating more bytes. But obviously, in the future it would reuse pages.

OutboundChannelBytes really just wraps an array of ByteBuffers and provides an index into what has been written. It does not really do anything else.

@Tim-Brooks
Copy link
Contributor Author

@s1monw After thinking about this a little bit, I think that the work from OutboundChannelBytes could probably be moved to the WriteOperation. As the WriteOperation is kind of a discrete thing (it is eventually done) I think it is probably alright to keep indexes and offsets and things like that in it. Just let me know what you think.

@Tim-Brooks Tim-Brooks changed the title Introduce inbound and outbound nio bytes Introduce resizable inbound byte buffer Nov 28, 2017
@Tim-Brooks
Copy link
Contributor Author

@s1monw I went ahead and moved all of the work related to writing bytes into the WriteOperation.

Copy link
Contributor

@s1monw s1monw left a comment

Choose a reason for hiding this comment

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

I like where this is going, I would appreciate if you could put more inline comment etc in so it would be easier to read


import java.nio.ByteBuffer;

public class ByteBufferReference extends BytesReference {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we have some javadocs?

}
}

public void releasePagesFromHead(int bytesToRelease) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we call it just release and always make it implicit that it's on the front?

@Override
public BytesReference slice(int from, int length) {
if (from < 0 || (from + length) > this.length) {
throw new IllegalArgumentException("can't slice a buffer with length [" + this.length + "], with slice parameters from ["
Copy link
Contributor

Choose a reason for hiding this comment

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

you can rather throw and IndexOutOfBoundsException here?


public ByteBuffer[] getPreIndexBuffers() {
if (internalIndex == 0) {
return new ByteBuffer[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe assign this to a static constant?

offset = indexInPage(bytesToRelease + offset);
}

public ByteBuffer[] getPreIndexBuffers() {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you document this, I am having trouble to understand waht that means

private final Supplier<Page> pageSupplier;

private int capacity = 0;
private int internalIndex = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

should we rather use longs here since we can grow beyond an array an if not can we add safety guards to ensure it never does?

return buffers;
}

public ByteBuffer[] getPostIndexBuffers() {
Copy link
Contributor

Choose a reason for hiding this comment

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

same here, I need docs.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it would be easier to have a single method that allows you to get a slice like ByteBuffer[] slice(long start, long length); then the caller can decide what is needed? this would remove the need for two methods...

return buffers;
}

public void incrementIndex(int delta) {
Copy link
Contributor

Choose a reason for hiding this comment

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

does it only go forward? should we enforce it is positive?

return capacity - internalIndex;
}

private int numPages(int capacity) {
Copy link
Contributor

Choose a reason for hiding this comment

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

up to you but maybe inline all these tiny methods? really up to you

return index & pageMask;
}

public static class Page implements Releasable {
Copy link
Contributor

Choose a reason for hiding this comment

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

is this a preparation for the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. As a different Supplier would need access. But I'll go ahead and change it to private right now. And can adjust in whenever the "recycling" pr lands.

@Tim-Brooks
Copy link
Contributor Author

@s1monw I made some changes to address your review comments.

@s1monw s1monw self-requested a review December 4, 2017 21:55
Copy link
Contributor

@s1monw s1monw left a comment

Choose a reason for hiding this comment

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

I did another pass but we are quite close. Looks good

* the pages internally. If more space is needed at the end of the buffer {@link #ensureCapacity(long)} can
* be called and the buffer will expand using the supplier provided.
*/
public class InboundChannelBuffer {
Copy link
Contributor

Choose a reason for hiding this comment

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

can this be final? and pkg private?

return buffers;
}

public ByteBuffer[] getPostIndexBuffers() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it would be easier to have a single method that allows you to get a slice like ByteBuffer[] slice(long start, long length); then the caller can decide what is needed? this would remove the need for two methods...

private int indexInPage(int index) {
return index & pageMask;
private int indexInPage(long index) {
return (int) (index & pageMask);
Copy link
Contributor

Choose a reason for hiding this comment

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

I usually use Math.toIntExact() for this just to have extra protection but I think it's ok here


private long capacity = 0;
private long internalIndex = 0;
private int offset = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

this is the offset into the last buffer? can you leave a comment on it why it's an int

if (last == null || last.hasWriteRemaining() == false) {
this.references.add(NetworkBytesReference.wrap(new BytesArray(new byte[DEFAULT_READ_LENGTH])));
if (channelBuffer.getRemaining() == 0) {
channelBuffer.ensureCapacity(channelBuffer.getCapacity() + InboundChannelBuffer.PAGE_SIZE);
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure if InboundChannelBuffer.PAGE_SIZE should be externally visible. Usually the contract of ensureCapacity is to guarantee some kind of capacity. it's up the the impl to ensure that we overallocate with certain page sizes. I think this call should be like this channelBuffer.ensureCapacity(channelBuffer.getCapacity() + 1) and internally we can make sure we overallocate?

Copy link
Contributor

Choose a reason for hiding this comment

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

then InboundChannelBuffer.PAGE_SIZE can be private

}

int pagesToRelease = pageIndex(offset + bytesToRelease);
for (int i = 0; i < pagesToRelease; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

s/++i/i++

*/
public ByteBuffer[] getPostIndexBuffers() {
if (internalIndex == capacity) {
return new ByteBuffer[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

use the constant?

return (int) (index & pageMask);
}

private static class Page implements Releasable {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we maybe remove this class all-together since we don't use it and can we introduce it once it's needed?

public static final int PAGE_SIZE = 1 << 14;
private static final ByteBuffer[] EMPTY_BYTE_BUFFER_ARRAY = new ByteBuffer[0];

private final int pageMask;
Copy link
Contributor

Choose a reason for hiding this comment

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

both pageMask and pageShift can be constants right?

private final int pageMask;
private final int pageShift;

private final ArrayDeque<Page> pages;
Copy link
Contributor

Choose a reason for hiding this comment

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

perfect application for an ArrayDeque!

@Tim-Brooks
Copy link
Contributor Author

@s1monw I have pushed changes to address your review comments.

Copy link
Contributor

@s1monw s1monw left a comment

Choose a reason for hiding this comment

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

LGTM left one suggestion

return capacity;
}

public long getRemaining() {
Copy link
Contributor

Choose a reason for hiding this comment

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

just a suggestion, can we assert that this is actually >= 0?

@Tim-Brooks Tim-Brooks merged commit 2aa62da into elastic:master Dec 6, 2017
Tim-Brooks added a commit that referenced this pull request Dec 6, 2017
This is related to #27563. In order to interface with java nio, we must
have buffers that are compatible with ByteBuffer. This commit introduces
a basic ByteBufferReference to easily allow transferring bytes off the
wire to usage in the application.

Additionally it introduces an InboundChannelBuffer. This is a buffer
that can internally expand as more space is needed. It is designed to
be integrated with a page recycler so that it can internally reuse pages.
The final piece is moving all of the index work for writing bytes to a
channel into the WriteOperation.
Tim-Brooks added a commit to Tim-Brooks/elasticsearch that referenced this pull request Dec 7, 2017
This is a followup to elastic#27551. That commit introduced a bug where the
incorrect byte buffers would be returned when we attempted a write. This
commit fixes the logic.
Tim-Brooks added a commit that referenced this pull request Dec 7, 2017
This is a followup to #27551. That commit introduced a bug where the
incorrect byte buffers would be returned when we attempted a write. This
commit fixes the logic.
Tim-Brooks added a commit that referenced this pull request Dec 7, 2017
This is a followup to #27551. That commit introduced a bug where the
incorrect byte buffers would be returned when we attempted a write. This
commit fixes the logic.
@Tim-Brooks Tim-Brooks deleted the buffer_reuse branch December 10, 2018 16:19
@jimczi jimczi added v7.0.0-beta1 and removed v7.0.0 labels Feb 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants