diff --git a/runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateSerde.java b/runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateSerde.java
index 02c9a06b3..56e1ff173 100644
--- a/runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateSerde.java
+++ b/runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateSerde.java
@@ -19,16 +19,24 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.flink.agents.api.Event;
import org.apache.flink.agents.api.InputEvent;
import org.apache.flink.agents.api.OutputEvent;
+import org.apache.flink.agents.api.context.MemoryUpdate;
import org.apache.flink.agents.runtime.operator.ActionTask;
import java.io.IOException;
+import java.util.Base64;
/**
* Backend-agnostic serializer/deserializer for {@link ActionState}.
@@ -39,6 +47,13 @@
*/
public final class ActionStateSerde {
+ /**
+ * Reserved envelope key marking a base64-encoded {@code byte[]} {@link MemoryUpdate} value. A
+ * namespaced key keeps the residual collision with a genuine single-entry user {@code Map}
+ * negligible and clearly framework-reserved.
+ */
+ private static final String MEMORY_UPDATE_BYTES_KEY = "__flink_agents_bytes__";
+
private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
private ActionStateSerde() {}
@@ -74,6 +89,9 @@ private static ObjectMapper createObjectMapper() {
module.addSerializer(ActionTask.class, new ActionTaskSerializer());
mapper.registerModule(module);
+ // Preserve byte[] MemoryUpdate values across the untyped Object round-trip
+ mapper.addMixIn(MemoryUpdate.class, MemoryUpdateValueMixin.class);
+
return mapper;
}
@@ -92,4 +110,61 @@ public void serialize(ActionTask value, JsonGenerator gen, SerializerProvider se
gen.writeNull();
}
}
+
+ /** Binds the byte[]-preserving (de)serializer to {@link MemoryUpdate#getValue()} only. */
+ abstract static class MemoryUpdateValueMixin {
+ @JsonSerialize(using = MemoryUpdateValueSerializer.class)
+ @JsonDeserialize(using = MemoryUpdateValueDeserializer.class)
+ Object value;
+ }
+
+ /**
+ * Serializes a {@code byte[]} {@link MemoryUpdate} value as a one-key base64 envelope so it can
+ * be recovered as a {@code byte[]} rather than the base64 {@code String} that untyped {@code
+ * Object} serialization would yield; all other types delegate to default serialization and stay
+ * byte-identical on disk.
+ *
+ *
Only a top-level {@code byte[]} value is preserved; a {@code byte[]} nested inside a
+ * {@code List} or {@code Map} value goes through the default serializers and is not preserved.
+ */
+ static class MemoryUpdateValueSerializer extends JsonSerializer