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

[improve][fn] Use functions classloader in TopicSchema.newSchemaInstance() to fix ClassNotFoundException when using custom SerDe classes. (targeted for master) #20115

Merged

Conversation

gmiklos-ltg
Copy link
Contributor

Fixes #5350

Motivation

While evaluating Pulsar Functions for our data transformation use-cases we ran into the following startup issue with our function that was using a custom SerDe class:
2023-04-12T11:30:35,368+0200 [public/default/user-transform-new-2-0] ERROR org.apache.pulsar.functions.instance.JavaInstanceRunnable - [public/default/user-transform-new-2:0] Uncaught exception in Java Instance java.lang.RuntimeException: User class must be in class path at org.apache.pulsar.common.util.Reflections.createInstance(Reflections.java:72) ~[org.apache.pulsar-pulsar-common-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.instance.InstanceUtils.createInstance(InstanceUtils.java:92) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.instance.InstanceUtils.initializeSerDe(InstanceUtils.java:51) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.source.TopicSchema.newSchemaInstance(TopicSchema.java:238) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.source.TopicSchema.newSchemaInstance(TopicSchema.java:246) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.source.TopicSchema.lambda$getSchema$0(TopicSchema.java:68) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at java.util.HashMap.computeIfAbsent(HashMap.java:1220) ~[?:?] at org.apache.pulsar.functions.source.TopicSchema.getSchema(TopicSchema.java:68) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.source.PulsarSource.buildPulsarSourceConsumerConfig(PulsarSource.java:176) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.source.SingleConsumerPulsarSource.open(SingleConsumerPulsarSource.java:69) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.instance.JavaInstanceRunnable.setupInput(JavaInstanceRunnable.java:774) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.instance.JavaInstanceRunnable.setup(JavaInstanceRunnable.java:226) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at org.apache.pulsar.functions.instance.JavaInstanceRunnable.run(JavaInstanceRunnable.java:259) ~[org.apache.pulsar-pulsar-functions-instance-2.11.0.jar:2.11.0] at java.lang.Thread.run(Thread.java:833) ~[?:?] Caused by: java.lang.ClassNotFoundException: com.bridge.data.UserMessageSerDe at java.net.URLClassLoader.findClass(URLClassLoader.java:445) ~[?:?] at java.lang.ClassLoader.loadClass(ClassLoader.java:587) ~[?:?] at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?] at java.lang.Class.forName0(Native Method) ~[?:?] at java.lang.Class.forName(Class.java:467) ~[?:?] at org.apache.pulsar.common.util.Reflections.createInstance(Reflections.java:70) ~[org.apache.pulsar-pulsar-common-2.11.0.jar:2.11.0] ... 13 more

We made it absolutely sure that the class is included in the fat jar that we have compiled.

config.yaml looks like this:

tenant: public
namespace: default
name: user-transform
className: com.bridge.data.TransformUserMessage
inputs:
  - "persistent://public/default/debezium.public.users"
customSerdeInputs:
  "persistent://public/default/debezium.public.users": com.bridge.data.UserMessageSerDe
output: "persistent://public/default/debezium.public.users-public"
outputSerdeClassName: com.bridge.data.PublicUserSerDe
jar: "transform-function-v1.jar"
parallelism: 1
retainOrdering: true

The mentioned UserMessageSerDe class is just a very simple wrapper around Jackson to deserialize UserMessage data classes.

Modifications

I have modified TopicSchema in such a way that it accepts a functions classloader parameter which will be used by newSchemaInstance() to resolve this class. This way our function can now start up without any issues.

Verifying this change

  • Make sure that the change passes the CI checks.

This change is already covered by existing tests, such as TopicSchemaTest.

Does this pull request potentially affect one of the following parts:

If the box was checked, please highlight the changes

  • Dependencies (add or upgrade a dependency)
  • The public API
  • The schema
  • The default values of configurations
  • The threading model
  • The binary protocol
  • The REST endpoints
  • The admin CLI options
  • The metrics
  • Anything that affects deployment

Documentation

  • doc
  • doc-required
  • doc-not-needed
  • doc-complete

