aws / aws/aws-dotnet-messaging
Reduce Allocations in Envelope Serialization
- Dominant language
- C#
- Stars
- 143
- Forks
- 27
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 4
Description
### Describe the feature
The current implementation of `EnvelopeSerializer` utilizes `JsonNode` for message serialization. While functional, this approach introduces unnecessary allocations and increased memory usage, particularly in high-throughput scenarios. There is an opportunity to significantly reduce allocations and improve performance by leveraging `Utf8JsonWriter` over rented buffers, thereby enabling buffer reuse and minimizing memory overhead per message.
### Use Case
Serialization of any enveloped payload passing through the library.
### Proposed Solution
### Serialization Optimization
#### Replace `JsonNode` Usage:
Transition the serialization process from `JsonNode` to `Utf8JsonWriter`, utilizing rented memory buffers. This change will reduce object allocations and improve memory management, especially under heavy load.
#### Interface Compatibility:
To maintain backward compatibility, the `EnvelopeSerializer.SerializeAsync` method should continue to return a string. In the future, further optimizations could be realized by returning a buffer directly, but such a change would introduce a breaking change to the API.
#### Potential for Future Improvements:
Passing a buffer to `EnvelopeSerializer.SerializeAsync` would allow dropping string allocations. With possible long term solution entirely working on rented buffers only across all layers.
### Interface Enhancements
#### Introduce Non-Allocating Serialization:
The current `IMessageSerializer` interface’s `MessageSerializerResult Serialize` method is incompatible with non-allocating serialization scenarios. To address this, a new method is proposed:
`
ValueTask SerializeToBufferAsync(Utf8JsonWriter writer, object? value, ...);
`
where return value is content type.
* Note: Evaluate whether it is feasible to utilize IBufferedWriter (to avoid tight coupling to Utf8JsonWriter) without disrupting the typical Utf8JsonWriter usage patterns.
#### Incremental Interface Introduction:
To ensure a smooth transition, introduce a new interface, `IJsonWriterMessageSerializer`, which derives from `IMessageSerializer` and adds the proposed non-allocating serialization method. This approach allows incremental adoption without breaking existing consumers.
`
public interface IJsonWriterMessageSerializer : IMessageSerializer
{
ValueTask SerializeToBufferAsync(Utf8JsonWriter writer, object? value, ...);
}
`
#### Runtime Detection and Adaptation:
Serialization code should detect at runtime whether a serializer implements `IJsonWriterMessageSerializer`. If so, it should delegate to the new method. This introduces a minimal runtime overhead (an additional type check) but enables immediate performance gains for updated serializers.
### Other Information
PoC with expectable savings stored in https://github.com/aws/aws-dotnet-messaging/pull/272
PoC solution cleans all rented memory. This adds some overhead but keeps the solution safer.
### Acknowledgements
- [x] I may be able to implement this feature request
- [ ] This feature might incur a breaking change
### AWS.Messaging (or related) package versions
AWS.Messaging 1.0.1
### Targeted .NET Platform
.NET 8
### Operating System and version
any
Contributor guide
Research direction
Start with EnvelopeSerializer and the existing IMessageSerializer contract, then review the referenced PoC in pull request 272. Compare the proposed IJsonWriterMessageSerializer and Utf8JsonWriter approach with the backward-compatible SerializeAsync API. Done means reduced allocations with rented buffers, runtime support for updated serializers, and preserved existing consumers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100