-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Updated CompositeByteBuf to be responsible for retaining its ByteBufs #1825
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
d3361a9
b3d6b91
a6fb3f9
82fdb75
b141f00
943be1e
23475ba
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 |
|---|---|---|
|
|
@@ -149,48 +149,43 @@ public final class CommandMessage extends RequestMessage { | |
| * `PAYLOAD_TYPE_1_DOCUMENT_SEQUENCE` sections. | ||
| */ | ||
| BsonDocument getCommandDocument(final ByteBufferBsonOutput bsonOutput) { | ||
| List<ByteBuf> byteBuffers = bsonOutput.getByteBuffers(); | ||
| CompositeByteBuf byteBuf = new CompositeByteBuf(bsonOutput.getByteBuffers()); | ||
| try { | ||
| CompositeByteBuf byteBuf = new CompositeByteBuf(byteBuffers); | ||
| try { | ||
| byteBuf.position(firstDocumentPosition); | ||
| ByteBufBsonDocument byteBufBsonDocument = createOne(byteBuf); | ||
|
|
||
| // If true, it means there is at least one `PAYLOAD_TYPE_1_DOCUMENT_SEQUENCE` section in the OP_MSG | ||
| if (byteBuf.hasRemaining()) { | ||
| BsonDocument commandBsonDocument = byteBufBsonDocument.toBaseBsonDocument(); | ||
|
|
||
| // Each loop iteration processes one Document Sequence | ||
| // When there are no more bytes remaining, there are no more Document Sequences | ||
| while (byteBuf.hasRemaining()) { | ||
| // skip reading the payload type, we know it is `PAYLOAD_TYPE_1` | ||
| byteBuf.position(byteBuf.position() + 1); | ||
| int sequenceStart = byteBuf.position(); | ||
| int sequenceSizeInBytes = byteBuf.getInt(); | ||
| int sectionEnd = sequenceStart + sequenceSizeInBytes; | ||
|
|
||
| String fieldName = getSequenceIdentifier(byteBuf); | ||
| // If this assertion fires, it means that the driver has started using document sequences for nested fields. If | ||
| // so, this method will need to change in order to append the value to the correct nested document. | ||
| assertFalse(fieldName.contains(".")); | ||
|
|
||
| ByteBuf documentsByteBufSlice = byteBuf.duplicate().limit(sectionEnd); | ||
| try { | ||
| commandBsonDocument.append(fieldName, new BsonArray(createList(documentsByteBufSlice))); | ||
| } finally { | ||
| documentsByteBufSlice.release(); | ||
| } | ||
| byteBuf.position(sectionEnd); | ||
| byteBuf.position(firstDocumentPosition); | ||
| ByteBufBsonDocument byteBufBsonDocument = createOne(byteBuf); | ||
|
|
||
| // If true, it means there is at least one `PAYLOAD_TYPE_1_DOCUMENT_SEQUENCE` section in the OP_MSG | ||
| if (byteBuf.hasRemaining()) { | ||
| BsonDocument commandBsonDocument = byteBufBsonDocument.toBaseBsonDocument(); | ||
|
|
||
| // Each loop iteration processes one Document Sequence | ||
| // When there are no more bytes remaining, there are no more Document Sequences | ||
| while (byteBuf.hasRemaining()) { | ||
| // skip reading the payload type, we know it is `PAYLOAD_TYPE_1` | ||
| byteBuf.position(byteBuf.position() + 1); | ||
| int sequenceStart = byteBuf.position(); | ||
| int sequenceSizeInBytes = byteBuf.getInt(); | ||
| int sectionEnd = sequenceStart + sequenceSizeInBytes; | ||
|
|
||
| String fieldName = getSequenceIdentifier(byteBuf); | ||
| // If this assertion fires, it means that the driver has started using document sequences for nested fields. If | ||
| // so, this method will need to change in order to append the value to the correct nested document. | ||
| assertFalse(fieldName.contains(".")); | ||
|
|
||
| ByteBuf documentsByteBufSlice = byteBuf.duplicate().limit(sectionEnd); | ||
| try { | ||
| commandBsonDocument.append(fieldName, new BsonArray(createList(documentsByteBufSlice))); | ||
| } finally { | ||
| documentsByteBufSlice.release(); | ||
| } | ||
| return commandBsonDocument; | ||
| } else { | ||
| return byteBufBsonDocument; | ||
| byteBuf.position(sectionEnd); | ||
| } | ||
| } finally { | ||
| byteBuf.release(); | ||
| return commandBsonDocument; | ||
| } else { | ||
| return byteBufBsonDocument; | ||
| } | ||
| } finally { | ||
| byteBuffers.forEach(ByteBuf::release); | ||
|
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. To understand whether removing A reader may omit everything from here and to "TL;DR". So now it is necessary to figure out / establish the aforementioned semantics of
Thus, deriving the semantics of
TL;DR Given the above, I find it virtually impossible to reason about the correctness of both the code before the PR, and the proposed changes. So I added sample tests (I am not suggesting that their current locations is the right one, nor that they should be added to the codebase):
Ideally, we should:
I am not insisting on us doing that now, but nor do I see how to approve the current PR: one one hand, it definitely solves something, on the other hand, I have no idea if it breaks something else, and how bad the effect may be. Some sample tests definitely behave differently when run against (1) Netty calls such buffers "derived", and they have the following semantics: "a parent buffer and its derived buffers share the same reference count, and the reference count does not increase when a derived buffer is created". |
||
| byteBuf.release(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |
| import java.util.concurrent.atomic.AtomicInteger; | ||
|
|
||
| import static java.lang.String.format; | ||
| import static com.mongodb.assertions.Assertions.assertTrue; | ||
| import static org.bson.assertions.Assertions.isTrueArgument; | ||
| import static org.bson.assertions.Assertions.notNull; | ||
|
|
||
|
|
@@ -49,8 +50,12 @@ class CompositeByteBuf implements ByteBuf { | |
| limit = components.get(components.size() - 1).endOffset; | ||
| } | ||
|
|
||
| CompositeByteBuf(final CompositeByteBuf from) { | ||
| components = from.components; | ||
| private CompositeByteBuf(final CompositeByteBuf from) { | ||
| notNull("from", from); | ||
| components = new ArrayList<>(from.components.size()); | ||
| from.components.forEach(component -> | ||
strogiyotec marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| components.add(new Component(component.buffer.duplicate(), component.offset)) | ||
| ); | ||
| position = from.position(); | ||
| limit = from.limit(); | ||
| } | ||
|
|
@@ -306,15 +311,22 @@ public ByteBuf retain() { | |
| referenceCount.decrementAndGet(); | ||
| throw new IllegalStateException("Attempted to increment the reference count when it is already 0"); | ||
| } | ||
| components.forEach(c -> c.buffer.retain()); | ||
| return this; | ||
| } | ||
|
|
||
| @Override | ||
| public void release() { | ||
| if (referenceCount.decrementAndGet() < 0) { | ||
| int decrementedReferenceCount = referenceCount.decrementAndGet(); | ||
| if (decrementedReferenceCount < 0) { | ||
| referenceCount.incrementAndGet(); | ||
| throw new IllegalStateException("Attempted to decrement the reference count below 0"); | ||
| } | ||
| components.forEach(c -> c.buffer.release()); | ||
| if (decrementedReferenceCount == 0) { | ||
| assertTrue(components.stream().noneMatch(c -> c.buffer.getReferenceCount() > 0), | ||
| "All component buffers should have reference count 0 when CompositeByteBuf is fully released, but some still have references."); | ||
|
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. This assertion combined with the fact that |
||
| } | ||
| } | ||
|
|
||
| private Component findComponent(final int index) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.