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

github-merge-queue[bot] pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git


The following commit(s) were added to refs/heads/dev by this push:
     new 9cd5d0368a [Improve][Zeta] Use fixed serialVersionUID in task state 
messages (#12466)
9cd5d0368a is described below

commit 9cd5d0368a4c6ccb72ea040edf315bcc1328e481
Author: Goutam Adwant <[email protected]>
AuthorDate: Tue Nov 3 06:23:53 2026 +0000

    [Improve][Zeta] Use fixed serialVersionUID in task state messages (#12466)
    
    Signed-off-by: Goutam Adwant <[email protected]>
---
 .../engine/server/execution/TaskDeployState.java   |   6 +
 .../server/execution/TaskExecutionState.java       |   6 +
 .../serializable/TaskStateSerializationTest.java   | 178 +++++++++++++++++++++
 3 files changed, 190 insertions(+)

diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskDeployState.java
 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskDeployState.java
index d683d6d86c..063c253df0 100644
--- 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskDeployState.java
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskDeployState.java
@@ -25,6 +25,12 @@ import java.io.Serializable;
 
 @Data
 public class TaskDeployState implements Serializable {
+    // Same as the previously computed value. Keep this UID for compatible 
field additions; new
+    // reference fields deserialize as null when absent from old instances; 
consumers must handle
+    // that null explicitly. Constructors do not supply defaults during 
deserialization.
+    // Incompatible state changes need an explicit versioned serialization 
contract.
+    private static final long serialVersionUID = 2646079648150626562L;
+
     private final boolean success;
     private final String throwableMsg;
 
diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionState.java
 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionState.java
index a2a5e747e5..04c98954e2 100644
--- 
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionState.java
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionState.java
@@ -23,6 +23,12 @@ import java.io.Serializable;
 
 public class TaskExecutionState implements Serializable {
 
+    // Same as the previously computed value. Keep this UID for compatible 
field additions; new
+    // reference fields deserialize as null when absent from old instances; 
consumers must handle
+    // that null explicitly. Constructors do not supply defaults during 
deserialization.
+    // Incompatible state changes need an explicit versioned serialization 
contract.
+    private static final long serialVersionUID = -108652017022658969L;
+
     private final TaskGroupLocation taskGroupLocation;
 
     private final ExecutionState executionState;
diff --git 
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/serializable/TaskStateSerializationTest.java
 
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/serializable/TaskStateSerializationTest.java
new file mode 100644
index 0000000000..1251553e62
--- /dev/null
+++ 
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/serializable/TaskStateSerializationTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.seatunnel.engine.server.serializable;
+
+import org.apache.seatunnel.engine.server.execution.ExecutionState;
+import org.apache.seatunnel.engine.server.execution.TaskDeployState;
+import org.apache.seatunnel.engine.server.execution.TaskExecutionState;
+import org.apache.seatunnel.engine.server.execution.TaskGroupLocation;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.hazelcast.internal.serialization.Data;
+import com.hazelcast.internal.serialization.InternalSerializationService;
+import 
com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder;
+import 
com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers.JavaSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.Base64;
+
+/**
+ * Verifies that task state messages exchanged between workers and the master 
keep the Java
+ * serialization form they had before their serialVersionUID was declared.
+ *
+ * <p>The fixtures were serialized from the unmodified pre-UID classes using 
JDK 8 and JDK 11; both
+ * produced identical bytes. The UID and ReadsPreviousWireForm tests guard 
old-writer/new-reader
+ * compatibility; do not regenerate their fixtures when adding fields. This 
Java serialization proxy
+ * applies while these classes remain plain Serializable without a custom 
Hazelcast serializer. The
+ * Hazelcast round-trip test checks that fallback and current field values, 
not exact archived
+ * bytes.
+ */
+class TaskStateSerializationTest {
+
+    private static final String TASK_EXECUTION_STATE_FAILED =
+            
"rO0ABXNyAD9vcmcuYXBhY2hlLnNlYXR1bm5lbC5lbmdpbmUuc2VydmVyLmV4ZWN1dGlvbi5UYXNr"
+                    + 
"RXhlY3V0aW9uU3RhdGX+ff2Q8TwWZwIAA0wADmV4ZWN1dGlvblN0YXRldAA9TG9yZy9hcGFjaGUv"
+                    + 
"c2VhdHVubmVsL2VuZ2luZS9zZXJ2ZXIvZXhlY3V0aW9uL0V4ZWN1dGlvblN0YXRlO0wAEXRhc2tH"
+                    + 
"cm91cExvY2F0aW9udABATG9yZy9hcGFjaGUvc2VhdHVubmVsL2VuZ2luZS9zZXJ2ZXIvZXhlY3V0"
+                    + 
"aW9uL1Rhc2tHcm91cExvY2F0aW9uO0wADHRocm93YWJsZU1zZ3QAEkxqYXZhL2xhbmcvU3RyaW5n"
+                    + 
"O3hwfnIAO29yZy5hcGFjaGUuc2VhdHVubmVsLmVuZ2luZS5zZXJ2ZXIuZXhlY3V0aW9uLkV4ZWN1"
+                    + 
"dGlvblN0YXRlAAAAAAAAAAASAAB4cgAOamF2YS5sYW5nLkVudW0AAAAAAAAAABIAAHhwdAAGRkFJ"
+                    + 
"TEVEc3IAPm9yZy5hcGFjaGUuc2VhdHVubmVsLmVuZ2luZS5zZXJ2ZXIuZXhlY3V0aW9uLlRhc2tH"
+                    + 
"cm91cExvY2F0aW9ujIP/i/I+4/kCAANKAAVqb2JJZEkACnBpcGVsaW5lSWRKAAt0YXNrR3JvdXBJ"
+                    + 
"ZHhwAAAAAAAAA+kAAAABAAAAAAAATiF0AC5qYXZhLmxhbmcuSWxsZWdhbFN0YXRlRXhjZXB0aW9u"
+                    + "OiBzb3VyY2UgZmFpbGVk";
+
+    private static final String TASK_EXECUTION_STATE_FINISHED =
+            
"rO0ABXNyAD9vcmcuYXBhY2hlLnNlYXR1bm5lbC5lbmdpbmUuc2VydmVyLmV4ZWN1dGlvbi5UYXNr"
+                    + 
"RXhlY3V0aW9uU3RhdGX+ff2Q8TwWZwIAA0wADmV4ZWN1dGlvblN0YXRldAA9TG9yZy9hcGFjaGUv"
+                    + 
"c2VhdHVubmVsL2VuZ2luZS9zZXJ2ZXIvZXhlY3V0aW9uL0V4ZWN1dGlvblN0YXRlO0wAEXRhc2tH"
+                    + 
"cm91cExvY2F0aW9udABATG9yZy9hcGFjaGUvc2VhdHVubmVsL2VuZ2luZS9zZXJ2ZXIvZXhlY3V0"
+                    + 
"aW9uL1Rhc2tHcm91cExvY2F0aW9uO0wADHRocm93YWJsZU1zZ3QAEkxqYXZhL2xhbmcvU3RyaW5n"
+                    + 
"O3hwfnIAO29yZy5hcGFjaGUuc2VhdHVubmVsLmVuZ2luZS5zZXJ2ZXIuZXhlY3V0aW9uLkV4ZWN1"
+                    + 
"dGlvblN0YXRlAAAAAAAAAAASAAB4cgAOamF2YS5sYW5nLkVudW0AAAAAAAAAABIAAHhwdAAIRklO"
+                    + 
"SVNIRURzcgA+b3JnLmFwYWNoZS5zZWF0dW5uZWwuZW5naW5lLnNlcnZlci5leGVjdXRpb24uVGFz"
+                    + 
"a0dyb3VwTG9jYXRpb26Mg/+L8j7j+QIAA0oABWpvYklkSQAKcGlwZWxpbmVJZEoAC3Rhc2tHcm91"
+                    + "cElkeHAAAAAAAAAD6QAAAAEAAAAAAABOIXA=";
+
+    private static final String TASK_DEPLOY_STATE_SUCCESS =
+            
"rO0ABXNyADxvcmcuYXBhY2hlLnNlYXR1bm5lbC5lbmdpbmUuc2VydmVyLmV4ZWN1dGlvbi5UYXNr"
+                    + 
"RGVwbG95U3RhdGUkuMNro2ihAgIAAloAB3N1Y2Nlc3NMAAx0aHJvd2FibGVNc2d0ABJMamF2YS9s"
+                    + "YW5nL1N0cmluZzt4cAFw";
+
+    private static final String TASK_DEPLOY_STATE_FAILED =
+            
"rO0ABXNyADxvcmcuYXBhY2hlLnNlYXR1bm5lbC5lbmdpbmUuc2VydmVyLmV4ZWN1dGlvbi5UYXNr"
+                    + 
"RGVwbG95U3RhdGUkuMNro2ihAgIAAloAB3N1Y2Nlc3NMAAx0aHJvd2FibGVNc2d0ABJMamF2YS9s"
+                    + 
"YW5nL1N0cmluZzt4cAB0AC5qYXZhLmxhbmcuSWxsZWdhbFN0YXRlRXhjZXB0aW9uOiBkZXBsb3kg"
+                    + "ZmFpbGVk";
+
+    private static final TaskGroupLocation LOCATION = new 
TaskGroupLocation(1001L, 1, 20001L);
+
+    @Test
+    void testDeclaredSerialVersionUidsMatchPreviouslyGeneratedValues() {
+        Assertions.assertEquals(
+                -108652017022658969L,
+                
ObjectStreamClass.lookup(TaskExecutionState.class).getSerialVersionUID());
+        Assertions.assertEquals(
+                2646079648150626562L,
+                
ObjectStreamClass.lookup(TaskDeployState.class).getSerialVersionUID());
+    }
+
+    @Test
+    void testTaskExecutionStateReadsPreviousWireForm() throws Exception {
+        TaskExecutionState failed = (TaskExecutionState) 
deserialize(TASK_EXECUTION_STATE_FAILED);
+        Assertions.assertEquals(LOCATION, failed.getTaskGroupLocation());
+        Assertions.assertEquals(ExecutionState.FAILED, 
failed.getExecutionState());
+        Assertions.assertEquals(
+                "java.lang.IllegalStateException: source failed", 
failed.getThrowableMsg());
+
+        TaskExecutionState finished =
+                (TaskExecutionState) 
deserialize(TASK_EXECUTION_STATE_FINISHED);
+        Assertions.assertEquals(LOCATION, finished.getTaskGroupLocation());
+        Assertions.assertEquals(ExecutionState.FINISHED, 
finished.getExecutionState());
+        Assertions.assertNull(finished.getThrowableMsg());
+    }
+
+    @Test
+    void testTaskDeployStateReadsPreviousWireForm() throws Exception {
+        TaskDeployState success = (TaskDeployState) 
deserialize(TASK_DEPLOY_STATE_SUCCESS);
+        Assertions.assertTrue(success.isSuccess());
+        Assertions.assertNull(success.getThrowableMsg());
+
+        TaskDeployState failed = (TaskDeployState) 
deserialize(TASK_DEPLOY_STATE_FAILED);
+        Assertions.assertFalse(failed.isSuccess());
+        Assertions.assertEquals(
+                "java.lang.IllegalStateException: deploy failed", 
failed.getThrowableMsg());
+    }
+
+    @Test
+    void testCurrentTaskStatesRoundTripThroughHazelcastJavaSerialization() {
+        InternalSerializationService service =
+                (InternalSerializationService) new 
DefaultSerializationServiceBuilder().build();
+        try {
+            TaskExecutionState originalFailed =
+                    new TaskExecutionState(
+                            LOCATION,
+                            ExecutionState.FAILED,
+                            new IllegalStateException("source failed"));
+            TaskExecutionState failed = roundTrip(service, originalFailed);
+            Assertions.assertEquals(LOCATION, failed.getTaskGroupLocation());
+            Assertions.assertEquals(ExecutionState.FAILED, 
failed.getExecutionState());
+            Assertions.assertEquals(originalFailed.getThrowableMsg(), 
failed.getThrowableMsg());
+
+            TaskExecutionState finished =
+                    roundTrip(service, new TaskExecutionState(LOCATION, 
ExecutionState.FINISHED));
+            Assertions.assertEquals(LOCATION, finished.getTaskGroupLocation());
+            Assertions.assertEquals(ExecutionState.FINISHED, 
finished.getExecutionState());
+            Assertions.assertNull(finished.getThrowableMsg());
+
+            TaskDeployState success = roundTrip(service, 
TaskDeployState.success());
+            Assertions.assertTrue(success.isSuccess());
+            Assertions.assertNull(success.getThrowableMsg());
+
+            TaskDeployState originalDeployFailed =
+                    TaskDeployState.failed(new IllegalStateException("deploy 
failed"));
+            TaskDeployState deployFailed = roundTrip(service, 
originalDeployFailed);
+            Assertions.assertFalse(deployFailed.isSuccess());
+            Assertions.assertEquals(
+                    originalDeployFailed.getThrowableMsg(), 
deployFailed.getThrowableMsg());
+        } finally {
+            service.dispose();
+        }
+    }
+
+    private static <T> T roundTrip(InternalSerializationService service, T 
value) {
+        Data data = service.toData(value);
+        Assertions.assertEquals(
+                new JavaSerializer(false, false, null).getTypeId(),
+                data.getType(),
+                "Archived Java fixtures require the Hazelcast Java 
serialization fallback");
+        return service.toObject(data);
+    }
+
+    private static Object deserialize(String base64) throws Exception {
+        try (ObjectInputStream input =
+                new ObjectInputStream(
+                        new 
ByteArrayInputStream(Base64.getDecoder().decode(base64)))) {
+            return input.readObject();
+        }
+    }
+}

Reply via email to