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

lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-table-store.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c2f1b4  [FLINK-27540] Let FileStoreSource accept pre-planned manifest 
entries
5c2f1b4 is described below

commit 5c2f1b4ac8c503e9d4212ee6d65b63dd355f02c9
Author: Jane Chan <[email protected]>
AuthorDate: Tue May 10 20:26:12 2022 +0800

    [FLINK-27540] Let FileStoreSource accept pre-planned manifest entries
    
    This closes #111
---
 .../flink/table/store/connector/TableStore.java    |   3 +-
 .../store/connector/source/FileStoreSource.java    |  27 +-
 .../source/FileStoreSourceSplitGenerator.java      |  10 +-
 .../connector/source/FileStoreSourceTest.java      | 292 +++++++++++++++++++++
 .../flink/table/store/file/stats/FieldStats.java   |   2 +-
 .../store/file/utils/PartitionedManifestMeta.java  | 151 +++++++++++
 6 files changed, 481 insertions(+), 4 deletions(-)

diff --git 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/TableStore.java
 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/TableStore.java
index c17a309..ae128ff 100644
--- 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/TableStore.java
+++ 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/TableStore.java
@@ -320,7 +320,8 @@ public class TableStore {
                     continuousScanLatest,
                     projectedFields,
                     partitionPredicate,
-                    fieldPredicate);
+                    fieldPredicate,
+                    null);
         }
 
         private Source<RowData, ?, ?> buildSource() {
diff --git 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSource.java
 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSource.java
index 93faca2..4eacd73 100644
--- 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSource.java
+++ 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSource.java
@@ -18,6 +18,7 @@
 
 package org.apache.flink.table.store.connector.source;
 
+import org.apache.flink.annotation.VisibleForTesting;
 import org.apache.flink.api.connector.source.Boundedness;
 import org.apache.flink.api.connector.source.Source;
 import org.apache.flink.api.connector.source.SourceReader;
@@ -30,6 +31,7 @@ import org.apache.flink.table.store.file.Snapshot;
 import org.apache.flink.table.store.file.operation.FileStoreRead;
 import org.apache.flink.table.store.file.operation.FileStoreScan;
 import org.apache.flink.table.store.file.predicate.Predicate;
+import org.apache.flink.table.store.file.utils.PartitionedManifestMeta;
 
 import javax.annotation.Nullable;
 
@@ -61,6 +63,12 @@ public class FileStoreSource
 
     @Nullable private final Predicate fieldPredicate;
 
+    /**
+     * The partitioned manifest meta collected at planning phase when manual 
compaction is
+     * triggered.
+     */
+    @Nullable private final PartitionedManifestMeta specifiedPartManifests;
+
     public FileStoreSource(
             FileStore fileStore,
             boolean valueCountMode,
@@ -69,7 +77,8 @@ public class FileStoreSource
             boolean latestContinuous,
             @Nullable int[][] projectedFields,
             @Nullable Predicate partitionPredicate,
-            @Nullable Predicate fieldPredicate) {
+            @Nullable Predicate fieldPredicate,
+            @Nullable PartitionedManifestMeta specifiedPartManifests) {
         this.fileStore = fileStore;
         this.valueCountMode = valueCountMode;
         this.isContinuous = isContinuous;
@@ -78,6 +87,7 @@ public class FileStoreSource
         this.projectedFields = projectedFields;
         this.partitionPredicate = partitionPredicate;
         this.fieldPredicate = fieldPredicate;
+        this.specifiedPartManifests = specifiedPartManifests;
     }
 
     @Override
@@ -122,6 +132,15 @@ public class FileStoreSource
             SplitEnumeratorContext<FileStoreSourceSplit> context,
             PendingSplitsCheckpoint checkpoint) {
         FileStoreScan scan = fileStore.newScan();
+
+        if (specifiedPartManifests != null) {
+            return new StaticFileStoreSplitEnumerator(
+                    context,
+                    scan.snapshot(specifiedPartManifests.getSnapshotId()),
+                    new FileStoreSourceSplitGenerator()
+                            
.createSplits(specifiedPartManifests.getManifestEntries()));
+        }
+
         if (partitionPredicate != null) {
             scan.withPartitionFilter(partitionPredicate);
         }
@@ -182,4 +201,10 @@ public class FileStoreSource
     public PendingSplitsCheckpointSerializer 
getEnumeratorCheckpointSerializer() {
         return new PendingSplitsCheckpointSerializer(getSplitSerializer());
     }
+
+    @VisibleForTesting
+    @Nullable
+    PartitionedManifestMeta getSpecifiedPartManifests() {
+        return specifiedPartManifests;
+    }
 }
