You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@flink.apache.org by "avalonalex (via GitHub)" <gi...@apache.org> on 2026/07/23 18:25:11 UTC

[PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

avalonalex opened a new pull request, #28810:
URL: https://github.com/apache/flink/pull/28810

   ## What is the purpose of the change
   
   `VariantSerializer#deserialize` fills the value and metadata byte arrays with `DataInputView#read(byte[])` and ignores the returned count. `read()` may legally return fewer bytes than requested when the array straddles an internal buffer or compression-frame boundary; the array tail is then left unfilled and the stream desynchronizes. Restoring keyed state containing large Variant values after a parallelism change (key-group re-partitioning) fails intermittently with MALFORMED_VARIANT (misaligned read lands on a bad metadata header) or OutOfMemoryError (misaligned read lands on a length prefix), and jobs can crash-loop on restore. Observed in production on Flink 2.2.1 with a window operator keyed by Variant: a different subtask failed each restore attempt and the same checkpoint later restored cleanly — consistent with a nondeterministic short read, not a format mismatch.
   
   ## Brief change log
   
     - `VariantSerializer#deserialize` uses `readFully` for the value and metadata arrays, matching every other array-reading serializer in flink-core (e.g. `BytePrimitiveArraySerializer`).
     - Added `VariantSerializerShortReadTest`: a `DataInputView` wrapper that legally returns short reads; deserialize corrupts data before the fix and round-trips correctly after.
   
   ## Verifying this change
   
   This change added tests and can be verified as follows:
   
     - New unit test `VariantSerializerShortReadTest` fails on the previous implementation and passes with the fix.
     - Existing `VariantSerializerTest` and `VariantSerializerUpgradeTest` pass unchanged.
   
   ## Does this pull request potentially affect one of the following parts:
   
     - Dependencies (does it add or upgrade a dependency): no
     - The public API, i.e., is any changed class annotated with `@Public(Evolving)`: no
     - The serializers: yes — behavior only; the serialized wire format is unchanged, so the change is fully snapshot-compatible and needs no migration or serializer-upgrade path
     - The runtime per-record code paths (performance sensitive): yes — `deserialize` switches `read` to `readFully`, the same pattern the other array-reading serializers use; no extra work when the first read fills the buffer
     - Anything that affects deployment or recovery: JobManager (and its components), Checkpointing, Kubernetes/Yarn, ZooKeeper: yes — fixes restore correctness for checkpoints/savepoints containing Variant keyed state; no format change
     - The S3 file system connector: no
   
   ## Documentation
   
     - Does this pull request introduce a new feature? no
     - If yes, how is the feature documented? not applicable
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes (Claude Code)
   
   Generated-by: Claude Code (Anthropic Claude)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

Posted by "flinkbot (via GitHub)" <gi...@apache.org>.
flinkbot commented on PR #28810:
URL: https://github.com/apache/flink/pull/28810#issuecomment-5061997788

   <!--
   Meta data
   {
     "version" : 1,
     "metaDataEntries" : [ {
       "hash" : "8c21644b6920df27da4965df5eebb3869b49003c",
       "status" : "UNKNOWN",
       "url" : "TBD",
       "triggerID" : "8c21644b6920df27da4965df5eebb3869b49003c",
       "triggerType" : "PUSH"
     } ]
   }-->
   ## CI report:
   
   * 8c21644b6920df27da4965df5eebb3869b49003c UNKNOWN
   
   <details>
   <summary>Bot commands</summary>
     The @flinkbot bot supports the following commands:
   
    - `@flinkbot run azure` re-run the last Azure build
   </details>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

Posted by "ferenc-csaky (via GitHub)" <gi...@apache.org>.
ferenc-csaky commented on PR #28810:
URL: https://github.com/apache/flink/pull/28810#issuecomment-5134352372

   Also, can you pls. open backport PR-s with the same content against the `release-2.3` and `release-2.2` branches? I think this would worth fixing in those versions too.
   
   You can omit the PR description there, just link this one and prefix it with the backport version, e.g. `[BP-2.2]` like https://github.com/apache/flink/pull/28840.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

Posted by "ferenc-csaky (via GitHub)" <gi...@apache.org>.
ferenc-csaky commented on code in PR #28810:
URL: https://github.com/apache/flink/pull/28810#discussion_r3684947898


##########
flink-core/src/test/java/org/apache/flink/api/common/typeutils/base/VariantSerializerShortReadTest.java:
##########
@@ -0,0 +1,109 @@
+/*
+ * 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.flink.api.common.typeutils.base;
+
+import org.apache.flink.core.memory.DataInputViewStreamWrapper;
+import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
+import org.apache.flink.types.variant.Variant;
+import org.apache.flink.types.variant.VariantBuilder;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Regression tests for FLINK-40227: {@link VariantSerializer#deserialize} must read each of a
+ * Variant's value/metadata byte arrays to completion. {@code DataInputView#read(byte[])} is allowed
+ * to return a partial (short) read when the array straddles a buffer or compression-frame boundary
+ * in the source stream, and ignoring that count corrupts the Variant. This is how large Variant
+ * keys fail to restore from a checkpoint key-group stream on rescale (surfacing as
+ * MALFORMED_VARIANT or an OutOfMemoryError).
+ */
+class VariantSerializerShortReadTest {
+
+    private static final VariantSerializer SERIALIZER = VariantSerializer.INSTANCE;
+
+    /**
+     * A Variant with many keys so both its value and its metadata (the key dictionary) span many
+     * read chunks.
+     */
+    private static Variant largeVariant() {
+        VariantBuilder builder = Variant.newBuilder();
+        VariantBuilder.VariantObjectBuilder object = builder.object();
+        for (int i = 0; i < 2048; i++) {
+            object.add("resource-key-" + i, builder.of("resource-value-payload-" + i));
+        }
+        return object.build();
+    }
+
+    private static byte[] serialize(Variant variant) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        SERIALIZER.serialize(variant, new DataOutputViewStreamWrapper(out));
+        return out.toByteArray();
+    }
+
+    @Test
+    void deserializeRecoversFromShortReads() throws IOException {
+        Variant original = largeVariant();
+        byte[] bytes = serialize(original);
+
+        // A source that hands back at most one byte per array read forces the short-read path on
+        // every value/metadata read; the stock read(byte[]) would silently drop the remainder and
+        // build a corrupt Variant.

Review Comment:
   Let's remove this. This is just noise, the code is fairly straightforward.



##########
flink-core/src/test/java/org/apache/flink/api/common/typeutils/base/VariantSerializerShortReadTest.java:
##########
@@ -0,0 +1,109 @@
+/*
+ * 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.flink.api.common.typeutils.base;
+
+import org.apache.flink.core.memory.DataInputViewStreamWrapper;
+import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
+import org.apache.flink.types.variant.Variant;
+import org.apache.flink.types.variant.VariantBuilder;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Regression tests for FLINK-40227: {@link VariantSerializer#deserialize} must read each of a
+ * Variant's value/metadata byte arrays to completion. {@code DataInputView#read(byte[])} is allowed
+ * to return a partial (short) read when the array straddles a buffer or compression-frame boundary
+ * in the source stream, and ignoring that count corrupts the Variant. This is how large Variant
+ * keys fail to restore from a checkpoint key-group stream on rescale (surfacing as
+ * MALFORMED_VARIANT or an OutOfMemoryError).
+ */
+class VariantSerializerShortReadTest {
+
+    private static final VariantSerializer SERIALIZER = VariantSerializer.INSTANCE;
+
+    /**
+     * A Variant with many keys so both its value and its metadata (the key dictionary) span many
+     * read chunks.
+     */
+    private static Variant largeVariant() {
+        VariantBuilder builder = Variant.newBuilder();
+        VariantBuilder.VariantObjectBuilder object = builder.object();
+        for (int i = 0; i < 2048; i++) {
+            object.add("resource-key-" + i, builder.of("resource-value-payload-" + i));
+        }
+        return object.build();
+    }
+
+    private static byte[] serialize(Variant variant) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        SERIALIZER.serialize(variant, new DataOutputViewStreamWrapper(out));
+        return out.toByteArray();
+    }
+
+    @Test
+    void deserializeRecoversFromShortReads() throws IOException {
+        Variant original = largeVariant();
+        byte[] bytes = serialize(original);
+
+        // A source that hands back at most one byte per array read forces the short-read path on
+        // every value/metadata read; the stock read(byte[]) would silently drop the remainder and
+        // build a corrupt Variant.
+        DataInputViewStreamWrapper source =
+                new DataInputViewStreamWrapper(new OneByteAtATimeInputStream(bytes));
+
+        Variant restored = SERIALIZER.deserialize(source);
+
+        assertThat(restored).isEqualTo(original);
+    }
+
+    @Test
+    void deserializeThrowsEofOnTruncatedStream() throws IOException {
+        byte[] bytes = serialize(largeVariant());
+        byte[] truncated = Arrays.copyOf(bytes, bytes.length - 1);
+
+        DataInputViewStreamWrapper source =
+                new DataInputViewStreamWrapper(new ByteArrayInputStream(truncated));
+
+        // A genuinely short checkpoint must fail loudly, not build a corrupt Variant from a
+        // partially filled buffer.

Review Comment:
   Let's remove this. This is just noise, the code is fairly straightforward.



##########
flink-core/src/test/java/org/apache/flink/api/common/typeutils/base/VariantSerializerShortReadTest.java:
##########
@@ -0,0 +1,109 @@
+/*
+ * 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.flink.api.common.typeutils.base;
+
+import org.apache.flink.core.memory.DataInputViewStreamWrapper;
+import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
+import org.apache.flink.types.variant.Variant;
+import org.apache.flink.types.variant.VariantBuilder;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Regression tests for FLINK-40227: {@link VariantSerializer#deserialize} must read each of a
+ * Variant's value/metadata byte arrays to completion. {@code DataInputView#read(byte[])} is allowed
+ * to return a partial (short) read when the array straddles a buffer or compression-frame boundary
+ * in the source stream, and ignoring that count corrupts the Variant. This is how large Variant
+ * keys fail to restore from a checkpoint key-group stream on rescale (surfacing as
+ * MALFORMED_VARIANT or an OutOfMemoryError).
+ */

Review Comment:
   Let's make this more concise, with less implementation details about the fix, and let's remove the explicit Jira reference. For now it's obvious from the commit, and if that changes in the future, then so does this comment should be, so let's try to minimize maintenance cost in the first place.



##########
flink-core/src/main/java/org/apache/flink/api/common/typeutils/base/VariantSerializer.java:
##########
@@ -81,8 +81,14 @@ public Variant deserialize(DataInputView source) throws IOException {
         byte[] value = new byte[valueLength];
         byte[] metaData = new byte[metadataLength];
 
-        source.read(value);
-        source.read(metaData);
+        // Use readFully, not read: DataInputView#read(byte[]) may return a partial (short) read
+        // when the array straddles a buffer/compression-frame boundary in the source stream, and
+        // its returned count must not be ignored. A short read here desyncs the stream and surfaces
+        // as MALFORMED_VARIANT / OutOfMemoryError when restoring large Variant keys from a
+        // checkpoint key-group stream on rescale. readFully loops until the buffer is filled and
+        // throws EOFException only on a genuine end-of-stream (a truly truncated checkpoint).

Review Comment:
   Let's make this comment more concise.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

Posted by "avalonalex (via GitHub)" <gi...@apache.org>.
avalonalex commented on PR #28810:
URL: https://github.com/apache/flink/pull/28810#issuecomment-5134928187

   Thanks for the review! Trimmed the comments as suggested. Backports are open: #28851 (release-2.3) and #28852 (release-2.2).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [FLINK-40227][core] Use readFully to read Variant value/metadata in VariantSerializer.deserialize [flink]

Posted by "ferenc-csaky (via GitHub)" <gi...@apache.org>.
ferenc-csaky merged PR #28810:
URL: https://github.com/apache/flink/pull/28810


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org