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

Correct and optimize custom serializers for stringized values, hashes, nullable and optional fields #1351

Merged
merged 11 commits into from
Sep 13, 2023

Conversation

mzabaluev
Copy link
Contributor

@mzabaluev mzabaluev commented Sep 12, 2023

The custom field serializers in tendermint and tendermint-proto have a few problems.
The modules where the deserializers expect the values to be an Option (i.e. nullable) do not consistently use Serializer::serialize_some when serializing a non-null value. This is not an issue with serde_json, but as the serde framework is generic, this implementation will break with other serializers, particularly for non-self-describing formats.
Deserializer functions unconditionally allocate temporary strings where they can potentially be borrowed from the deserializer, achieving zero copies from source data.

These changes correct the serde schema issues and introduce optimization by deserializing strings into Cow, allowing use of borrowed string values from deserializers that support it.

  • Referenced an issue explaining the need for the change
  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Added entry in .changelog/

Borrow string slices from the deserializer instead of necessarily
allocating. Let the de::Error::custom constructor handle the
Display formatting of the errors.
To be correct with all serializers, the serialize function always
serializes an Option<T>.
Borrow strings from the deserializer instead of allocating,
serialize nullable values with `Serializer::serialize_some` to match
what the deserializer expects.
Make it correct with regards to its own serialization format when
None is serialized. Add a round trip unit test to prove it.
@mzabaluev mzabaluev added enhancement New feature or request serialization technical debt Issues that are important, but not urgent, that should eventually receive attention labels Sep 12, 2023
@codecov-commenter
Copy link

codecov-commenter commented Sep 12, 2023

Codecov Report

Merging #1351 (4de748b) into main (a73362d) will increase coverage by 0.9%.
The diff coverage is 79.3%.

❗ Current head 4de748b differs from pull request most recent head 185315e. Consider uploading reports for the commit 185315e to get more accurate results

@@           Coverage Diff           @@
##            main   #1351     +/-   ##
=======================================
+ Coverage   59.5%   60.4%   +0.9%     
=======================================
  Files        272     272             
  Lines      26974   26544    -430     
=======================================
- Hits       16068   16052     -16     
+ Misses     10906   10492    -414     
Files Changed Coverage Δ
tendermint/src/serializers/option_hash.rs 33.3% <46.1%> (+33.3%) ⬆️
tendermint/src/serializers/apphash.rs 46.6% <50.0%> (ø)
tendermint/src/serializers/hash.rs 46.6% <50.0%> (ø)
tendermint/src/serializers/apphash_base64.rs 94.4% <75.0%> (-5.6%) ⬇️
proto/src/serializers/from_str.rs 100.0% <100.0%> (ø)
proto/src/serializers/nullable.rs 100.0% <100.0%> (ø)
proto/src/serializers/optional.rs 100.0% <100.0%> (ø)
proto/src/serializers/optional_from_str.rs 67.7% <100.0%> (+67.7%) ⬆️

... and 12 files with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

If the deserializer cannot provide a borrowed string value,
deserialize into an owned string and bridge the two cases with Cow.
The serialize function is changed to serialize as `Option<T>` and
serialize `None` as `Some(T::default()). This makes the round-trip
work with any serializers, without breaking the schema produced
by serde_json.
@mzabaluev mzabaluev changed the title Correct and optimize custom serializers for stringized values and hashes Correct and optimize custom serializers for stringized values, hashes, nullable and optional fields Sep 12, 2023
@mzabaluev mzabaluev marked this pull request as ready for review September 12, 2023 19:26
Comment on lines +2 to +3
//! Deserialize to `None` if the received value equals the `Default` value.
//! Serialize `None` as `Some` with the `Default` value for `T`.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The behaviour of this helper is questionable, before or after this change. If the type has a reasonable default value, why make the domain type field an Option? And in the other direction, why uselessly serialize a default value when a None would do as well?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, It's kind of a head-scratcher!
I see this is derived on fields of certain proto types with Option<…> types (e.g. here). Perhaps, those types do not inherently need to be Option. Consequently, they required serde to handle Option this way.

Farhad-Shabani

This comment was marked as duplicate.

Copy link
Member

@Farhad-Shabani Farhad-Shabani left a comment

Choose a reason for hiding this comment

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

Just a small nit. Otherwise looks good. Thanks

@mzabaluev mzabaluev merged commit eaafe3b into main Sep 13, 2023
23 checks passed
@mzabaluev mzabaluev deleted the mikhail/serializer-improvements branch September 13, 2023 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request serialization technical debt Issues that are important, but not urgent, that should eventually receive attention
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants