This is an automated email from the ASF dual-hosted git repository.

jongyoul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new 427bf3aa5c [ZEPPELIN-6543] Handle invokeMethod serialization failure 
as InterpreterRPCException
427bf3aa5c is described below

commit 427bf3aa5c8010cdbdb04b256c4e0bf0d78f054d
Author: gyowoo1113 <[email protected]>
AuthorDate: Sun Aug 9 23:20:18 2026 +0900

    [ZEPPELIN-6543] Handle invokeMethod serialization failure as 
InterpreterRPCException
    
    ### What is this PR for?
    This PR follows up on ZEPPELIN-6467 / PR #5312.
    
    Following PR #5312, `Resource.serializeObject()` propagates serialization 
failures as `IOException`. As a result, 
`RemoteInterpreterEventServer.invokeMethod()` can receive an exception while 
re-serializing a remote resource invocation result. The existing handler logged 
the exception and returned a null `ByteBuffer`, causing the generated Thrift 
client to report a missing result instead of preserving the original 
serialization failure.
    
    The Jira issue identified this behavior through code analysis, but the 
server-side deserialize-and-re-serialize failure path had not yet been 
reproduced.
    
    This PR adds a regression test using a serializable object that succeeds 
during the initial serialization and fails during the server-side second 
serialization. It then changes `invokeMethod()` to propagate the failure as 
`InterpreterRPCException`, allowing the original error message to reach the 
caller instead of being converted into an unrelated Thrift missing-result error.
    
    The behavior for successfully serialized results is unchanged.
    
    ### What type of PR is it?
    Bug Fix
    
    ### Todos
    * [x] Reproduce the server-side deserialize-and-re-serialize failure path 
with a regression test
    * [x] Propagate serialization failures as `InterpreterRPCException`
    * [x] Verify that the propagated exception contains the original failure 
message
    
    ### What is the Jira issue?
    [[ZEPPELIN-6543](https://issues.apache.org/jira/browse/ZEPPELIN-6543)]
    
    ### How should this be tested?
    `./mvnw test -pl zeppelin-server -Dtest=RemoteInterpreterEventServerTest` 
passes successfully.
    
    ### Screenshots (if appropriate)
    N/A
    
    ### Questions:
    * Does the license files need to update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    * Code inspection suggests that `Resource.serializeObject()` may return 
`null` when the result is not serializable, but this path is not covered by the 
regression test in this PR. Should this case also be handled in this PR, or 
should it be addressed separately?
    
    Closes #5349 from 
gyowoo1113/ZEPPELIN-6543-handle-invoke-method-serialization-failure.
    
    Signed-off-by: Jongyoul Lee <[email protected]>
---
 .../interpreter/RemoteInterpreterEventServer.java  |  1 +
 .../RemoteInterpreterEventServerTest.java          | 95 ++++++++++++++++++++++
 2 files changed, 96 insertions(+)

diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServer.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServer.java
index bab3ee7b2a..8cba498dac 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServer.java
@@ -441,6 +441,7 @@ public class RemoteInterpreterEventServer implements 
RemoteInterpreterEventServi
         obj = Resource.serializeObject(ret);
       } catch (IOException e) {
         LOGGER.error("invokeMethod failed", e);
+        throw new InterpreterRPCException(e.toString());
       }
     }
     return obj;
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServerTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServerTest.java
new file mode 100644
index 0000000000..ad385c612e
--- /dev/null
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/RemoteInterpreterEventServerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zeppelin.interpreter;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+
+import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.apache.zeppelin.interpreter.remote.InvokeResourceMethodEventMessage;
+import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess;
+import org.apache.zeppelin.interpreter.thrift.InterpreterRPCException;
+import org.apache.zeppelin.resource.Resource;
+import org.apache.zeppelin.resource.ResourceId;
+import org.junit.jupiter.api.Test;
+
+public class RemoteInterpreterEventServerTest {
+  
+  @Test
+  void invokeMethodThrowsRpcExceptionWhenSerializationFails() throws Exception 
{
+    ZeppelinConfiguration zConf = mock(ZeppelinConfiguration.class);
+    InterpreterSettingManager manager = mock(InterpreterSettingManager.class);
+    RemoteInterpreterEventServer server = new 
RemoteInterpreterEventServer(zConf, manager);
+
+    ManagedInterpreterGroup interpreterGroup = 
mock(ManagedInterpreterGroup.class);
+    RemoteInterpreterProcess remoteInterpreterProcess = 
mock(RemoteInterpreterProcess.class);
+      
+    when(manager.getInterpreterGroupById("pool-id"))
+        .thenReturn(interpreterGroup);
+    when(interpreterGroup.getRemoteInterpreterProcess())
+        .thenReturn(remoteInterpreterProcess);
+    when(remoteInterpreterProcess.isRunning())
+        .thenReturn(true);
+      
+    ByteBuffer remoteResult = Resource.serializeObject(new 
SerializableOnlyOnce());
+    doReturn(remoteResult)
+        .when(remoteInterpreterProcess)
+        .callRemoteFunction(any());
+      
+    ResourceId resourceId = ResourceId.fromJson(
+        
"{\"resourcePoolId\":\"pool-id\",\"name\":\"resource-name\",\"noteId\":\"note-id\",\"paragraphId\":\"paragraph-id\"}"
+    );
+
+    InvokeResourceMethodEventMessage message = new 
InvokeResourceMethodEventMessage(
+        resourceId
+        , "someMethod"
+        , null
+        , null
+        , null);
+
+    InterpreterRPCException exception = assertThrows(
+        InterpreterRPCException.class,
+        () -> server.invokeMethod("caller-group-id", message.toJson()));
+      
+    assertTrue(exception.toString().contains("failed on second 
serialization"));
+  }
+  private static class SerializableOnlyOnce implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private static final int FAILURE_SERIALIZATION_COUNT = 2;
+
+    private int serializationCount;
+
+    private void writeObject(ObjectOutputStream outputStream) throws 
IOException {
+      serializationCount++;
+
+      if (serializationCount == FAILURE_SERIALIZATION_COUNT) {
+        throw new IOException("failed on second serialization");
+      }
+      
+      outputStream.defaultWriteObject();
+    }
+  }
+}

Reply via email to