Skip to content

Commit b0e2bdf

Browse files
stIncMalevbabaninjyemin
authored
Implement splitting and encoding ops, nsInfo as separate OP_MSG sections, implement prose tests (#1495)
JAVA-5529 JAVA-5610 JAVA-5695 --------- Co-authored-by: Viacheslav Babanin <[email protected]> Co-authored-by: Jeff Yemin <[email protected]>
1 parent fc65ad9 commit b0e2bdf

File tree

51 files changed

+2702
-1269
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2702
-1269
lines changed

bson/src/main/org/bson/AbstractBsonWriter.java

+9
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,15 @@ protected void throwInvalidState(final String methodName, final State... validSt
748748
methodName, validStatesString, state));
749749
}
750750

751+
/**
752+
* {@inheritDoc}
753+
* <p>
754+
* The {@link #flush()} method of {@link AbstractBsonWriter} does nothing.</p>
755+
*/
756+
@Override
757+
public void flush() {
758+
}
759+
751760
@Override
752761
public void close() {
753762
closed = true;

bson/src/main/org/bson/BSONCallbackAdapter.java

-5
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ protected BSONCallbackAdapter(final BsonWriterSettings settings, final BSONCallb
3939
this.bsonCallback = bsonCallback;
4040
}
4141

42-
@Override
43-
public void flush() {
44-
//Looks like should be no-op?
45-
}
46-
4742
@Override
4843
public void doWriteStartDocument() {
4944
BsonContextType contextType = getState() == State.SCOPE_DOCUMENT

bson/src/main/org/bson/BsonBinaryWriter.java

-4
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ public BsonBinaryWriterSettings getBinaryWriterSettings() {
108108
return binaryWriterSettings;
109109
}
110110

111-
@Override
112-
public void flush() {
113-
}
114-
115111
@Override
116112
protected Context getContext() {
117113
return (Context) super.getContext();

bson/src/main/org/bson/BsonDocumentWriter.java

-4
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,6 @@ public void doWriteUndefined() {
194194
write(new BsonUndefined());
195195
}
196196

197-
@Override
198-
public void flush() {
199-
}
200-
201197
@Override
202198
protected Context getContext() {
203199
return (Context) super.getContext();

bson/src/main/org/bson/io/OutputBuffer.java

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ public void write(final byte[] b) {
4141
public void close() {
4242
}
4343

44+
/**
45+
* {@inheritDoc}
46+
* <p>
47+
* The {@link #flush()} method of {@link OutputBuffer} does nothing.</p>
48+
*/
49+
@Override
50+
public void flush() throws IOException {
51+
super.flush();
52+
}
53+
4454
@Override
4555
public void write(final byte[] bytes, final int offset, final int length) {
4656
writeBytes(bytes, offset, length);

driver-core/src/main/com/mongodb/internal/connection/AsyncConnection.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ <T> void commandAsync(String database, BsonDocument command, FieldNameValidator
5050

5151
<T> void commandAsync(String database, BsonDocument command, FieldNameValidator commandFieldNameValidator,
5252
@Nullable ReadPreference readPreference, Decoder<T> commandResultDecoder,
53-
OperationContext operationContext, boolean responseExpected, @Nullable SplittablePayload payload,
54-
@Nullable FieldNameValidator payloadFieldNameValidator, SingleResultCallback<T> callback);
53+
OperationContext operationContext, boolean responseExpected, MessageSequences sequences, SingleResultCallback<T> callback);
5554

5655
void markAsPinned(Connection.PinningMode pinningMode);
5756
}

driver-core/src/main/com/mongodb/internal/connection/BsonWriterHelper.java

+154-16
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,56 @@
1616

1717
package com.mongodb.internal.connection;
1818

19+
import com.mongodb.internal.connection.DualMessageSequences.EncodeDocumentsResult;
20+
import com.mongodb.internal.connection.DualMessageSequences.WritersProviderAndLimitsChecker;
21+
import com.mongodb.lang.Nullable;
22+
import org.bson.BsonBinaryWriter;
23+
import org.bson.BsonBinaryWriterSettings;
24+
import org.bson.BsonContextType;
1925
import org.bson.BsonDocument;
2026
import org.bson.BsonElement;
2127
import org.bson.BsonMaximumSizeExceededException;
2228
import org.bson.BsonValue;
2329
import org.bson.BsonWriter;
30+
import org.bson.BsonWriterSettings;
31+
import org.bson.FieldNameValidator;
2432
import org.bson.codecs.BsonValueCodecProvider;
25-
import org.bson.codecs.Codec;
33+
import org.bson.codecs.Encoder;
2634
import org.bson.codecs.EncoderContext;
2735
import org.bson.codecs.configuration.CodecRegistry;
2836
import org.bson.io.BsonOutput;
2937

3038
import java.util.List;
3139

40+
import static com.mongodb.assertions.Assertions.assertTrue;
41+
import static com.mongodb.internal.connection.DualMessageSequences.WritersProviderAndLimitsChecker.WriteResult.FAIL_LIMIT_EXCEEDED;
42+
import static com.mongodb.internal.connection.DualMessageSequences.WritersProviderAndLimitsChecker.WriteResult.OK_LIMIT_NOT_REACHED;
43+
import static com.mongodb.internal.connection.DualMessageSequences.WritersProviderAndLimitsChecker.WriteResult.OK_LIMIT_REACHED;
44+
import static com.mongodb.internal.connection.MessageSettings.DOCUMENT_HEADROOM_SIZE;
3245
import static java.lang.String.format;
3346
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
3447

35-
final class BsonWriterHelper {
36-
private static final int DOCUMENT_HEADROOM = 1024 * 16;
48+
/**
49+
* This class is not part of the public API and may be removed or changed at any time.
50+
*/
51+
public final class BsonWriterHelper {
3752
private static final CodecRegistry REGISTRY = fromProviders(new BsonValueCodecProvider());
3853
private static final EncoderContext ENCODER_CONTEXT = EncoderContext.builder().build();
3954

40-
static void writeElements(final BsonWriter writer, final List<BsonElement> bsonElements) {
41-
for (BsonElement bsonElement : bsonElements) {
42-
writer.writeName(bsonElement.getName());
43-
getCodec(bsonElement.getValue()).encode(writer, bsonElement.getValue(), ENCODER_CONTEXT);
55+
static void appendElementsToDocument(
56+
final BsonOutput bsonOutputWithDocument,
57+
final int documentStartPosition,
58+
@Nullable final List<BsonElement> bsonElements) {
59+
if ((bsonElements == null) || bsonElements.isEmpty()) {
60+
return;
61+
}
62+
try (AppendingBsonWriter writer = new AppendingBsonWriter(bsonOutputWithDocument, documentStartPosition)) {
63+
for (BsonElement element : bsonElements) {
64+
String name = element.getName();
65+
BsonValue value = element.getValue();
66+
writer.writeName(name);
67+
encodeUsingRegistry(writer, value);
68+
}
4469
}
4570
}
4671

@@ -65,16 +90,86 @@ static void writePayload(final BsonWriter writer, final BsonOutput bsonOutput, f
6590
}
6691

6792
if (payload.getPosition() == 0) {
68-
throw new BsonMaximumSizeExceededException(format("Payload document size is larger than maximum of %d.",
69-
payloadSettings.getMaxDocumentSize()));
93+
throw createBsonMaximumSizeExceededException(payloadSettings.getMaxDocumentSize());
7094
}
7195
}
7296

97+
/**
98+
* @return See {@link DualMessageSequences#encodeDocuments(WritersProviderAndLimitsChecker)}.
99+
*/
100+
static EncodeDocumentsResult writeDocumentsOfDualMessageSequences(
101+
final DualMessageSequences dualMessageSequences,
102+
final int commandDocumentSizeInBytes,
103+
final BsonOutput firstOutput,
104+
final BsonOutput secondOutput,
105+
final MessageSettings messageSettings) {
106+
BsonBinaryWriter firstWriter = createBsonBinaryWriter(firstOutput, dualMessageSequences.getFirstFieldNameValidator(), null);
107+
BsonBinaryWriter secondWriter = createBsonBinaryWriter(secondOutput, dualMessageSequences.getSecondFieldNameValidator(), null);
108+
// the size of operation-agnostic command fields (a.k.a. extra elements) is counted towards `messageOverheadInBytes`
109+
int messageOverheadInBytes = 1000;
110+
int maxSizeInBytes = messageSettings.getMaxMessageSize() - (messageOverheadInBytes + commandDocumentSizeInBytes);
111+
int firstStart = firstOutput.getPosition();
112+
int secondStart = secondOutput.getPosition();
113+
int maxBatchCount = messageSettings.getMaxBatchCount();
114+
return dualMessageSequences.encodeDocuments(writeAction -> {
115+
int firstBeforeWritePosition = firstOutput.getPosition();
116+
int secondBeforeWritePosition = secondOutput.getPosition();
117+
int batchCountAfterWrite = writeAction.doAndGetBatchCount(firstWriter, secondWriter);
118+
assertTrue(batchCountAfterWrite <= maxBatchCount);
119+
int writtenSizeInBytes =
120+
firstOutput.getPosition() - firstStart
121+
+ secondOutput.getPosition() - secondStart;
122+
if (writtenSizeInBytes < maxSizeInBytes && batchCountAfterWrite < maxBatchCount) {
123+
return OK_LIMIT_NOT_REACHED;
124+
} else if (writtenSizeInBytes > maxSizeInBytes) {
125+
firstOutput.truncateToPosition(firstBeforeWritePosition);
126+
secondOutput.truncateToPosition(secondBeforeWritePosition);
127+
if (batchCountAfterWrite == 1) {
128+
// we have failed to write a single document
129+
throw createBsonMaximumSizeExceededException(messageSettings.getMaxDocumentSize());
130+
}
131+
return FAIL_LIMIT_EXCEEDED;
132+
} else {
133+
return OK_LIMIT_REACHED;
134+
}
135+
});
136+
}
137+
138+
/**
139+
* @param messageSettings Non-{@code null} iff the document size limit must be validated.
140+
*/
141+
static BsonBinaryWriter createBsonBinaryWriter(
142+
final BsonOutput out,
143+
final FieldNameValidator validator,
144+
@Nullable final MessageSettings messageSettings) {
145+
return new BsonBinaryWriter(
146+
new BsonWriterSettings(),
147+
messageSettings == null
148+
? new BsonBinaryWriterSettings()
149+
: new BsonBinaryWriterSettings(messageSettings.getMaxDocumentSize() + DOCUMENT_HEADROOM_SIZE),
150+
out,
151+
validator);
152+
}
153+
154+
/**
155+
* Backpatches the document/message/sequence length into the beginning of the document/message/sequence.
156+
*
157+
* @param startPosition The start position of the document/message/sequence in {@code bsonOutput}.
158+
*/
159+
static void backpatchLength(final int startPosition, final BsonOutput bsonOutput) {
160+
int messageLength = bsonOutput.getPosition() - startPosition;
161+
bsonOutput.writeInt32(startPosition, messageLength);
162+
}
163+
164+
private static BsonMaximumSizeExceededException createBsonMaximumSizeExceededException(final int maxSize) {
165+
return new BsonMaximumSizeExceededException(format("Payload document size is larger than maximum of %d.", maxSize));
166+
}
167+
73168
private static boolean writeDocument(final BsonWriter writer, final BsonOutput bsonOutput, final MessageSettings settings,
74169
final BsonDocument document, final int messageStartPosition, final int batchItemCount,
75170
final int maxSplittableDocumentSize) {
76171
int currentPosition = bsonOutput.getPosition();
77-
getCodec(document).encode(writer, document, ENCODER_CONTEXT);
172+
encodeUsingRegistry(writer, document);
78173
int messageSize = bsonOutput.getPosition() - messageStartPosition;
79174
int documentSize = bsonOutput.getPosition() - currentPosition;
80175
if (exceedsLimits(settings, messageSize, documentSize, batchItemCount)
@@ -85,24 +180,25 @@ private static boolean writeDocument(final BsonWriter writer, final BsonOutput b
85180
return true;
86181
}
87182

88-
@SuppressWarnings({"unchecked"})
89-
private static Codec<BsonValue> getCodec(final BsonValue bsonValue) {
90-
return (Codec<BsonValue>) REGISTRY.get(bsonValue.getClass());
183+
static void encodeUsingRegistry(final BsonWriter writer, final BsonValue value) {
184+
@SuppressWarnings("unchecked")
185+
Encoder<BsonValue> encoder = (Encoder<BsonValue>) REGISTRY.get(value.getClass());
186+
encoder.encode(writer, value, ENCODER_CONTEXT);
91187
}
92188

93189
private static MessageSettings getPayloadMessageSettings(final SplittablePayload.Type type, final MessageSettings settings) {
94190
MessageSettings payloadMessageSettings = settings;
95191
if (type != SplittablePayload.Type.INSERT) {
96192
payloadMessageSettings = createMessageSettingsBuilder(settings)
97-
.maxDocumentSize(settings.getMaxDocumentSize() + DOCUMENT_HEADROOM)
193+
.maxDocumentSize(settings.getMaxDocumentSize() + DOCUMENT_HEADROOM_SIZE)
98194
.build();
99195
}
100196
return payloadMessageSettings;
101197
}
102198

103199
private static MessageSettings getDocumentMessageSettings(final MessageSettings settings) {
104200
return createMessageSettingsBuilder(settings)
105-
.maxMessageSize(settings.getMaxDocumentSize() + DOCUMENT_HEADROOM)
201+
.maxMessageSize(settings.getMaxDocumentSize() + DOCUMENT_HEADROOM_SIZE)
106202
.build();
107203
}
108204

@@ -126,8 +222,50 @@ private static boolean exceedsLimits(final MessageSettings settings, final int m
126222
return false;
127223
}
128224

225+
/**
226+
* A {@link BsonWriter} that allows appending key/value pairs to a document that has been fully written to a {@link BsonOutput}.
227+
*/
228+
private static final class AppendingBsonWriter extends LevelCountingBsonWriter implements AutoCloseable {
229+
private static final int INITIAL_LEVEL = DEFAULT_INITIAL_LEVEL + 1;
129230

130-
private BsonWriterHelper() {
231+
/**
232+
* @param bsonOutputWithDocument A {@link BsonOutput} {@linkplain BsonOutput#getPosition() positioned}
233+
* immediately after the end of the document.
234+
* @param documentStartPosition The {@linkplain BsonOutput#getPosition() position} of the start of the document
235+
* in {@code bsonOutputWithDocument}.
236+
*/
237+
AppendingBsonWriter(final BsonOutput bsonOutputWithDocument, final int documentStartPosition) {
238+
super(
239+
new InternalAppendingBsonBinaryWriter(bsonOutputWithDocument, documentStartPosition),
240+
INITIAL_LEVEL);
241+
}
242+
243+
@Override
244+
public void writeEndDocument() {
245+
assertTrue(getCurrentLevel() > INITIAL_LEVEL);
246+
super.writeEndDocument();
247+
}
248+
249+
@Override
250+
public void close() {
251+
try (InternalAppendingBsonBinaryWriter writer = (InternalAppendingBsonBinaryWriter) getBsonWriter()) {
252+
writer.writeEndDocument();
253+
}
254+
}
255+
256+
private static final class InternalAppendingBsonBinaryWriter extends BsonBinaryWriter {
257+
InternalAppendingBsonBinaryWriter(final BsonOutput bsonOutputWithDocument, final int documentStartPosition) {
258+
super(bsonOutputWithDocument);
259+
int documentEndPosition = bsonOutputWithDocument.getPosition();
260+
int bsonDocumentEndingSize = 1;
261+
int appendFromPosition = documentEndPosition - bsonDocumentEndingSize;
262+
bsonOutputWithDocument.truncateToPosition(appendFromPosition);
263+
setState(State.NAME);
264+
setContext(new Context(null, BsonContextType.DOCUMENT, documentStartPosition));
265+
}
266+
}
131267
}
132268

269+
private BsonWriterHelper() {
270+
}
133271
}

0 commit comments

Comments
 (0)