diff --git 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSourceSplitGenerator.java
 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSourceSplitGenerator.java
index f9b64ab..9e438e1 100644
--- 
a/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSourceSplitGenerator.java
+++ 
b/flink-table-store-connector/src/main/java/org/apache/flink/table/store/connector/source/FileStoreSourceSplitGenerator.java
@@ -18,9 +18,12 @@
 
 package org.apache.flink.table.store.connector.source;
 
+import org.apache.flink.table.data.binary.BinaryRowData;
+import org.apache.flink.table.store.file.data.DataFileMeta;
 import org.apache.flink.table.store.file.operation.FileStoreScan;
 
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -36,7 +39,12 @@ public class FileStoreSourceSplitGenerator {
     private final char[] currentId = "0000000000".toCharArray();
 
     public List<FileStoreSourceSplit> createSplits(FileStoreScan.Plan plan) {
-        return plan.groupByPartFiles().entrySet().stream()
+        return createSplits(plan.groupByPartFiles());
+    }
+
+    public List<FileStoreSourceSplit> createSplits(
+            Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> groupBy) {
+        return groupBy.entrySet().stream()
                 .flatMap(
                         pe ->
                                 pe.getValue().entrySet().stream()
diff --git 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/source/FileStoreSourceTest.java
 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/source/FileStoreSourceTest.java
new file mode 100644
index 0000000..e5f6584
--- /dev/null
+++ 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/source/FileStoreSourceTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.table.store.connector.source;
+
+import org.apache.flink.api.java.tuple.Tuple2;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.data.GenericRowData;
+import org.apache.flink.table.data.StringData;
+import org.apache.flink.table.data.binary.BinaryRowData;
+import org.apache.flink.table.runtime.typeutils.RowDataSerializer;
+import org.apache.flink.table.store.file.FileStore;
+import org.apache.flink.table.store.file.FileStoreImpl;
+import org.apache.flink.table.store.file.data.DataFileMeta;
+import 
org.apache.flink.table.store.file.mergetree.compact.DeduplicateMergeFunction;
+import org.apache.flink.table.store.file.mergetree.compact.MergeFunction;
+import 
org.apache.flink.table.store.file.mergetree.compact.ValueCountMergeFunction;
+import org.apache.flink.table.store.file.stats.FieldStats;
+import org.apache.flink.table.store.file.utils.PartitionedManifestMeta;
+import org.apache.flink.table.types.logical.BigIntType;
+import org.apache.flink.table.types.logical.DoubleType;
+import org.apache.flink.table.types.logical.IntType;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.RowType;
+import org.apache.flink.table.types.logical.VarCharType;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Test for {@link FileStoreSource}. */
+public class FileStoreSourceTest {
+
+    private static final RowType RECORD_TYPE =
+            RowType.of(
+                    new LogicalType[] {
+                        new IntType(), new VarCharType(), new DoubleType(), 
new VarCharType()
+                    },
+                    new String[] {"k0", "k1", "v0", "v1"});
+
+    @MethodSource("parameters")
+    @ParameterizedTest
+    public void testSerDe(boolean hasPk, boolean partitioned, boolean 
specified)
+            throws ClassNotFoundException, IOException {
+        FileStore fileStore = buildFileStore(hasPk, partitioned);
+        Long specifiedSnapshotId = specified ? 1L : null;
+        Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
specifiedManifestEntries =
+                specified ? buildManifestEntries(hasPk, partitioned) : null;
+        PartitionedManifestMeta partitionedManifestMeta =
+                specified
+                        ? new PartitionedManifestMeta(
+                                specifiedSnapshotId,
+                                specifiedManifestEntries,
+                                getPartitionType(partitioned).getFieldCount(),
+                                getKeyType(hasPk),
+                                getValueType(hasPk))
+                        : null;
+        FileStoreSource source =
+                new FileStoreSource(
+                        fileStore,
+                        !hasPk,
+                        true,
+                        Duration.ofSeconds(1).toMillis(),
+                        true,
+                        null,
+                        null,
+                        null,
+                        partitionedManifestMeta);
+        Object object = readObject(writeObject(source));
+        assertThat(object).isInstanceOf(FileStoreSource.class);
+        FileStoreSource deserialized = (FileStoreSource) object;
+        
assertThat(deserialized.getBoundedness()).isEqualTo(source.getBoundedness());
+        if (specified) {
+            assertThat(deserialized.getSpecifiedPartManifests())
+                    .isEqualTo(source.getSpecifiedPartManifests());
+        } else {
+            assertThat(deserialized.getSpecifiedPartManifests()).isNull();
+        }
+    }
+
+    private byte[] writeObject(FileStoreSource source) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(source);
+        oos.close();
+        return baos.toByteArray();
+    }
+
+    private Object readObject(byte[] bytes) throws IOException, 
ClassNotFoundException {
+        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        Object object = ois.readObject();
+        ois.close();
+        return object;
+    }
+
+    public static Stream<Arguments> parameters() {
+        // hasPk, partitioned, specified
+        return Stream.of(
+                Arguments.of(true, true, false),
+                Arguments.of(true, false, false),
+                Arguments.of(false, false, false),
+                Arguments.of(false, true, false),
+                Arguments.of(true, true, true),
+                Arguments.of(true, false, true),
+                Arguments.of(false, false, true),
+                Arguments.of(false, true, true));
+    }
+
+    private static FileStore buildFileStore(boolean hasPk, boolean 
partitioned) {
+        ObjectIdentifier tableIdentifier = ObjectIdentifier.of("cat", "db", 
"tbl");
+        String user = "user";
+        RowType partitionType = getPartitionType(partitioned);
+        RowType keyType = getKeyType(hasPk);
+        RowType valueType = getValueType(hasPk);
+        MergeFunction mergeFunction =
+                hasPk ? new DeduplicateMergeFunction() : new 
ValueCountMergeFunction();
+        return new FileStoreImpl(
+                tableIdentifier,
+                new Configuration(),
+                user,
+                partitionType,
+                keyType,
+                valueType,
+                mergeFunction);
+    }
+
+    private static Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
buildManifestEntries(
+            boolean hasPk, boolean partitioned) {
+        Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> manifestEntries = 
new HashMap<>();
+        Map<Integer, List<DataFileMeta>> bucketEntries = new HashMap<>();
+        int totalBuckets = new Random().nextInt(10) + 1;
+        for (int bucket = 0; bucket < totalBuckets; bucket++) {
+            List<DataFileMeta> metaList = new ArrayList<>();
+            for (Tuple2<BinaryRowData, BinaryRowData> tuple : 
genMinMax(hasPk)) {
+                metaList.add(genDataFileMeta(hasPk, tuple.f0, tuple.f1));
+            }
+            bucketEntries.put(bucket, metaList);
+        }
+        genPartitionValues(partitioned)
+                .forEach(partValue -> manifestEntries.put(partValue, 
bucketEntries));
+        return manifestEntries;
+    }
+
+    private static List<BinaryRowData> genPartitionValues(boolean partitioned) 
{
+        RowDataSerializer partSerializer = new 
RowDataSerializer(getPartitionType(partitioned));
+        if (partitioned) {
+            int partSize = new Random().nextInt(10) + 1;
+            List<BinaryRowData> partKeys = new ArrayList<>();
+            for (int i = 0; i < partSize; i++) {
+                partKeys.add(
+                        partSerializer
+                                .toBinaryRow(
+                                        GenericRowData.of(
+                                                StringData.fromString(
+                                                        
UUID.randomUUID().toString())))
+                                .copy());
+            }
+            return partKeys;
+        }
+        return 
Collections.singletonList(partSerializer.toBinaryRow(GenericRowData.of()).copy());
+    }
+
+    private static List<Tuple2<BinaryRowData, BinaryRowData>> 
genMinMax(boolean hasPk) {
+        RowDataSerializer keySerializer = new 
RowDataSerializer(getKeyType(hasPk));
+        int size = new Random().nextInt(20);
+        List<Tuple2<BinaryRowData, BinaryRowData>> minMaxKeys = new 
ArrayList<>();
+        for (int i = 0; i < size; i++) {
+            int k0 = new Random().nextInt(1000);
+            String k1 = UUID.randomUUID().toString();
+            BinaryRowData keyMin;
+            BinaryRowData keyMax;
+            if (hasPk) {
+                keyMin =
+                        keySerializer.toBinaryRow(GenericRowData.of(k0, 
StringData.fromString(k1)));
+                keyMax =
+                        keySerializer.toBinaryRow(
+                                GenericRowData.of(k0 + 100, 
StringData.fromString(k1)));
+            } else {
+                double v0 = new Random().nextDouble();
+                String v1 = UUID.randomUUID().toString();
+                keyMin =
+                        keySerializer.toBinaryRow(
+                                GenericRowData.of(
+                                        k0,
+                                        StringData.fromString(k1),
+                                        v0,
+                                        StringData.fromString(v1)));
+                keyMax =
+                        keySerializer.toBinaryRow(
+                                GenericRowData.of(
+                                        k0 + 100,
+                                        StringData.fromString(k1),
+                                        v0 + 1.5,
+                                        StringData.fromString(v1)));
+            }
+            minMaxKeys.add(Tuple2.of(keyMin, keyMax));
+        }
+        return minMaxKeys;
+    }
+
+    private static DataFileMeta genDataFileMeta(
+            boolean hasPk, BinaryRowData keyMin, BinaryRowData keyMax) {
+        long seqNumber = new Random().nextLong() + 1;
+        FieldStats k0Stats = new FieldStats(keyMin.getInt(0), 
keyMax.getInt(1), 0);
+        FieldStats k1Stats = new FieldStats(keyMin.getString(1), 
keyMax.getString(1), 0);
+        FieldStats v0Status =
+                new FieldStats(
+                        hasPk ? null : keyMin.getDouble(2), hasPk ? null : 
keyMax.getDouble(2), 0);
+        FieldStats v1Status =
+                new FieldStats(
+                        hasPk ? null : keyMin.getString(3), hasPk ? null : 
keyMax.getString(3), 0);
+        long count = new Random().nextLong();
+        return new DataFileMeta(
+                "data-" + UUID.randomUUID(),
+                new Random().nextInt(100),
+                new Random().nextInt(100),
+                keyMin,
+                keyMax,
+                hasPk
+                        ? new FieldStats[] {k0Stats, k1Stats}
+                        : new FieldStats[] {
+                            k0Stats, k1Stats,
+                            v0Status, v1Status
+                        },
+                hasPk
+                        ? new FieldStats[] {
+                            k0Stats, k1Stats,
+                            v0Status, v1Status
+                        }
+                        : new FieldStats[] {
+                            new FieldStats(count, count + new 
Random().nextInt(100), 0)
+                        },
+                seqNumber,
+                seqNumber + new Random().nextInt(100),
+                new Random().nextInt(4));
+    }
+
+    private static RowType getKeyType(boolean hasPk) {
+        return hasPk
+                ? RowType.of(
+                        new LogicalType[] {new IntType(), new VarCharType()},
+                        new String[] {"k0", "k1"})
+                : RECORD_TYPE;
+    }
+
+    private static RowType getValueType(boolean hasPk) {
+        return hasPk
+                ? RECORD_TYPE
+                : RowType.of(
+                        new LogicalType[] {new BigIntType(false)}, new 
String[] {"_VALUE_COUNT"});
+    }
+
+    private static RowType getPartitionType(boolean partitioned) {
+        return partitioned
+                ? RowType.of(new LogicalType[] {new VarCharType()}, new 
String[] {"k1"})
+                : RowType.of();
+    }
+}
diff --git 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/stats/FieldStats.java
 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/stats/FieldStats.java
index 62843bf..55a0742 100644
--- 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/stats/FieldStats.java
+++ 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/stats/FieldStats.java
@@ -29,7 +29,7 @@ public class FieldStats {
     @Nullable private final Object maxValue;
     private final long nullCount;
 
-    public FieldStats(Object minValue, Object maxValue, long nullCount) {
+    public FieldStats(@Nullable Object minValue, @Nullable Object maxValue, 
long nullCount) {
         this.minValue = minValue;
         this.maxValue = maxValue;
         this.nullCount = nullCount;
diff --git 
a/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/utils/PartitionedManifestMeta.java
 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/utils/PartitionedManifestMeta.java
new file mode 100644
index 0000000..4968642
--- /dev/null
+++ 
b/flink-table-store-core/src/main/java/org/apache/flink/table/store/file/utils/PartitionedManifestMeta.java
@@ -0,0 +1,151 @@
+/*
+ * 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.table.store.file.utils;
+
+import org.apache.flink.core.memory.DataInputViewStreamWrapper;
+import org.apache.flink.core.memory.DataOutputViewStreamWrapper;
+import org.apache.flink.table.data.binary.BinaryRowData;
+import org.apache.flink.table.runtime.typeutils.BinaryRowDataSerializer;
+import org.apache.flink.table.store.file.data.DataFileMeta;
+import org.apache.flink.table.store.file.data.DataFileMetaSerializer;
+import org.apache.flink.table.types.logical.RowType;
+import org.apache.flink.util.Preconditions;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/** Manifest entries per partitioned with the corresponding snapshot id. */
+public class PartitionedManifestMeta implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** The latest snapshot id seen at planning phase when manual compaction 
is triggered. */
+    private final Long snapshotId;
+
+    /** The manifest entries collected at planning phase when manual 
compaction is triggered. */
+    private transient Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
manifestEntries;
+
+    private final int partFieldCount;
+    private final RowType keyType;
+    private final RowType valueType;
+
+    public PartitionedManifestMeta(
+            Long snapshotId,
+            Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
specifiedManifestEntries,
+            int partFieldCount,
+            RowType keyType,
+            RowType valueType) {
+        Preconditions.checkNotNull(snapshotId, "Specified snapshot should not 
be null.");
+        Preconditions.checkNotNull(
+                specifiedManifestEntries, "Specified manifest entries should 
not be null.");
+        this.snapshotId = snapshotId;
+        this.manifestEntries = specifiedManifestEntries;
+        this.partFieldCount = partFieldCount;
+        this.keyType = keyType;
+        this.valueType = valueType;
+    }
+
+    public Long getSnapshotId() {
+        return snapshotId;
+    }
+
+    public Map<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
getManifestEntries() {
+        return manifestEntries;
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+
+        BinaryRowDataSerializer partSerializer = new 
BinaryRowDataSerializer(partFieldCount);
+        DataFileMetaSerializer metaSerializer = new 
DataFileMetaSerializer(keyType, valueType);
+        DataOutputViewStreamWrapper view = new 
DataOutputViewStreamWrapper(out);
+        view.writeInt(manifestEntries.size());
+        for (Map.Entry<BinaryRowData, Map<Integer, List<DataFileMeta>>> 
partEntry :
+                manifestEntries.entrySet()) {
+            partSerializer.serialize(partEntry.getKey(), view);
+            Map<Integer, List<DataFileMeta>> bucketEntry = 
partEntry.getValue();
+            view.writeInt(bucketEntry.size());
+            for (Map.Entry<Integer, List<DataFileMeta>> entry : 
bucketEntry.entrySet()) {
+                view.writeInt(entry.getKey());
+                view.writeInt(entry.getValue().size());
+                for (DataFileMeta meta : entry.getValue()) {
+                    metaSerializer.serialize(meta, view);
+                }
+            }
+        }
+    }
+
+    private void readObject(ObjectInputStream in) throws 
ClassNotFoundException, IOException {
+        in.defaultReadObject();
+
+        manifestEntries = new HashMap<>();
+        BinaryRowDataSerializer partSerializer = new 
BinaryRowDataSerializer(partFieldCount);
+        DataFileMetaSerializer metaSerializer = new 
DataFileMetaSerializer(keyType, valueType);
+        DataInputViewStreamWrapper view = new DataInputViewStreamWrapper(in);
+        int partitionNum = view.readInt();
+        for (int i = 0; i < partitionNum; i++) {
+            BinaryRowData partition = partSerializer.deserialize(view);
+            Map<Integer, List<DataFileMeta>> bucketEntry = new HashMap<>();
+            int bucketNum = view.readInt();
+            for (int j = 0; j < bucketNum; j++) {
+                int bucket = view.readInt();
+                int entryNum = view.readInt();
+                if (entryNum == 0) {
+                    bucketEntry.put(bucket, Collections.emptyList());
+                } else {
+                    List<DataFileMeta> metas = new ArrayList<>();
+                    for (int k = 0; k < entryNum; k++) {
+                        metas.add(metaSerializer.deserialize(view));
+                    }
+                    bucketEntry.put(bucket, metas);
+                }
+            }
+            manifestEntries.put(partition, bucketEntry);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PartitionedManifestMeta)) {
+            return false;
+        }
+        PartitionedManifestMeta that = (PartitionedManifestMeta) o;
+        return partFieldCount == that.partFieldCount
+                && snapshotId.equals(that.snapshotId)
+                && manifestEntries.equals(that.manifestEntries)
+                && keyType.equals(that.keyType)
+                && valueType.equals(that.valueType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(snapshotId, manifestEntries, partFieldCount, 
keyType, valueType);
+    }
+}

Reply via email to