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

Support decoding empty DataBuffers for Decoders that support it #29903

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

spheenik
Copy link

Some decoders support decoding a proper payload from empty DataBuffers. One good example is the ProtobufDecoder.

This patch adds a method canDecodeEmptyDataBuffer() to the Decoder interface, allowing the Decoder to signal that it is able to do that.

In this case, the PayloadMethodArgumentResolver should pass the empty DataBuffer to the Decoder, and in all other cases behave like before.

Some decoders support decoding a proper payload from empty DataBuffers.
One good example is the ProtobufDecoder.

This patch adds a method canDecodeEmptyDataBuffer() to the Decoder
interface, allowing the Decoder to signal that it is able to do that.

In this case, the PayloadMethodArgumentResolver should pass the empty
DataBuffer to the Decoder, and in all other cases behave like before.
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 30, 2023
@spheenik
Copy link
Author

In response to the following issue: #29898

@sbrannen sbrannen added in: messaging Issues in messaging modules (jms, messaging) in: web Issues in web modules (web, webmvc, webflux, websocket) in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 31, 2023
Copy link
Contributor

@simonbasle simonbasle left a comment

Choose a reason for hiding this comment

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

this sounds reasonable. Can you please at least add some small test coverage. notably there's a ProtobufDecoderTest that could have a testDecodeEmptyBuffer method for this purpose

@simonbasle simonbasle added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 6, 2023
@spheenik
Copy link
Author

spheenik commented Feb 6, 2023

I updated the remaining Decoders that are capable to decode empty buffers to also return true on canDecodeEmptyDataBuffers().
I also added tests for the decode() and the decodeToMono() scenarios, and made sure all the marked Decoders support it properly.

The `filterNonEmptyDataBuffer()` method was added via the fix for spring-projectsgh-26344. The comment states that this fix was done because for some Decoders, an empty input may not yield a valid message. Since this is now fixed by asking the decoder whether it can decode an empty DataBuffer, the test is now able to yield a value from empty input.
Copy link
Contributor

@rstoyanchev rstoyanchev left a comment

Choose a reason for hiding this comment

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

Thanks for raising this issue, and submitting a PR.

A Decoder has 3 kinds of methods, decoding to Flux, Mono, or a value. The Flux and Mono take a Publisher of buffers each of which could be a chunk of data, and not necessarily a complete message (e.g. parsing HTTP request body). In that case, empty data buffers (chunks) are okay, and does not mean the aggregated message in the end will be empty too. Moreover, since Flux and Mono methods aggregate chunks internally, they also deal with empty messages, perhaps producing a value, or completing empty, or with an error.

It's only the decode to a value method which takes a single DataBuffer and where we can apply a check before calling it. For Flux and Mono, we can do the same only when each DataBuffer is a complete message, but at that point you're processing each DataBuffer individually anyway, and might as well call the decode to value method, which is what we do for RSocket.

This is why it's quite confusing to call the method canDecodeEmptyDataBuffer. It would make more sense to call it canDecodeEmptyMessage and document very clearly that it's referring to an empty complete message and not a chunk of data, which is what each DataBuffer could be, so it really depends on the context in which the Decoder is called.

The exact semantics should also be made clear. I'm not too sure does true mean the Decoder will produce a non-null value, or that it will produce null without raising an error, or in the case of Flux and Mono that translates to producing a value, or filtering empty values, or completing with an error?

@spheenik
Copy link
Author

spheenik commented Feb 8, 2023

Thank you for the detailed explanation of the toMono-Case, and your other remarks. Seems I definitively had a misunderstanding what it is used for.

As a consequence, I renamed the method to canDecodeEmptyMessage, as suggested. I also improved on it's documentation, stating that, if the decoder returns true here, the contract will be that it will never return null when decoding an empty Message. I did not mention the error case though, which of course can happen.

I was wondering whether or not to leave the test in the toMono case in, and decided to do so, and improved on it by giving it a Flux of two empty DataBuffers as an input, to make sure a supporting decoder gives only one non-null result back.

@spheenik spheenik requested review from rstoyanchev and simonbasle and removed request for simonbasle and rstoyanchev February 8, 2023 17:03
@spheenik spheenik requested review from rstoyanchev and removed request for simonbasle February 8, 2023 17:12
@simonbasle simonbasle self-requested a review February 9, 2023 11:14
Copy link
Contributor

@simonbasle simonbasle left a comment

Choose a reason for hiding this comment

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

I'm concerned about the potential unforeseen side-effects of canDecodeEmptyMessage() == true for a lot of low-level decoders (String, Resource, buffers...). I would reduce the scope and keep most of these decoders with the default value of false, and just add support for the ProtobufDecoder for now. @rstoyanchev what do you think?

@spheenik
Copy link
Author

spheenik commented Feb 9, 2023

@simonbasle I was also thinking about this, and I can certainly see why you are worried. I came to the conclusion that as long as the decoder makes sure to always return something non null, it should be fine. All the decode- and decodeToMono-Scenarios of the ByteArray-style decoders act in this way anyway, not even looking at the flag.
For the StringDecoder, it even includes a proper fix, instead of the workaround in gh-26344.
Of course, this is only with my limited knowledge, so I am eagerly awaiting @rstoyanchev 's insight.

@spheenik
Copy link
Author

spheenik commented Feb 9, 2023

I just rechecked, the only place in the whole framework code where canDecodeEmptyMessage() is even used is in the PayloadMethodArgumentResolver in spring-messaging. Everything else is acting like before.

@simonbasle simonbasle removed their assignment Feb 28, 2023
@spheenik
Copy link
Author

It's been a while, nothing happened. To make sure, this doesn't get lost, question: Is there anything more I can do?

@snicoll
Copy link
Member

snicoll commented Sep 15, 2023

@spheenik thank you for following up. We need to take the time to review it one more time to answer your questions.

@jhoeller jhoeller removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 5, 2024
@jhoeller jhoeller added this to the 6.2.x milestone Feb 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) in: messaging Issues in messaging modules (jms, messaging) in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Messaging: PayloadMethodArgumentResolver should call certain Decoders even if the message is empty
7 participants