Repository navigation
[server] Register external KV snapshots and restore replicas #4242
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
Open
platinumhamburg
wants to merge
8
commits into
apache:main
Choose a base branch
from
platinumhamburg:feature-kv-snapshot-import
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
5bac15d
[server] Register external KV snapshots and restore replicas
platinumhamburg 6dcfff8
[server] Align snapshot recovery and local log initialization
platinumhamburg 4287970
[server] Preserve leader epoch semantics during snapshot recovery
platinumhamburg 9f33811
[server] Encapsulate external snapshot registration in the store
platinumhamburg 4a1fb0a
[server] Resolve snapshot metadata before leader activation
platinumhamburg 6bffe3a
[test] Stabilize snapshot import CI tests
platinumhamburg b3dcdac
[server] Validate external KV snapshot files
platinumhamburg 59b9e28
[server] Reject out-of-range snapshot metadata versions
platinumhamburg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
279 changes: 279 additions & 0 deletions
279
fluss-common/src/main/java/org/apache/fluss/metadata/KvSnapshotFileMetadata.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,279 @@ | ||
| /* | ||
| * 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.fluss.metadata; | ||
|
|
||
| import org.apache.fluss.annotation.Internal; | ||
|
|
||
| import javax.annotation.Nullable; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| import static org.apache.fluss.utils.Preconditions.checkArgument; | ||
| import static org.apache.fluss.utils.Preconditions.checkNotNull; | ||
|
|
||
| /** Immutable standard metadata stored in a KV snapshot {@code _METADATA} file. */ | ||
| @Internal | ||
| public final class KvSnapshotFileMetadata { | ||
|
|
||
| private final TableBucket tableBucket; | ||
| private final long snapshotId; | ||
| private final String snapshotLocation; | ||
| private final List<FileHandle> sharedFiles; | ||
| private final List<FileHandle> privateFiles; | ||
| private final long incrementalSize; | ||
| private final long logOffset; | ||
| private final @Nullable Long rowCount; | ||
| private final @Nullable List<AutoIncrementRange> autoIncrementRanges; | ||
|
|
||
| /** Creates immutable standard KV snapshot file metadata. */ | ||
| public KvSnapshotFileMetadata( | ||
| TableBucket tableBucket, | ||
| long snapshotId, | ||
| String snapshotLocation, | ||
| List<FileHandle> sharedFiles, | ||
| List<FileHandle> privateFiles, | ||
| long incrementalSize, | ||
| long logOffset, | ||
| @Nullable Long rowCount, | ||
| @Nullable List<AutoIncrementRange> autoIncrementRanges) { | ||
| this.tableBucket = checkNotNull(tableBucket, "Table bucket must not be null."); | ||
| this.snapshotId = snapshotId; | ||
| this.snapshotLocation = | ||
| checkNotNull(snapshotLocation, "Snapshot location must not be null."); | ||
| this.sharedFiles = immutableCopy(sharedFiles, "Shared files must not be null."); | ||
| this.privateFiles = immutableCopy(privateFiles, "Private files must not be null."); | ||
| this.incrementalSize = incrementalSize; | ||
| this.logOffset = logOffset; | ||
| this.rowCount = rowCount; | ||
| this.autoIncrementRanges = | ||
| autoIncrementRanges == null | ||
| ? null | ||
| : immutableCopy( | ||
| autoIncrementRanges, | ||
| "Auto-increment ranges must not contain null entries."); | ||
| } | ||
|
|
||
| /** Returns the table bucket described by this metadata. */ | ||
| public TableBucket getTableBucket() { | ||
| return tableBucket; | ||
| } | ||
|
|
||
| /** Returns the snapshot ID. */ | ||
| public long getSnapshotId() { | ||
| return snapshotId; | ||
| } | ||
|
|
||
| /** Returns the snapshot location. */ | ||
| public String getSnapshotLocation() { | ||
| return snapshotLocation; | ||
| } | ||
|
|
||
| /** Returns the shared snapshot files. */ | ||
| public List<FileHandle> getSharedFiles() { | ||
| return sharedFiles; | ||
| } | ||
|
|
||
| /** Returns the private snapshot files. */ | ||
| public List<FileHandle> getPrivateFiles() { | ||
| return privateFiles; | ||
| } | ||
|
|
||
| /** Returns the incremental snapshot size. */ | ||
| public long getIncrementalSize() { | ||
| return incrementalSize; | ||
| } | ||
|
|
||
| /** Returns the next log offset at snapshot time. */ | ||
| public long getLogOffset() { | ||
| return logOffset; | ||
| } | ||
|
|
||
| /** Returns the row count when present. */ | ||
| @Nullable | ||
| public Long getRowCount() { | ||
| return rowCount; | ||
| } | ||
|
|
||
| /** Returns the auto-increment ranges when present. */ | ||
| @Nullable | ||
| public List<AutoIncrementRange> getAutoIncrementRanges() { | ||
| return autoIncrementRanges; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| KvSnapshotFileMetadata that = (KvSnapshotFileMetadata) o; | ||
| return snapshotId == that.snapshotId | ||
| && incrementalSize == that.incrementalSize | ||
| && logOffset == that.logOffset | ||
| && Objects.equals(tableBucket, that.tableBucket) | ||
| && Objects.equals(snapshotLocation, that.snapshotLocation) | ||
| && Objects.equals(sharedFiles, that.sharedFiles) | ||
| && Objects.equals(privateFiles, that.privateFiles) | ||
| && Objects.equals(rowCount, that.rowCount) | ||
| && Objects.equals(autoIncrementRanges, that.autoIncrementRanges); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash( | ||
| tableBucket, | ||
| snapshotId, | ||
| snapshotLocation, | ||
| sharedFiles, | ||
| privateFiles, | ||
| incrementalSize, | ||
| logOffset, | ||
| rowCount, | ||
| autoIncrementRanges); | ||
| } | ||
|
|
||
| private static <T> List<T> immutableCopy(List<T> values, String message) { | ||
| checkNotNull(values, message); | ||
| ArrayList<T> copy = new ArrayList<>(values.size()); | ||
| for (T value : values) { | ||
| copy.add(checkNotNull(value, message)); | ||
| } | ||
| return Collections.unmodifiableList(copy); | ||
| } | ||
|
|
||
| /** Immutable file reference stored in standard KV snapshot metadata. */ | ||
| @Internal | ||
| public static final class FileHandle { | ||
|
|
||
| private final String path; | ||
| private final long size; | ||
| private final String localPath; | ||
| private final @Nullable String sha256; | ||
|
|
||
| /** Creates an immutable file reference without a content digest. */ | ||
| public FileHandle(String path, long size, String localPath) { | ||
| this(path, size, localPath, null); | ||
| } | ||
|
|
||
| /** Creates an immutable file reference with an optional SHA-256 content digest. */ | ||
| public FileHandle(String path, long size, String localPath, @Nullable String sha256) { | ||
| this.path = checkNotNull(path, "File path must not be null."); | ||
| checkArgument(size >= 0L, "File size must not be negative."); | ||
| this.size = size; | ||
| this.localPath = checkNotNull(localPath, "File local path must not be null."); | ||
| checkArgument( | ||
| sha256 == null || sha256.matches("[0-9a-f]{64}"), | ||
| "File SHA-256 must contain exactly 64 lowercase hexadecimal characters."); | ||
| this.sha256 = sha256; | ||
| } | ||
|
|
||
| /** Returns the remote file path. */ | ||
| public String getPath() { | ||
| return path; | ||
| } | ||
|
|
||
| /** Returns the file size. */ | ||
| public long getSize() { | ||
| return size; | ||
| } | ||
|
|
||
| /** Returns the local-path identity stored in the metadata. */ | ||
| public String getLocalPath() { | ||
| return localPath; | ||
| } | ||
|
|
||
| /** Returns the lowercase hexadecimal SHA-256 when present. */ | ||
| @Nullable | ||
| public String getSha256() { | ||
| return sha256; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| FileHandle that = (FileHandle) o; | ||
| return size == that.size | ||
| && Objects.equals(path, that.path) | ||
| && Objects.equals(localPath, that.localPath) | ||
| && Objects.equals(sha256, that.sha256); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(path, size, localPath, sha256); | ||
| } | ||
| } | ||
|
|
||
| /** Immutable auto-increment range stored in standard KV snapshot metadata. */ | ||
| @Internal | ||
| public static final class AutoIncrementRange { | ||
|
|
||
| private final int columnId; | ||
| private final long start; | ||
| private final long end; | ||
|
|
||
| /** Creates an immutable auto-increment range. */ | ||
| public AutoIncrementRange(int columnId, long start, long end) { | ||
| this.columnId = columnId; | ||
| this.start = start; | ||
| this.end = end; | ||
| } | ||
|
|
||
| /** Returns the auto-increment column ID. */ | ||
| public int getColumnId() { | ||
| return columnId; | ||
| } | ||
|
|
||
| /** Returns the range start. */ | ||
| public long getStart() { | ||
| return start; | ||
| } | ||
|
|
||
| /** Returns the range end. */ | ||
| public long getEnd() { | ||
| return end; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| AutoIncrementRange that = (AutoIncrementRange) o; | ||
| return columnId == that.columnId && start == that.start && end == that.end; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(columnId, start, end); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.