-
Notifications
You must be signed in to change notification settings - Fork 870
CBOR response unmarshallers and related protocol test #3905
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
CBOR response unmarshallers and related protocol test #3905
Conversation
@@ -98,7 +98,7 @@ private void addServiceProtocolSpecificImports() { | |||
writer.addImport(serviceName, "System.Xml"); | |||
writer.addImport(serviceName, "System.Xml.Linq"); | |||
} else if (this.serviceName.toLowerCase().contains("rpcv2")) { | |||
writer.addImport(serviceName, "AWSSDK.Extensions.CborProtocol.Internal"); | |||
writer.addImport(serviceName, "AWSSDK.Extensions.CborProtocol.Internal.Transform"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a general comment, but I don't see the "QueryCompatbile" protocol tests added here. The CBOR protocol-selection SEP says that a service using the CBOR protocol can make use of the awsquerycompatible
trait. I'm fine with adding this in a separate PR but I was wondering if that was on your radar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like these were added two weeks ago, after I added the protocol tests, will include them in the next PR.
{ | ||
return new CborUnmarshallerContext(responseStream, maintainResponseBody, response, isException, requestContext); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to override
ShouldReadEntireResponse(IWebResponseData response, bool readEntireResponse)
It is defined in the base class so I'm just wondering if you left it out intentionally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, didn't need to override it here since we are reading the response anyway for the CborReader
.
|
||
// Create a new byte array to hold only the read data. | ||
var actualBytes = new byte[totalRead]; | ||
Buffer.BlockCopy(tempBuffer, 0, actualBytes, 0, totalRead); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't tempBuffer already contain all the data you need? Why do you need to create another byte array and copy the contents of tempBuffer to actualBytes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we use ArrayPool<byte>.Shared.Rent(someSize)
it may give us a buffer of larger size, if we passed this to CborReader
it wont know that it should reading data after the size that we specified, so we have to copy only the data that we need to CborReader
.
I wasn't sure about this whole approach at the beginning since we allocate some additional memory just to pass it to CborReader
, but I did some benchmarking and even with that it still allocates less memory than JSON
, and much less than XML
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah okay, i remember running into this as well when using array pools. makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of copying the whole value can't we just wrap the byte buffer in a properly sized ReadOnlyMemory struct? That would avoid the copying?
var actualBytes = new ReadOnlyMemory<byte>(tempBuffer, 0, totalRead);
When we do that we would not return the tempBuffer
to the array pool since passed the buffer to another object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a good improvement, but I guess we should keep a reference to the tempBuffer
then return it when we dispose the Unmarshaller, otherwise we may end up with unnecessary allocations.
Will do some testing and make this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@normj Updated the final copy to use ReadOnlyMemory
and I can see some improvement in the memory allocations over the previous approach.
using System.Formats.Cbor; | ||
using System.IO; | ||
|
||
namespace AWSSDK.Extensions.CborProtocol.Internal.Transform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part of weird legacy is we never use or should never use AWSSDK
in the namespace name. For example AWSSDK.Extensions.NETCore.Setup
uses Amazon.Extension.NETCore.Setup
as the namespace. I know it is weird but I want to be consistent and not have 1% of code use a different namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating the namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the namespaces to Amazon.Extensions.CborProtocol
.
extensions/src/AWSSDK.Extensions.CborProtocol/Internal/Transform/CborSimpleTypeUnmarshaller.cs
Show resolved
Hide resolved
extensions/src/AWSSDK.Extensions.CborProtocol/Internal/Transform/CborSimpleTypeUnmarshaller.cs
Show resolved
Hide resolved
) | ||
{ | ||
reader.ReadNull(); | ||
value = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should value
be set to default(T)
so types like int
would be 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int
cannot be null
and reader.PeekState() == CborReaderState.Null
wont be true, this is related to the above comment and being extra protective.
But if we kept the the typeof
conditions then returning default(T)
will give the same result as we made sure the type is nullable, but I feel it is more clear that way (we read null
and we return null
) but since it is the same output I'm okay with changing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to default(T)
.
|
||
// Create a new byte array to hold only the read data. | ||
var actualBytes = new byte[totalRead]; | ||
Buffer.BlockCopy(tempBuffer, 0, actualBytes, 0, totalRead); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of copying the whole value can't we just wrap the byte buffer in a properly sized ReadOnlyMemory struct? That would avoid the copying?
var actualBytes = new ReadOnlyMemory<byte>(tempBuffer, 0, totalRead);
When we do that we would not return the tempBuffer
to the array pool since passed the buffer to another object.
d5ea8b4
to
241892b
Compare
{ | ||
if (!this.disposed) | ||
{ | ||
if (disposing && rentedBuffer != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The member variable rentedBuffer
is never set and is always null
because CreateCborReaderFromStream
which is static is working with a local rentedBuffer
variable. Some logic still needs to be fixed up for the ArrayPool<byte>.Shared.Return
to work here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol, how did I get some allocations improvements 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, I guess we get the benefits of ReadOnlyMemory
even if we didn't return the buffer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated it to set the private member, gonna compare it to the previous version to see if returning the memory made any difference.
241892b
to
2ece81a
Compare
} | ||
catch | ||
{ | ||
ArrayPool<byte>.Shared.Return(newBuffer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are exiting out due to an exception shouldn't we also Return
the rentedBuffer
as well?
if (streamSize.HasValue) | ||
{ | ||
// If we know the size, we can read directly into a buffer of exact size | ||
var buffer = new byte[streamSize.Value]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do the same trick as below and rent a buffer here and return as part of the dispose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying this made me think that we will have an issue if the stream size is larger than int.MaxValue
since arrays size should be an int, in JSON
and XML
where we can read chunks directly from the stream so it doesn't matter if we have a stream larger than 2GB, but here the CborReader
needs the whole buffer since it suppose to improve its performance being in-memory parser.
I'm not sure if a Cbor
payload can be larger than that, will double check.
465b51f
to
0a8c9d5
Compare
Description
CborUnmarshallerContext
andCborResponseUnmarshaller
toAWSSDK.Extensions.CborProtocol
package.CloudWatch
orSecretsManager
unmarshallers this time since we have the protocol tests unmarshallers for reviewing the generator output.Motivation and Context
DOTNET-8161
DOTNET-8162
DOTNET-8165
Testing
smithy-rpc-v2-cbor
to CloudWatch and SecretsManager modelsprotocols
, generated the marshallers/unmarshallers, and executed some requests using CBOR marshallers and unmarshallers.DRY_RUN-36078c9c-efcb-46d7-95e3-a6052e91aa33
.Screenshots (if appropriate)
Types of changes
License