Skip to content

Conversation

@franz1981
Copy link
Contributor

@franz1981 franz1981 commented Oct 7, 2025

Jackson usually configure different form of buffer pooling (see https://github.com/FasterXML/jackson-core/blob/e19615bb99f6127230793e2c7766bbb3beaee6fc/src/main/java/com/fasterxml/jackson/core/io/IOContext.java#L63) and perform a single write(byte[] b, int offset, int lengh) vs the provided output stream during serialization, using its internally pooled buffer.

In short, this enable an async write to:

  • expect a single encoded byte[]
  • size the receiver output stream exactly to it
  • save the additional copy for the async completion to happen
image

This is just an idea as the subsequent AsyncOutputStream ::asyncWrite can "backfire" performing an additional copy, see 3408cf1

see

image

@franz1981
Copy link
Contributor Author

@jamezp FYI 🙏

@franz1981 franz1981 marked this pull request as ready for review October 7, 2025 13:23
@franz1981 franz1981 requested a review from a team as a code owner October 7, 2025 13:23
@franz1981 franz1981 changed the title Draft: Save double byte[] copies on Jackson RESTEASY-3656 ResteasyJackson2Provider excessive byte[] copies Oct 7, 2025
@franz1981 franz1981 force-pushed the optimized_async_jackson_outputstream branch from 62a9ebb to 83d79d2 Compare October 7, 2025 14:22
@franz1981 franz1981 changed the title RESTEASY-3656 ResteasyJackson2Provider excessive byte[] copies [RESTEASY-3656] ResteasyJackson2Provider excessive byte[] copies Oct 7, 2025
@franz1981
Copy link
Contributor Author

franz1981 commented Oct 7, 2025

I've run locally the jackson2 provider integration tests (w debugging), to verify the number of copies are reduced; it passes and looks good 👍 @jamezp

@jamezp
Copy link
Member

jamezp commented Oct 7, 2025

The failures are not related. This is something I need to get fixed.

@jamezp
Copy link
Member

jamezp commented Oct 7, 2025

@franz1981 Is this something we should do more generally for the org.jboss.resteasy.spi.AsyncMessageBodyWriter?

In general looking at that, writing everything to memory before writing it to the async stream like it could be a potential issue for large objects.

@franz1981
Copy link
Contributor Author

franz1981 commented Oct 8, 2025

In Quarkus I have implemented a thing called AppendBuffer in the vertx output stream, for Restleasy Reactive, which:

  • allow appending encoded chunks in batches
  • once the buffer capacity (sum of all chunks) is exhausted, it flush it to http as chunked response, and keep on filling it with the rest of the stream

In this way you both have batchy flush and some predictable memory usage.

This specific Jackson writer here looks to be just in memory, I can tell; which as you said, could be dangerous under heavy load due to memory footprint: I've indeed found this improvement because due to the existing implementation, it was so memory hungry to appear in our profiling data in the performance lab

@franz1981 franz1981 force-pushed the optimized_async_jackson_outputstream branch from 83d79d2 to 64809e4 Compare October 8, 2025 01:56
@jamezp
Copy link
Member

jamezp commented Oct 8, 2025

Awesome, thank you. I'd be tempted to put this also in org.jboss.resteasy.core.messagebody.AsyncBufferedMessageBodyWriter

@franz1981
Copy link
Contributor Author

franz1981 commented Oct 8, 2025

Good point @jamezp yep, in theory should work similarly -although for a generic use case I would create a stream which append chunks of byte[] instead of reallocating (by copying and enlarging) the same byte[] over and over.
This would be able to skip allocating a copy, if there's a single chunk, and be more friendly if the stream is "long" and made of many small writes.
But to be fair, even this one should work as good as the existing one (saving synchronized to happen).

@jamezp
Copy link
Member

jamezp commented Oct 8, 2025

Perfect. I may copy it over and look at the other chunking separately.

Also, thank for you this. I really want to learn more about performance testing, but it seems like an art form that there either isn't a lot of information on or I'm not able to find it :)

@franz1981
Copy link
Contributor Author

Reach out to me and my team and I can help with it @jamezp no worries :)

@jamezp jamezp force-pushed the optimized_async_jackson_outputstream branch from b901034 to 65c9ce8 Compare October 13, 2025 21:08
@jamezp jamezp merged commit 2c58353 into resteasy:main Oct 13, 2025
12 checks passed
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.

2 participants