Skip to content
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

ByteBufBsonDocument remains a ByteBuf #1119

Merged
merged 1 commit into from
Jun 13, 2023
Merged

ByteBufBsonDocument remains a ByteBuf #1119

merged 1 commit into from
Jun 13, 2023

Conversation

jyemin
Copy link
Collaborator

@jyemin jyemin commented May 17, 2023

This one got a bit complicated. The root cause of the bug is that ByteBufBsonDocument#entrySet hydrates its byte buffer into the base class's hash map instead of implementing the entry set via iterating over the raw BSON. To avoid this hydration completely, including for nested documents and arrays, requires quite a few code changes, including the introduction of a new class, ByteBufBsonArray, that does the same job for nested arrays.

JAVA-4917

@jyemin jyemin self-assigned this May 17, 2023
@jyemin jyemin force-pushed the j4917 branch 5 times, most recently from 3d439c8 to 183edf6 Compare May 22, 2023 02:40
@jyemin jyemin changed the title Command logs don't reflect actual command/reply documents ByteBufBsonDocument remains a ByteBuf May 22, 2023
* Ensure that ByteBufBsonDocument never hydrates itself into a HashMap
* Add ByteBufBsonArray and ensure it never hydrates itself into an ArrayList

JAVA-4917
@@ -237,7 +237,7 @@ public int hashCode() {
@Override
public String toString() {
return "BsonArray{"
+ "values=" + values
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To allow subclasses to override #getValues

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To simplify the design, this class was merged into the only actual subclass, `ByteBufBsonDocument``


import static com.mongodb.internal.connection.ByteBufBsonHelper.readBsonValue;

public class ByteBufBsonArray extends BsonArray {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A lot of the implementation of the overridden methods in this class were adapted from the JDKs abstract collection classes. Unfortunately, we can't just call the existing methods directly due to the fact that we have to extend BsonArray rather than, say, AbstractList. This is due to a design flaw in the driver. Really, BsonArray and BsonDocument should have been interfaces, but because they are classes we are forced to extend them rather than implement them, and the subclasses (like this one) have to do a lot of work to ensure that the base class implementations are not used (effectively treating them as interfaces or pure abstract classes.


@Override
public ListIterator<BsonValue> listIterator(final int index) {
// Not the most efficient way to do this, but unlikely anyone will notice in practice
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think it's worth investing in a more efficient ListIterator implementation. Ultimately, it's going to have to hydrate the array, so it's not going to be much better than this for most use cases.

private static final long serialVersionUID = 2L;

private final transient ByteBuf byteBuf;

static List<ByteBufBsonDocument> createList(final ResponseBuffers responseBuffers) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unused method

byteBuf.get(byteBuf.position(), clonedBytes);
return new RawBsonDocument(clonedBytes);
}

@Nullable
<T> T findInDocument(final Finder<T> finder) {
ByteBuf duplicateByteBuf = byteBuf.duplicate();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The double duplication of the byte buf was unintentional, but caused a bug once the hydration behavior was changed

bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
T found = finder.find(bsonReader);
T found = finder.find(duplicateByteBuf, bsonReader);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Now we need the byte buf sent to the finder to avoid hydration of nested documents/arrays

@jyemin jyemin requested review from stIncMale and vbabanin June 1, 2023 14:27
@jyemin jyemin marked this pull request as ready for review June 1, 2023 14:28
return values;
}

private static final String READ_ONLY_MESSAGE = "This BsonArray instance is read-only";
Copy link
Member

Choose a reason for hiding this comment

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

nit: static final can be moved to the top of the class


@Override
public int size() {
int size = 0;
Copy link
Member

@vbabanin vbabanin Jun 7, 2023

Choose a reason for hiding this comment

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

Since the ByteBufBsonArray is read-only and its size doesn't change after initialization, we can consider storing the size value after first call to avoid additional computations during each size() call.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's true, and I did consider a few optimizations like this but decided to keep it simple.

@stIncMale stIncMale requested review from rozza and removed request for stIncMale June 8, 2023 14:38
Copy link
Member

@rozza rozza left a comment

Choose a reason for hiding this comment

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

LGTM

Could / should RawBsonDocument now delegate to ByteBufBsonDocument internally?

Would save two very similar document types.

@SuppressWarnings("unchecked")
public <T> T[] toArray(final T[] a) {
int size = size();
T[] retVal = a.length >= size ? a : (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
Copy link
Member

Choose a reason for hiding this comment

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

Wow - that's gnarly

@jyemin
Copy link
Collaborator Author

jyemin commented Jun 13, 2023

Could / should RawBsonDocument now delegate to ByteBufBsonDocument internally

Added JAVA-5023 to consider this

@jyemin jyemin merged commit 2468e98 into mongodb:master Jun 13, 2023
@jyemin jyemin deleted the j4917 branch June 13, 2023 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants