Open BewareMyPower opened 1 year ago
Can reproduce on my local env. This is a X is not X class loading issue -
java.lang.ClassCastException: class org.apache.pulsar.client.impl.MessageIdImpl cannot be cast to class org.apache.pulsar.client.impl.MessageIdImpl (org.apache.pulsar.client.impl.MessageIdImpl is in unnamed module of loader java.net.URLClassLoader @148080bb; org.apache.pulsar.client.impl.MessageIdImpl is in unnamed module of loader org.apache.pulsar.common.nar.NarClassLoader @2416a51)
at org.example.DemoSink.lambda$write$0(DemoSink.java:25) ~[OjpVVcIuC05Ba5ELqz3aLA/:?]
at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]
at org.example.DemoSink.write(DemoSink.java:22) ~[OjpVVcIuC05Ba5ELqz3aLA/:?]
at org.apache.pulsar.functions.instance.JavaInstanceRunnable.sendOutputMessage(JavaInstanceRunnable.java:439) ~[?:?]
at org.apache.pulsar.functions.instance.JavaInstanceRunnable.handleResult(JavaInstanceRunnable.java:401) ~[?:?]
at org.apache.pulsar.functions.instance.JavaInstanceRunnable.run(JavaInstanceRunnable.java:341) ~[?:?]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
I'll try to investigate it.
Logging how NarClassLoader
load classes. It loads -
- loading org.example.DemoSink
- loading org.apache.pulsar.client.impl.MessageIdImpl
- loading org.apache.pulsar.client.api.MessageIdAdv
But not MessageId
. So the instance is of MessageId
loaded by the URLClassLoader while we try to cast it to MessageIdImpl
loaded by NarClassLoader.
We set the NarClassLoader as context class loader before handling result -
Thread.currentThread().setContextClassLoader(instanceClassLoader);
// register end time
stats.processTimeEnd();
if (result != null) {
// process the synchronous results
handleResult(currentRecord, result);
}
Workaround with a hack internal -
diff --git a/pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java b/pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java
index c3f36f754d..cc4bd4d7d2 100644
--- a/pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java
+++ b/pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java
@@ -211,6 +211,8 @@ public class JavaInstanceRunnable implements AutoCloseable, Runnable {
this.collectorRegistry = collectorRegistry;
this.instanceClassLoader = Thread.currentThread().getContextClassLoader();
+ log.info("componentClassLoader: {}", this.componentClassLoader);
+ log.info("instanceClassLoader: {}", this.instanceClassLoader);
}
/**
diff --git a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java
index 89281a2f55..c47c2bf9fa 100644
--- a/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java
+++ b/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/runtime/JavaInstanceStarter.java
@@ -195,7 +195,7 @@ public class JavaInstanceStarter implements AutoCloseable {
functionDetailsJsonString = functionDetailsJsonString.substring(0, functionDetailsJsonString.length() - 1);
}
JsonFormat.parser().merge(functionDetailsJsonString, functionDetailsBuilder);
- FunctionCacheManager fnCache = new FunctionCacheManagerImpl(rootClassLoader);
+ FunctionCacheManager fnCache = new FunctionCacheManagerImpl(functionInstanceClassLoader);
ClassLoader functionClassLoader = ThreadRuntime.loadJars(jarFile, instanceConfig, functionId,
functionDetailsBuilder.getName(), narExtractionDirectory, fnCache);
inferringMissingTypeClassName(functionDetailsBuilder, functionClassLoader);
The root cause is that the Record
passed to sink.write
is loaded by functionInstanceClassLoader
and thus its internal MessageIdImpl
. The class loader is URLClassLoader loading pulsar.functions.instance.classpath
.
... while the sink class is loaded by NarClassLoader
whose parent is "root classloader" a.k.a. the default AppClassLoader.
For more details,
functionInstanceClassLoader =
root = /Users/tison/Brittani/pulsar/download/pulsar_functions/public/default/demo-sink/0/pulsar-sink-demo-1.0.0.nar
Ref - https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.1
cc @jiangpengcheng @nlu90 @lhotari @nicoloboschi Although the root cause located, it's not quite easy to solve this issue. My workaround can work, but I'm not sure if extra affect coupled.
The issue had no activity for 30 days, mark with Stale label.
Search before asking
Version
Minimal reproduce step
See https://github.com/BewareMyPower/pulsar-sink-demo
What did you expect to see?
It should succeed.
What did you see instead?
ClassCastException
Anything else?
From https://github.com/apache/pulsar/pull/17835, it seems that we need to use reflection to access methods and classes in the module. But it harms the user experience.
From the PR description, it seems to be related to how the Java Functions framework loads the connector. Ideally, it should use the default classpath of Pulsar broker.
Are you willing to submit a PR?