-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Inherit snapshot ids for manifest entries #675
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 all commits
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 |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ | |
|
|
||
| package org.apache.iceberg; | ||
|
|
||
| import com.google.common.base.Preconditions; | ||
| import com.google.common.collect.Iterables; | ||
| import com.google.common.collect.Lists; | ||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
@@ -29,6 +31,9 @@ | |
| import org.apache.iceberg.exceptions.RuntimeIOException; | ||
| import org.apache.iceberg.io.OutputFile; | ||
|
|
||
| import static org.apache.iceberg.TableProperties.SNAPSHOT_ID_INHERITANCE_ENABLED; | ||
| import static org.apache.iceberg.TableProperties.SNAPSHOT_ID_INHERITANCE_ENABLED_DEFAULT; | ||
|
|
||
| /** | ||
| * {@link AppendFiles Append} implementation that adds a new manifest file for the write. | ||
| * <p> | ||
|
|
@@ -37,9 +42,11 @@ | |
| class FastAppend extends SnapshotProducer<AppendFiles> implements AppendFiles { | ||
| private final TableOperations ops; | ||
| private final PartitionSpec spec; | ||
| private final boolean snapshotIdInheritanceEnabled; | ||
| private final SnapshotSummary.Builder summaryBuilder = SnapshotSummary.builder(); | ||
| private final List<DataFile> newFiles = Lists.newArrayList(); | ||
| private final List<ManifestFile> appendManifests = Lists.newArrayList(); | ||
| private final List<ManifestFile> rewrittenAppendManifests = Lists.newArrayList(); | ||
| private ManifestFile newManifest = null; | ||
| private final AtomicInteger manifestCount = new AtomicInteger(0); | ||
| private boolean hasNewFiles = false; | ||
|
|
@@ -48,6 +55,8 @@ class FastAppend extends SnapshotProducer<AppendFiles> implements AppendFiles { | |
| super(ops); | ||
| this.ops = ops; | ||
| this.spec = ops.current().spec(); | ||
| this.snapshotIdInheritanceEnabled = ops.current() | ||
| .propertyAsBoolean(SNAPSHOT_ID_INHERITANCE_ENABLED, SNAPSHOT_ID_INHERITANCE_ENABLED_DEFAULT); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -81,16 +90,31 @@ public FastAppend appendFile(DataFile file) { | |
|
|
||
| @Override | ||
| public FastAppend appendManifest(ManifestFile manifest) { | ||
| // the manifest must be rewritten with this update's snapshot ID | ||
| try (ManifestReader reader = ManifestReader.read( | ||
| ops.io().newInputFile(manifest.path()), ops.current().specsById())) { | ||
| Preconditions.checkArgument(!manifest.hasExistingFiles(), "Cannot append manifest with existing files"); | ||
| Preconditions.checkArgument(!manifest.hasDeletedFiles(), "Cannot append manifest with deleted files"); | ||
| Preconditions.checkArgument( | ||
| manifest.snapshotId() == null || manifest.snapshotId() == -1, | ||
| "Snapshot id must be assigned during commit"); | ||
|
|
||
| if (snapshotIdInheritanceEnabled && manifest.snapshotId() == null) { | ||
| summaryBuilder.addedManifest(manifest); | ||
| appendManifests.add(manifest); | ||
| } else { | ||
| // the manifest must be rewritten with this update's snapshot ID | ||
| ManifestFile copiedManifest = copyManifest(manifest); | ||
| rewrittenAppendManifests.add(copiedManifest); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| private ManifestFile copyManifest(ManifestFile manifest) { | ||
| try (ManifestReader reader = ManifestReader.read(manifest, ops.io(), ops.current().specsById())) { | ||
|
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. I think we need to collect more metadata while writing manifests so that we don't have to read manifests to simply get stats. Clearly, this kills all the benefits of inhering the snapshot id.
Collaborator
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. Do you mean to collect more metadata to build the summary without reading the passing manifest from file system?
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. Yes, we can keep the summary in
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. Actually, we can use the manifest's summary stats for top-level properties. Partition-level properties are optional so we should just not include them.
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. @chenjunjiedada, yes, I would like to avoid reading passed manifests for better performance. @rdblue, could you elaborate on what you mean by top-level and partition-level properties? Do you mean
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. Ideally, we would produce all of the summary stats, but the most important ones are I think my response was confusing because we keep additional summary information about each partition in our version. I can move that upstream if everyone wants it, but it can make the metadata files quite large. Without a use case for doing this upstream, I didn't think it was a good idea to make everyone's metadata significantly larger.
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. Do you have a few examples of what stats you collect per partition? One of our customers was asking about some stats about what partitions were modified, for example. |
||
| OutputFile newManifestPath = manifestPath(manifestCount.getAndIncrement()); | ||
| appendManifests.add(ManifestWriter.copyAppendManifest(reader, newManifestPath, snapshotId(), summaryBuilder)); | ||
| return ManifestWriter.copyAppendManifest(reader, newManifestPath, snapshotId(), summaryBuilder); | ||
| } catch (IOException e) { | ||
| throw new RuntimeIOException(e, "Failed to close manifest: %s", manifest); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -106,7 +130,11 @@ public List<ManifestFile> apply(TableMetadata base) { | |
| throw new RuntimeIOException(e, "Failed to write manifest"); | ||
| } | ||
|
|
||
| newManifests.addAll(appendManifests); | ||
| // TODO: add sequence numbers here | ||
| Iterable<ManifestFile> appendManifestsWithMetadata = Iterables.transform( | ||
| Iterables.concat(appendManifests, rewrittenAppendManifests), | ||
| manifest -> GenericManifestFile.copyOf(manifest).withSnapshotId(snapshotId()).build()); | ||
| Iterables.addAll(newManifests, appendManifestsWithMetadata); | ||
|
|
||
| if (base.currentSnapshot() != null) { | ||
| newManifests.addAll(base.currentSnapshot().manifests()); | ||
|
|
@@ -121,7 +149,9 @@ protected void cleanUncommitted(Set<ManifestFile> committed) { | |
| deleteFile(newManifest.path()); | ||
| } | ||
|
|
||
| for (ManifestFile manifest : appendManifests) { | ||
|
Collaborator
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. Do we need to clean
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. The appended manifests become part of the table, so there is no need to delete them.
Collaborator
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. Do you mean even manifests that are not committed are part of the table? Or all appended manifests must be committed successfully so we don't have to care for this?
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. @chenjunjiedada, yes, all appended manifests must be successfully committed. They are not generated by Iceberg anymore. Instead, they are produced by users.
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. One case we need to consider is when we add a manifest using merge append and it gets combined with other manifests/files in
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. It seems reasonable to clean up the external manifest if the commit is successful but that manifest is not part of the table metadata in
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. Sounds good to me. |
||
| // clean up only rewrittenAppendManifests as they are always owned by the table | ||
| // don't clean up appendManifests as they are added to the manifest list and are not compacted | ||
| for (ManifestFile manifest : rewrittenAppendManifests) { | ||
| if (!committed.contains(manifest)) { | ||
| deleteFile(manifest.path()); | ||
| } | ||
|
|
||
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.
This place requires attention. When we introduce sequence numbers, we will have to iterate through all new manifests. For now, iterating through
newManifestsandrewrittenAddedManifestsis redundant. However, I expect we will add sequence number quickly and we will simply need to change the closure.