Skip to content

Conversation

@jun-he
Copy link
Collaborator

@jun-he jun-he commented Mar 16, 2020

For issue #280.

I took a different approach from the previous WIP PR (#499).
Instead of adding additional states and logics into TableMetadata, I created PartitionSpecUpdate to handle that.
Also, it is unnecessary to keep lastAssignedPartitonFieldId in TableMetadata as it can be lazily derived from all specs (previous specs are still there) stored in TableMetadata.

@jun-he
Copy link
Collaborator Author

jun-he commented Mar 23, 2020

@rdblue can you please take a look? Thanks.

@jun-he
Copy link
Collaborator Author

jun-he commented Mar 29, 2020

Hi @rdblue, can you please take a look? Thanks.

Copy link
Collaborator

@chenjunjiedada chenjunjiedada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jun-he! This addresses my open issue for adding partition evolution.

Jut left some comments for you.

private transient volatile ListMultimap<Integer, PartitionField> fieldsBySourceId = null;
private transient volatile Class<?>[] lazyJavaClasses = null;
private transient volatile List<PartitionField> fieldList = null;
private final int lastAssignedFieldId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use TypeUtil::NextID?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tracking something different. Here, this is the highest ID assigned to any partition, so that the next ID assigned will be unique.

PartitionField(int sourceId, String name, Transform<?, ?> transform) {
PartitionField(int sourceId, int fieldId, String name, Transform<?, ?> transform) {
this.sourceId = sourceId;
this.fieldId = fieldId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check fieldId is larger than 1000?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. That is a convention that we use, but not strictly required by the spec.


builder.add(sourceId, name, transform);
// partition field ids are missing in old PartitionSpec, they always auto-increment from PARTITION_DATA_ID_START
if (!hasFieldId) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the forward compatibility? Is it possible that an old reader reads the new spec? then it still parses the new spec field id start from 1000?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For forward-compatibility, I think that this should detect breaking changes to specs and throw an exception.

If IDs are removed by an older writer, then the IDs will be reassigned. That means that IDs must be assigned starting at 1000 and should have no gaps. If there are IDs, this should validate that assumption by checking that the field actually has the ID that is expected.

We should make a similar change on the write path: for each field, check that it's field ID is what would be assigned if it were removed by an older writer. That will prevent newer writers from creating specs that will break.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making these changes here, I think that this should be verified in TableMetadata. That would accomplish the same thing, but make it easier to check the table version.

if (elements.hasNext() && elements.next().has(FIELD_ID)) {
hasFieldId = true;
}
elements = json.elements();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be easier to follow and would result in a better error message if we put this logic inside the elements loop.

How about adding a counter for field IDs that are present and after the loop throwing an exception if the counter is not equal to the number of fields? Then each field would be handled independently (using has(FIELD_ID)).

I like the idea of a check here that states there were missing field IDs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

@rdblue
Copy link
Contributor

rdblue commented Apr 3, 2020

I'm only about half-way done reviewing this, but I wanted to capture some thoughts about forward-compatibility that was raised by @chenjunjiedada.

If there are already multiple partition specs, then the IDs may be reused and can even conflict. This isn't something we can change because manifest files embed the field IDs in their schemas. That means assignment when there are no IDs must be from 1000 and should be independent across different partition specs.

If an older version writes to the table, then it may remove any assigned partition IDs. That means that for any format v1 table, we must remain compatible with the current assignment strategy. That way, IDs can be removed by an old writer and will be the same when they are reassigned.

This also means that evolution is limited in v1 tables. To ensure that IDs can be reassigned correctly if they are removed, partition fields cannot be dropped or reordered in any way. Otherwise, reassignment would be incorrect. That means no removing partition fields, no reordering partition fields, and no adding partition fields unless they are added at the end of the spec.

We will be able to make more evolution changes when we can guarantee that all partition fields have IDs that won't be removed. We'll make the IDs a requirement in v2 tables.

* When committing, these changes will be applied to the current table metadata. Commit conflicts
* will not be resolved and will result in a {@link CommitFailedException}.
*/
public interface UpdatePartitionSpec extends PendingUpdate<PartitionSpec> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the new interface and methods from this PR?

I don't think this is needed for this PR, and I'd like to minimize the number of changes. In addition, I don't think we want to move to a model where users create a new spec and apply it to a table. I think we instead want to evolve the partition spec of a table. So this API will probably be different when we release that feature.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it is better to move the update to a new PR, which addresses #281.
The main reason I put it here is to have additional unit tests to make sure it works as expected.

Additionally, I think we may support the table partition spec evolution in two ways

table.updatePartitionSpec()
  .update(spec)
  .commit();
table.updatePartitionSpec().newSpec(schema)
  .identity(...)
  .bucket(...)
  ...
  .commit();

The first approach may be used if clients want to define a spec and manage it by their codes, e.g. use defined spec object in multiple places.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For tests, you can use TableMetadata and commit directly:

TableOperations ops = table.operations();
TableMetadata base = ops.current()
TableMetadata updated = base.updatePartitionSpec(newSpec);
ops.commit(base, updated);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

@rdblue
Copy link
Contributor

rdblue commented Apr 3, 2020

Thanks @jun-he, this is great progress on partition field IDs! I think I understand how compatibility will work with field IDs and I tried to add that context to my comments in this PR. If it isn't clear, please let me know what doesn't make sense.

@jun-he jun-he force-pushed the jun/partition-field-id branch from 6d804af to cd75cbd Compare April 10, 2020 07:59
@jun-he
Copy link
Collaborator Author

jun-he commented Apr 10, 2020

I will move UpdatePartitionSpec and all related unit tests to another PR.

@jun-he jun-he requested a review from rdblue April 15, 2020 07:41
@jun-he
Copy link
Collaborator Author

jun-he commented Apr 15, 2020

@chenjunjiedada FYI, I refactored the code and the partition spec evolution changes and related tests are now in #922. Thanks.

.appendManifest(manifestWithDeletedFiles)
.commit());
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: unnecessary newline.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Will remove it in #922.

@rdblue rdblue merged commit b6cdc69 into apache:master Apr 15, 2020
@rdblue
Copy link
Contributor

rdblue commented Apr 15, 2020

Awesome work, @jun-he! I'm merging this.

rdblue added a commit to rdblue/iceberg that referenced this pull request Apr 15, 2020
@jun-he jun-he deleted the jun/partition-field-id branch April 16, 2020 00:25
Fokko pushed a commit to Fokko/iceberg that referenced this pull request Apr 21, 2020
rdblue pushed a commit that referenced this pull request May 18, 2020
sunchao pushed a commit to sunchao/iceberg that referenced this pull request May 9, 2023
…dule (apache#845)

* Internal: Updates AppleAwsSparkConnect util to 1.0.9
* Internal: Allow AppleAwsConnectionFactory to use Proxy Hosts
* Internal: Allows AppleAwsConnectionFactory to use Spark/Hadoop Configuration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants