Skip to content

[FLINK-40040][cdc-runtime] Wrap coordinator failure in SerializedThrowable across the RPC boundary#4456

Open
Zhile wants to merge 4 commits into
apache:masterfrom
Zhile:FLINK-40040
Open

[FLINK-40040][cdc-runtime] Wrap coordinator failure in SerializedThrowable across the RPC boundary#4456
Zhile wants to merge 4 commits into
apache:masterfrom
Zhile:FLINK-40040

Conversation

@Zhile

@Zhile Zhile commented Jul 1, 2026

Copy link
Copy Markdown

What is the purpose of the change

Schema-evolution coordination responses are dropped when the failure's cause is a class that only lives in the user classloader (e.g. com.mysql.cj.exceptions.ConnectionIsClosedException). flink-rpc-akka deserializes the coordination response with an isolated classloader and throws ClassNotFoundException, so the response never reaches SchemaOperator, which then blocks on responseFuture.get(rpcTimeout) and fails with a misleading TimeoutException -- turning a transient error into a restart loop that only a full job restart recovers from.

Observed in production (Flink CDC 3.6.0 on Flink 1.20.3):

ERROR org.apache.pekko.remote.Remoting                             [] - com.mysql.cj.exceptions.ConnectionIsClosedException
java.lang.ClassNotFoundException: com.mysql.cj.exceptions.ConnectionIsClosedException
	at java.base/java.net.URLClassLoader.findClass(Unknown Source) ~[?:?]
	at java.base/java.lang.ClassLoader.loadClass(Unknown Source) ~[?:?]
	at org.apache.flink.core.classloading.ComponentClassLoader.loadClassFromComponentOnly(ComponentClassLoader.java:150) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.core.classloading.ComponentClassLoader.loadClass(ComponentClassLoader.java:113) ~[flink-dist-1.20.3.jar:1.20.3]
	at java.base/java.lang.ClassLoader.loadClass(Unknown Source) ~[?:?]
	at java.base/java.lang.Class.forName0(Native Method) ~[?:?]
	at java.base/java.lang.Class.forName(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.resolveClass(Unknown Source) ~[?:?]
	at org.apache.pekko.util.ClassLoaderObjectInputStream.resolveClass(ClassLoaderObjectInputStream.scala:29) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readClassDesc(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadFields(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadObject(Unknown Source) ~[?:?]
	at java.base/java.lang.Throwable.readObject(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectStreamClass.invokeReadObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readSerialData(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadFields(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadObject(Unknown Source) ~[?:?]
	at java.base/java.lang.Throwable.readObject(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectStreamClass.invokeReadObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readSerialData(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadFields(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.defaultReadObject(Unknown Source) ~[?:?]
	at java.base/java.lang.Throwable.readObject(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectStreamClass.invokeReadObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readSerialData(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject(Unknown Source) ~[?:?]
	at java.base/java.io.ObjectInputStream.readObject(Unknown Source) ~[?:?]
	at org.apache.pekko.serialization.JavaSerializer.$anonfun$fromBinary$1(Serializer.scala:358) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.JavaSerializer.fromBinary(Serializer.scala:358) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.$anonfun$deserializeByteArray$1(Serialization.scala:233) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.deserializeByteArray(Serialization.scala:167) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.$anonfun$deserialize$5(Serialization.scala:218) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at scala.util.Try$.apply(Try.scala:213) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.deserialize(Serialization.scala:209) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.serialization.WrappedPayloadSupport$.deserializePayload(WrappedPayloadSupport.scala:107) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.serialization.WrappedPayloadSupport.deserializePayload(WrappedPayloadSupport.scala:48) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.serialization.MiscMessageSerializer.deserializeStatusFailure(MiscMessageSerializer.scala:478) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.serialization.MiscMessageSerializer.$anonfun$fromBinaryMap$4(MiscMessageSerializer.scala:352) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.serialization.MiscMessageSerializer.fromBinary(MiscMessageSerializer.scala:427) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.$anonfun$deserializeByteArray$1(Serialization.scala:230) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.deserializeByteArray(Serialization.scala:167) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.$anonfun$deserialize$5(Serialization.scala:218) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at scala.util.Try$.apply(Try.scala:213) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.serialization.Serialization.deserialize(Serialization.scala:209) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.MessageSerializer$.deserialize(MessageSerializer.scala:44) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.DefaultMessageDispatcher.payload$lzycompute$1(Endpoint.scala:84) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.DefaultMessageDispatcher.payload$1(Endpoint.scala:84) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.DefaultMessageDispatcher.dispatch(Endpoint.scala:110) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.EndpointReader$$anonfun$receive$2.applyOrElse(Endpoint.scala:1155) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.actor.Actor.aroundReceive(Actor.scala:547) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.actor.Actor.aroundReceive$(Actor.scala:545) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.remote.EndpointActor.aroundReceive(Endpoint.scala:550) ~[flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.actor.ActorCell.receiveMessage(ActorCell.scala:590) [flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.actor.ActorCell.invoke(ActorCell.scala:557) [flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.dispatch.Mailbox.processMailbox(Mailbox.scala:272) [flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.dispatch.Mailbox.run(Mailbox.scala:233) [flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at org.apache.pekko.dispatch.Mailbox.exec(Mailbox.scala:245) [flink-rpc-akka3036b107-8ca6-47fb-81d3-1dc9a83bcb7b.jar:1.20.3]
	at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source) [?:?]
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source) [?:?]
	at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source) [?:?]
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source) [?:?]
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source) [?:?]

3 minutes later (exactly the schema operator rpcTimeout), the pending request times out:

WARN  org.apache.flink.runtime.taskmanager.Task                    [] - Transform:Data -> SchemaOperator -> PrePartition (1/1)#3 (8f7dbf9559420d3b74a342f42d4fe3d4_90bea66de1c231edf33913ecd54406c1_0_3) switched from RUNNING to FAILED with failure cause:
org.apache.flink.cdc.runtime.operators.transform.exceptions.TransformException: Failed to post-transform with
    AddColumnEvent{tableId=a, addedColumns=[ColumnWithPosition{column=`index` DECIMAL(20, 0) NOT NULL '0', position=AFTER, existedColumnName=.}]}
for table
a
from schema
    columns={`id` DECIMAL(20, 0) NOT NULL,...}, primaryKeys=id, options=()
to schema
    columns={`id` DECIMAL(20, 0) NOT NULL,...}, primaryKeys=id, options=().
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElement(PostTransformOperator.java:160) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.streaming.runtime.tasks.OneInputStreamTask$StreamTaskNetworkOutput.emitRecord(OneInputStreamTask.java:238) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.processElement(AbstractStreamTaskNetworkInput.java:157) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.emitNext(AbstractStreamTaskNetworkInput.java:114) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.io.StreamOneInputProcessor.processInput(StreamOneInputProcessor.java:65) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.StreamTask.processInput(StreamTask.java:638) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:231) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:973) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:917) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.runtime.taskmanager.Task.runWithSystemExitMonitoring(Task.java:970) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.runtime.taskmanager.Task.restoreAndInvoke(Task.java:949) [flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:763) [flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.runtime.taskmanager.Task.run(Task.java:575) [flink-dist-1.20.3.jar:1.20.3]
	at java.base/java.lang.Thread.run(Unknown Source) [?:?]
Caused by: org.apache.flink.streaming.runtime.tasks.ExceptionInChainedOperatorException: Could not forward element to next operator
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.pushToOperator(CopyingChainingOutput.java:92) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:50) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:29) ~[flink-dist-1.20.3.jar:1.20.3]
	at java.base/java.util.Optional.ifPresent(Unknown Source) ~[?:?]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElementInternal(PostTransformOperator.java:194) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElement(PostTransformOperator.java:144) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	... 13 more
Caused by: java.lang.IllegalStateException: Failed to send request to coordinator: SchemaChangeRequest{tableId=a, schemaChangeEvent=AddColumnEvent{tableId=a, addedColumns=[ColumnWithPosition{column=`index` DECIMAL(20, 0) NOT NULL '0', position=AFTER, existedColumnName=.}]}, subTaskId=0}
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.sendRequestToCoordinator(SchemaOperator.java:246) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.requestSchemaChange(SchemaOperator.java:232) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.handleSchemaChangeEvent(SchemaOperator.java:178) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.processElement(SchemaOperator.java:153) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.pushToOperator(CopyingChainingOutput.java:75) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:50) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:29) ~[flink-dist-1.20.3.jar:1.20.3]
	at java.base/java.util.Optional.ifPresent(Unknown Source) ~[?:?]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElementInternal(PostTransformOperator.java:194) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElement(PostTransformOperator.java:144) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	... 13 more
Caused by: java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.CompletableFuture.timedGet(Unknown Source) ~[?:?]
	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source) ~[?:?]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.sendRequestToCoordinator(SchemaOperator.java:243) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.requestSchemaChange(SchemaOperator.java:232) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.handleSchemaChangeEvent(SchemaOperator.java:178) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.schema.regular.SchemaOperator.processElement(SchemaOperator.java:153) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.pushToOperator(CopyingChainingOutput.java:75) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:50) ~[flink-dist-1.20.3.jar:1.20.3]
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:29) ~[flink-dist-1.20.3.jar:1.20.3]
	at java.base/java.util.Optional.ifPresent(Unknown Source) ~[?:?]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElementInternal(PostTransformOperator.java:194) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	at org.apache.flink.cdc.runtime.operators.transform.PostTransformOperator.processElement(PostTransformOperator.java:144) ~[flink-cdc-dist-3.6.0-1.20.jar:3.6.0-1.20]
	... 13 more

JIRA: https://issues.apache.org/jira/browse/FLINK-40040

Brief change log

  • SchemaRegistry.failJob now wraps the cause into SerializedThrowable (idempotent, not re-wrapped if already one) before calling handleUnrecoverableError (which completes pending coordination responses exceptionally) and context.failJob. SerializedThrowable carries the original exception as bytes plus a stringified stack trace, so the receiving side can deserialize it without the original class and still see the real cause.
  • SchemaRegistry.runInEventLoop's catch block now routes through failJob instead of duplicating the fail path, so every exit shares the wrapping.
  • Applies to both the regular and distributed SchemaCoordinator, since both complete pending response futures via the shared failJob -> handleUnrecoverableError path.

Verifying this change

Added SchemaRegistryFailJobSerializationTest:

  • failJobWrapsExceptionIntoSerializedThrowable asserts the failure delivered to the coordinator context is a SerializedThrowable.
  • wrappedFailureSurvivesIsolatedClassloaderButRawOneDoesNot shows a raw exception whose cause is a classloader-isolated type fails to deserialize (ClassNotFoundException), while the SerializedThrowable-wrapped one deserializes and preserves the real cause text.

Does this pull request potentially affect one of the following parts

  • Dependencies (does it add or upgrade a dependency): no
  • Public API / SDK: no
  • Serializers / state: no (wraps only the failure carried over the coordinator RPC channel; no state format change)

@yuxiqian

yuxiqian commented Jul 1, 2026

Copy link
Copy Markdown
Member

Could you please check the failing test cases?

@yuxiqian yuxiqian self-requested a review July 1, 2026 14:29
@Zhile

Zhile commented Jul 2, 2026

Copy link
Copy Markdown
Author

@yuxiqian please help review. I also pasted the full exception in the description. Please refer to that.

@yuxiqian

yuxiqian commented Jul 7, 2026

Copy link
Copy Markdown
Member

Could you please add a pipeline E2e case for this patch? I believe we can reproduce this issue by putting different jar dependencies in JM and TM docker containers.

Also, rebasing latest master branch may eliminate most CI failing noises.

Jiyong Wang added 3 commits July 8, 2026 11:24
…wable across RPC boundary

SchemaRegistry.failJob now wraps the failure cause into a SerializedThrowable
before it crosses the operator-coordinator RPC boundary, and runInEventLoop
routes through failJob so all exits share the wrapping.

Otherwise an exception whose class only lives in the user classloader (e.g. the
MySQL driver's com.mysql.cj.exceptions.ConnectionIsClosedException raised during
table discovery) fails to deserialize on the receiving side, where flink-rpc-akka
uses an isolated classloader, with ClassNotFoundException. The coordination
response is then lost and the SchemaOperator request stalls until rpcTimeout,
failing with a misleading TimeoutException and turning a transient error into a
restart loop that only a full job restart recovers from.
…lizedThrowable-wrapped coordinator failures

Since the coordinator now wraps its failure into a SerializedThrowable before
completing the pending coordination response (so it survives the
operator-coordinator RPC boundary), the exact typed exception chain is no
longer observable by the requester. Update testExceptionEvolveSchema and
testEvolveSchemaWithFailure to assert on stack trace content instead:

- The top-level exception type is not asserted because the failure may
  surface via the operator request path or the job-failure path depending
  on timing.
- Only strings present in all printed representations are asserted: the
  wrapping FlinkRuntimeException message and the original exception's
  class name (the wrapped cause is printed either as its fully-qualified
  class name or via the exception's custom toString, depending on the
  delivery path).
…lizedThrowable-wrapped coordinator failures

Since the coordinator failure now crosses the operator-coordinator RPC
boundary as a SerializedThrowable, the "Caused by: " rendering of the
cause chain in the JobManager log varies with the delivery path. Drop the
"Caused by: " prefixes from the expected log lines and only wait for the
class names / messages that appear in every representation. The
tolerant-mode expectations are unchanged, since those WARN logs print the
original exception before it is wrapped.
…th JobManager-only exception class

Reproduces the production classpath asymmetry: a jar containing
JobManagerOnlyException is installed into the JobManager container's
/opt/flink/lib only, never shipped to the TaskManager container or
submitted with the pipeline jars. The values sink gains an
error.on.schema.change.exception.class option so its metadata applier
raises that class (instantiated reflectively on the coordinator) as the
cause of a rejected schema change.

Verified against the pre-fix runtime: without the SerializedThrowable
wrapping in SchemaRegistry.failJob, the exceptionally completed
coordination response cannot be deserialized by the TaskManager's RPC
layer, which tears down the JobManager <-> TaskManager Pekko association
("Association with remote system ... has failed, address is now gated.
Reason: [org.apache.flink.cdc...Exception]"). Cancellation then stalls
for a full RPC timeout cycle before the job reaches FAILED, and the
original cause never surfaces on the TaskManager. The test asserts the
job fails cleanly and that the TaskManager's RPC layer never chokes on
the coordinator failure; it fails on the pre-fix runtime and passes
after the fix (19s vs 79s runtime).
@Zhile

Zhile commented Jul 8, 2026

Copy link
Copy Markdown
Author

please help review.

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.

2 participants