@github-actions github-actions bot added the doc-not-needed Your PR changes do not impact docs label Apr 17, 2023
@gmiklos-ltg gmiklos-ltg changed the title Use functions classloader in TopicSchema.newSchemaInstance() to fix ClassNotFoundException when using custom SerDe classes. (targeted for master) [improve][fn] Use functions classloader in TopicSchema.newSchemaInstance() to fix ClassNotFoundException when using custom SerDe classes. (targeted for master) Apr 17, 2023
@tisonkun tisonkun requested review from nlu90 and tisonkun April 19, 2023 07:00
this.client = client;
this.functionsClassloader = AccessController.doPrivileged(
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need AccessController.doPrivileged here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interestingly IntelliJ IDEA gives me a pretty strong warning that doPrivileged should be used while creating this URLClassLoader. Shall we omit this call and just create the URLClassLoader as it is? I saw other parts of code that made use of doPrivileged (perhaps it also makes static analysis to fail).

Copy link
Member

Choose a reason for hiding this comment

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

This method (as well as AccessController) is deprecated to be removed since JDK 17. We should find other way or just omit the warning.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

@tisonkun tisonkun Apr 19, 2023

Choose a reason for hiding this comment

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

Is it possible for us to add a test case fails on the current master branch? Maybe follow the case introduced in #5357 so we know what is fixed and prevent regression.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll see what I can do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have expanded upon PulsarFunctionsTest to also include an input SerDe class. It should fail on the current master because as it was similarly with #5357 it uses the wrong classloader to load the input SerDe class.

@gmiklos-ltg gmiklos-ltg force-pushed the serde-source-classloading-master branch from 535291c to c417377 Compare April 28, 2023 13:32
@tisonkun tisonkun self-requested a review May 5, 2023 08:42
@tisonkun
Copy link
Member

tisonkun commented May 5, 2023

/pulsarbot run-failure-tests

@tisonkun
Copy link
Member

tisonkun commented May 8, 2023

/pulsarbot run-failure-checks

@nlu90
Copy link
Member

nlu90 commented May 15, 2023

/pulsarbot run-failure-checks

@nlu90 nlu90 closed this May 15, 2023
@nlu90 nlu90 reopened this May 15, 2023
@nlu90
Copy link
Member

nlu90 commented May 15, 2023

/pulsarbot run-failure-checks

@codecov-commenter
Copy link

Codecov Report

Merging #20115 (3150370) into master (9841364) will decrease coverage by 0.71%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master   #20115      +/-   ##
============================================
- Coverage     72.87%   72.16%   -0.71%     
- Complexity    31943    31957      +14     
============================================
  Files          1868     1869       +1     
  Lines        138594   142047    +3453     
  Branches      15246    15996     +750     
============================================
+ Hits         101003   102512    +1509     
- Misses        29547    31367    +1820     
- Partials       8044     8168     +124     
Flag Coverage Δ
inttests 24.61% <0.00%> (+0.34%) ⬆️
systests 25.11% <100.00%> (+0.18%) ⬆️
unittests 72.22% <100.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
.../apache/pulsar/functions/instance/ContextImpl.java 61.19% <100.00%> (ø)
...a/org/apache/pulsar/functions/sink/PulsarSink.java 73.85% <100.00%> (ø)
...g/apache/pulsar/functions/source/PulsarSource.java 77.01% <100.00%> (ø)
...r/functions/source/SingleConsumerPulsarSource.java 93.33% <100.00%> (ø)
...rg/apache/pulsar/functions/source/TopicSchema.java 68.35% <100.00%> (+0.40%) ⬆️

... and 94 files with indirect coverage changes

@nlu90
Copy link
Member

nlu90 commented May 18, 2023

@tisonkun do you have any other concerns or comments about the PR?

If no, we'll approve and merge it.

Copy link
Member

@tisonkun tisonkun left a comment

Choose a reason for hiding this comment

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

LGTM

@nlu90 your approval should be isolated from mine, lol.

Technoboy- pushed a commit that referenced this pull request May 24, 2023
…nce() to fix ClassNotFoundException when using custom SerDe classes. (targeted for master) (#20115)
poorbarcode pushed a commit that referenced this pull request May 30, 2023
…nce() to fix ClassNotFoundException when using custom SerDe classes. (targeted for master) (#20115)

(cherry picked from commit 43a9898)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pulsar can't load the customized SerDe
6 participants