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

Fix serialization of subclasses #92

Merged

Conversation

albertchae
Copy link
Collaborator

@albertchae albertchae commented Nov 4, 2024

Summary

Changes:

  • Add integration test reproducing the issue when catching a StepError as a generic Exception
  • Add integration test showing that any subclass/parent class that gets memoized with the Inngest server will have this issue
  • Resolve serialization issue by sending the class name in the step data payload and using it reflectively when deserializing

Checklist

  • Update documentation
  • Added unit/integration tests

Related

https://inngest.slack.com/archives/C06KB85RXRV/p1728067438991139

@albertchae albertchae requested a review from KiKoS0 November 4, 2024 17:16
@albertchae albertchae force-pushed the poc-UnrecognizedPropertyException-anyparentclass-java-class branch from 8faaf8c to 988a10e Compare November 7, 2024 00:02
This is the reproduction of an error reported by a user. Because
StepError contains additional fields to a generic Exception,
deserialization fails.
This shows that the issue is not limited to StepError/Exception.
Previously we were deserializing step data based on the T provided by the compiler.
However, this could be lossy if the step data was actually a subclass of
T with additional fields and is semantically incorrect if we try to
deserialize it as the parent class.

To remedy this, we need to know what the actual class of the step data
was before it was memoized with the Inngest server. We are adding this
as an additional field `class` on the JSON when serializing, then
reading (and deleting) this field and using it with reflection to
deserialize using the proper class.

It might make sense to put the field `class` in some kind of
meta/extension section of the payload to avoid conflict, but it's highly
unlikely a field will legitimately be named class in the user code since
it is a reserved keyword in Java.

This strategy doesn't entirely work for schema evolution because it's
still possible the classes have changed to the point where
deserialization would fail. We should follow up here and probably have a
fallback mechanism of returning a generic hash if deserialization fails.
@albertchae albertchae force-pushed the poc-UnrecognizedPropertyException-anyparentclass-java-class branch from a317066 to 644a99e Compare November 7, 2024 16:24
@albertchae albertchae marked this pull request as ready for review November 7, 2024 16:30
@albertchae albertchae changed the title [WIP] Fix serialization of subclasses Fix serialization of subclasses Nov 7, 2024

val writeableJson = serializedStepData as ObjectNode
val className = writeableJson.remove("class").asText()
return mapper.treeToValue(writeableJson, Class.forName(className)) as T
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need to figure out how to handle the case where this will fail because the class no longer exists (or renamed) or has been changed enough since serialization that this won't deserialize

Comment on lines +269 to +270
val jsonString = mapper.writeValueAsString(stepData)
val readOnlyJson = mapper.readTree(jsonString)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

converting an object to a JSON string only to read that back in to get a JSON node is silly, but replacing this with

val writeableJson = mapper.convertValue(stepData, ObjectNode::class.java)

resulted in a bunch of test failures, so we decided to punt on figuring this out to a follow up

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good - I think I may have had code like this in an early PoC. There wasn't a great pattern for this in Java and the JSON libraries from what I could tell that didn't result in duplicate parsing/serialization work. I'm find with leaving this for a future improvement.

@albertchae albertchae merged commit 76cd8f9 into main Nov 11, 2024
9 checks passed
@albertchae albertchae deleted the poc-UnrecognizedPropertyException-anyparentclass-java-class branch November 11, 2024 19:45
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