-
Notifications
You must be signed in to change notification settings - Fork 568
[hotfix] Fix union read can't restore issue #2321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| package org.apache.fluss.flink.source.state; | ||
|
|
||
| import org.apache.fluss.annotation.VisibleForTesting; | ||
| import org.apache.fluss.flink.source.split.SourceSplitBase; | ||
| import org.apache.fluss.flink.source.split.SourceSplitSerializer; | ||
| import org.apache.fluss.lake.source.LakeSource; | ||
|
|
@@ -37,17 +38,21 @@ | |
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| import static org.apache.fluss.utils.Preconditions.checkNotNull; | ||
|
|
||
| /** A serializer for {@link SourceEnumeratorState}. */ | ||
| public class FlussSourceEnumeratorStateSerializer | ||
| implements SimpleVersionedSerializer<SourceEnumeratorState> { | ||
|
|
||
| @Nullable private final LakeSource<LakeSplit> lakeSource; | ||
|
|
||
| private static final int VERSION_0 = 0; | ||
| private static final int VERSION_1 = 1; | ||
|
|
||
| private static final ThreadLocal<DataOutputSerializer> SERIALIZER_CACHE = | ||
| ThreadLocal.withInitial(() -> new DataOutputSerializer(64)); | ||
|
|
||
| private static final int CURRENT_VERSION = VERSION_0; | ||
| private static final int CURRENT_VERSION = VERSION_1; | ||
|
|
||
| public FlussSourceEnumeratorStateSerializer(LakeSource<LakeSplit> lakeSource) { | ||
| this.lakeSource = lakeSource; | ||
|
|
@@ -61,11 +66,28 @@ public int getVersion() { | |
| @Override | ||
| public byte[] serialize(SourceEnumeratorState state) throws IOException { | ||
| final DataOutputSerializer out = SERIALIZER_CACHE.get(); | ||
|
|
||
| // serialize assign bucket and partitions | ||
| serializeAssignBucketAndPartitions( | ||
| out, state.getAssignedBuckets(), state.getAssignedPartitions()); | ||
|
|
||
| // serialize remain hybrid lake splits | ||
| serializeRemainingHybridLakeFlussSplits(out, state); | ||
|
|
||
| final byte[] result = out.getCopyOfBuffer(); | ||
| out.clear(); | ||
| return result; | ||
| } | ||
|
|
||
| private void serializeAssignBucketAndPartitions( | ||
| DataOutputSerializer out, | ||
| Set<TableBucket> assignedBuckets, | ||
| Map<Long, String> assignedPartitions) | ||
| throws IOException { | ||
| // write assigned buckets | ||
| out.writeInt(state.getAssignedBuckets().size()); | ||
| for (TableBucket tableBucket : state.getAssignedBuckets()) { | ||
| out.writeInt(assignedBuckets.size()); | ||
| for (TableBucket tableBucket : assignedBuckets) { | ||
| out.writeLong(tableBucket.getTableId()); | ||
|
|
||
| // write partition | ||
| // if partition is not null | ||
| if (tableBucket.getPartitionId() != null) { | ||
|
|
@@ -78,24 +100,29 @@ public byte[] serialize(SourceEnumeratorState state) throws IOException { | |
| out.writeInt(tableBucket.getBucket()); | ||
| } | ||
| // write assigned partitions | ||
| out.writeInt(state.getAssignedPartitions().size()); | ||
| for (Map.Entry<Long, String> entry : state.getAssignedPartitions().entrySet()) { | ||
| out.writeInt(assignedPartitions.size()); | ||
| for (Map.Entry<Long, String> entry : assignedPartitions.entrySet()) { | ||
| out.writeLong(entry.getKey()); | ||
| out.writeUTF(entry.getValue()); | ||
| } | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| protected byte[] serializeV0(SourceEnumeratorState state) throws IOException { | ||
| final DataOutputSerializer out = SERIALIZER_CACHE.get(); | ||
| serializeAssignBucketAndPartitions( | ||
| out, state.getAssignedBuckets(), state.getAssignedPartitions()); | ||
| if (lakeSource != null) { | ||
| serializeRemainingHybridLakeFlussSplits(out, state); | ||
| } | ||
|
|
||
| final byte[] result = out.getCopyOfBuffer(); | ||
| out.clear(); | ||
| return result; | ||
| } | ||
|
|
||
| @Override | ||
| public SourceEnumeratorState deserialize(int version, byte[] serialized) throws IOException { | ||
| if (version != VERSION_0) { | ||
| if (version != VERSION_0 && version != CURRENT_VERSION) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When we support version 3 later, state with version 3 cannot downgrade. We have met this problem in many connectors. @leonardBang , CC, should we stop higher version's state? And I also found a problem : we can never downgrade later to version 1. Because in later code will version 1 state: version != VERSION_0 is always true. Maybe we should add this to upgrade document later.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1: so, what's the best pratice in here? Always make the version check pass and make sure version3 compitable with v2? From kafka connector code, seems the kafka connector will still have the problem? 2: what do you mean by saying
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, that's clearer |
||
| throw new IOException("Unknown version or corrupt state: " + version); | ||
| } | ||
| final DataInputDeserializer in = new DataInputDeserializer(serialized); | ||
|
|
@@ -124,8 +151,18 @@ public SourceEnumeratorState deserialize(int version, byte[] serialized) throws | |
| } | ||
|
|
||
| List<SourceSplitBase> remainingHybridLakeFlussSplits = null; | ||
| if (lakeSource != null) { | ||
| // todo: add a ut for serialize remaining hybrid lake fluss splits | ||
|
|
||
| if (version == VERSION_0) { | ||
| // For VERSION_0, deserialize remaining hybrid lake Fluss splits only when lakeSource is | ||
| // not null. | ||
| if (lakeSource != null) { | ||
| remainingHybridLakeFlussSplits = deserializeRemainingHybridLakeFlussSplits(in); | ||
| } | ||
| } else { | ||
| // For VERSION_1 and later, always attempt to deserialize remaining hybrid lake/Fluss | ||
| // splits. The serialized state encodes their presence via a boolean flag, so this | ||
| // logic no longer depends on the lakeSource flag. This unconditional deserialization | ||
| // is the intended behavior change compared to VERSION_0. | ||
| remainingHybridLakeFlussSplits = deserializeRemainingHybridLakeFlussSplits(in); | ||
| } | ||
|
|
||
|
|
@@ -160,7 +197,11 @@ private List<SourceSplitBase> deserializeRemainingHybridLakeFlussSplits( | |
| if (in.readBoolean()) { | ||
| int numSplits = in.readInt(); | ||
| List<SourceSplitBase> splits = new ArrayList<>(numSplits); | ||
| SourceSplitSerializer sourceSplitSerializer = new SourceSplitSerializer(lakeSource); | ||
| SourceSplitSerializer sourceSplitSerializer = | ||
| new SourceSplitSerializer( | ||
| checkNotNull( | ||
| lakeSource, | ||
| "lake source must not be null when there are hybrid lake splits.")); | ||
| int version = in.readInt(); | ||
| for (int i = 0; i < numSplits; i++) { | ||
| int splitSizeInBytes = in.readInt(); | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would like to add what's the different with different version? such as:
