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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to