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

Deserialize empty payloads #455

Merged
merged 12 commits into from
Apr 3, 2024
Merged

Deserialize empty payloads #455

merged 12 commits into from
Apr 3, 2024

Conversation

mtmk
Copy link
Collaborator

@mtmk mtmk commented Mar 27, 2024

Remove the payload empty check when deserializing so that empty payloads are also passed to the deserializer for it to make the decision. This way, deserializers will have the option of what to do with the empty payload rather than just being skipped and a default value being returned. We also altered the existing default deserializers to still return a default value when the payload is empty to maintain backward compatibility. However, any custom deserializer implementations might break with this update. Applications can place a check for empty payloads or test their deserialization behavior for empty payloads:

public class MyDeserialize<T> : INatsDeserialize<T>
    public T? Deserialize(in ReadOnlySequence<byte> buffer)
    {
        if (buffer.Length == 0)
        {
            return default;
        }
        // deserializer implementation
    }
}

@mtmk mtmk linked an issue Mar 27, 2024 that may be closed by this pull request
@caleblloyd
Copy link
Collaborator

caleblloyd commented Mar 28, 2024

Should we handle this at the sterilizer level instead? Make a chain-able ignoreEmptyPayload serailizer and add that as a default to the serailizer chains we use?

I feel like the setting is tied to the serailizer, not the subscription

@mtmk
Copy link
Collaborator Author

mtmk commented Mar 28, 2024

Should we handle this at the sterilizer level instead? Make a chain-able ignoreEmptyPayload serailizer and add that as a default

good idea but would still be a breaking change. it may break custom serializers application is using.

@thinkbeforecoding
Copy link

thinkbeforecoding commented Mar 29, 2024

Is there a way to keep existing serializers as is, and be able to define new deserializers that parse empty as non null ? Like using an interface that inherit from IDeserialize...
That would not be a breaking change

@mtmk
Copy link
Collaborator Author

mtmk commented Mar 29, 2024

Is there a way to keep existing serializers as is, and be able to define new deserializers that parse empty as non null ? Like using an interface that inherit from IDeserialize...
That would not be a breaking change

genius! 🥇 ... created INatsDeserializeWithEmpty marker interface

@mtmk mtmk marked this pull request as ready for review March 29, 2024 16:48
@mtmk mtmk requested a review from caleblloyd March 29, 2024 16:48
@mtmk mtmk added the enhancement New feature or request label Mar 29, 2024
@thinkbeforecoding
Copy link

That seems perfect.

@caleblloyd
Copy link
Collaborator

For what it's worth, the Interface Design Guidelines say:

❌ AVOID using marker interfaces (interfaces with no members).
If you need to mark a class as having a specific characteristic (marker), in general, use a custom attribute rather than an interface.

Should we use a custom attribute here instead?

@caleblloyd
Copy link
Collaborator

caleblloyd commented Mar 29, 2024

Another issue I see is the way that we have our serializes chain-able would mean that this marker interface (or attribute, if changed to an attribute) would mean that the marker interface or attribute would have to be on the first serailizer in the chain. What if my chain is

NatsRawSerializer (does not serialize empty) -> CustomSerailizer (does serialize empty)

Even though my CustomSerailizer is marked, it still does not work

If we want to preserve the chain-ability, we should probably re-think the approach to either

  1. Add another property to INatsDeserialize such as HandlesEmptyPayload
  2. Always try to Deserailize, but catch certain kinds of exceptions like System.NotSupportedException and return null when the payload is empty instead of throwing. Biggest issue with this approach is different serailizers may use different exception types for this

@mtmk
Copy link
Collaborator Author

mtmk commented Mar 29, 2024

AVOID using marker interfaces

didn't know this. I see it is a workaround and somewhat misusing a feature of the language etc. at the same time I don't like to be dogmatic. if it works, it's OK! but never mind...

  1. Add another property to INatsDeserialize such as HandlesEmptyPayload
  2. Always try to Deserailize, but catch certain kinds of exceptions like System.NotSupportedException

If we're going to introduce a breaking change, maybe we should fix the issue and just remove the 'payload > 0' check and be done with it. We already are going for a breaking release, maybe just fix the issue instead of working around it. as I think @caleblloyd already suggested this, we can fix the default serializers to handle empty payloads.

WDYT?

edit: updated the implementation accordingly

@mtmk mtmk changed the title Deserialize empty payloads option Deserialize empty payloads Mar 31, 2024
@mtmk mtmk added bug Something isn't working breaking and removed enhancement New feature or request labels Mar 31, 2024
src/NATS.Client.Core/INatsSerialize.cs Outdated Show resolved Hide resolved
src/NATS.Client.Core/INatsSerialize.cs Outdated Show resolved Hide resolved
Copy link
Collaborator

@caleblloyd caleblloyd left a comment

Choose a reason for hiding this comment

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

Needs a format and a few tests failing then LGTM

@mtmk mtmk added this to the 2.2.0 milestone Apr 2, 2024
@mtmk mtmk mentioned this pull request Apr 2, 2024
@mtmk mtmk merged commit 4bda883 into main Apr 3, 2024
10 checks passed
@mtmk mtmk deleted the 449-deserialize-0-length-payloads branch April 3, 2024 14:13
mtmk added a commit that referenced this pull request Apr 4, 2024
* #451 Dispose fixes
* #433 Update NATS.Client.Hosting package as NATS.Extensions.Microsoft.DependencyInjection
* (staged) #454 Add client certificate chain support and SSL context generation
   * #463 add NatsTlsOpts.LoadClientCertContext 
* #459 Object store metadata fix
* #455 Deserialize empty payloads
* Release 2.2.0

[nats:update-docs]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deserializer is skipped for 0 length payloads
3 participants