Skip to content
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

Hive: Unwrap RuntimeException for Hive TException with rename table #9432

Merged
merged 1 commit into from
Jan 17, 2024

Conversation

nk1506
Copy link
Contributor

@nk1506 nk1506 commented Jan 8, 2024

No description provided.

ALTER_TABLE.invoke(client, databaseName, tblName, table, new EnvironmentContext(env));
try {
ALTER_TABLE.invoke(client, databaseName, tblName, table, new EnvironmentContext(env));
} catch (RuntimeException e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

How widely is alterTable used?
Wouldn't it be better (more general solution) to expose the wrapped MetaException instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's only being used at two places:

  1. To update the table .
  2. To rename the table .

Are you proposing to throw non RTE MetaException at alter table . something like

catch (RuntimeException e) {
      if (e.getCause() instanceof InvalidOperationException
          && e.getCause().getMessage() != null
          && e.getCause()
              .getMessage()
              .contains(
                  String.format(
                      "new table %s.%s already exists", table.getDbName(), table.getTableName()))) {
        throw new MetaException(
            String.format("Table already exists: %s.%s", table.getDbName(), table.getTableName()));
      }
      throw e;
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

I proposing something like this:

catch (RuntimeException e) {
      if (e.getCause() instanceof MetaException) {
        throw (MetaException) e.getCause()
      } else {
        throw e;
      }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any thoughts on the test case testRenameTableDestinationTableAlreadyExists since it expects error message as

.isInstanceOf(AlreadyExistsException.class)
        .hasMessageContaining("Table already exists");

Currently we have override this as

.isInstanceOf(RuntimeException.class)
        .hasMessageContaining("new table newdb.table_renamed already exists");

Copy link
Contributor

Choose a reason for hiding this comment

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

alterTable should return the MetaException and where we call it, we should handle the InvalidOperationException with the message of new table %s.%s already exists and translate it to an AlreadyExistsException.

We should only consider moving the exception if we find that all of the places where we call alterTable we would like to throw the same AlreadyExistsException

Copy link
Contributor Author

Choose a reason for hiding this comment

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

InvalidOperationException still will be wrapped with RuntimeException . So basically we will have to catch RuntimeException and unwrap with AlreadyExistsException .

Or Am I missing something here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why not unwrap RTE with both MetaException as well as InvalidOperationException and translate to org.apache.hadoop.hive.metastore.api.AlreadyExistsException . Since all the callers of alterTable is already handling org.apache.hadoop.hive.metastore.api.AlreadyExistsException.
I am thinking something like this:

catch (RuntimeException e) {
      // MetaException would be wrapped into RuntimeException during reflection
      if (e.getCause() instanceof MetaException) {
        throw (MetaException) e.getCause();
      }
      // InvalidOperationException would be wrapped into RuntimeException during reflection, as
      // java.lang.RuntimeException:InvalidOperationException(message:new table <> already exists)
      if (e.getCause() instanceof InvalidOperationException
              && e.getCause().getMessage() != null
              && e.getCause()
              .getMessage()
              .contains(
                      String.format(
                              "new table %s.%s already exists", table.getDbName(), table.getTableName()))) {
        throw new AlreadyExistsException(
                String.format("Table already exists: %s.%s", table.getDbName(), table.getTableName()));
      }
      throw e;
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

In the HiveTableOperations we are accustomed to handle Hive MetaExceptions, so I think handling InvalidOperationException there would be "natural".

The alterTable wraps the exceptions to a RuntimeException because of the DynMethod calls. I think this is a mistake. We should behave the same way as the metaClients.run(client -> client...) like calls, we should throw the original MetaException - my opinion here could be changes if this becomes a breaking change somewhere, but if it does not break code for someone, then we should change it.

Copy link
Contributor

@pvary pvary Jan 10, 2024

Choose a reason for hiding this comment

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

I made a mistake in the above command, I thought, that the root exception is MetaException, but in reality it is TException.

So fixing this issue, my comment correctly is below:


The alterTable wraps the exceptions to a RuntimeException because of the DynMethod calls. I think this is a mistake. We should behave the same way as the metaClients.run(client -> client...) like calls, we should throw the original TException - my opinion here could be changes if this becomes a breaking change somewhere, but if it does not break code for someone, then we should change it.

} catch (AlreadyExistsException e) {
throw new org.apache.iceberg.exceptions.AlreadyExistsException(
"Table already exists: %s", to);
} catch (RuntimeException e) {
Copy link
Member

Choose a reason for hiding this comment

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

You should catch here MetaException instead of RuntimeException from the altertable and throw AlreadyExistsException ?

Copy link
Member

Choose a reason for hiding this comment

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

or if it is not coming as MetaException, if it is coming as InvalidOperationException cause. We should unwrap this at alterTable and throw only the cause.

Copy link
Member

Choose a reason for hiding this comment

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

Unwrapping only MetaException looks odd.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure why we should explicitly catch MetaException here. Since it's already being handled with

catch (TException e) {
      throw new RuntimeException("Failed to rename " + from + " to " + to, e);

    }

InvalidOperationException will always be wrapped with RuntimeException in case of alterTable. Either we should unwrap it MetastoreUtil or at every caller of it.

Copy link
Member

Choose a reason for hiding this comment

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

Originally we were unwrapping to MetaException but here were still catching RuntimeException. Hence I gave that comment.

Now we are unwrapping to TException and catching the TException. So, now LGTM.

@pvary
Copy link
Contributor

pvary commented Jan 11, 2024

@ajantha-bhat: I think this looks good. Any more comments on your side?

@ajantha-bhat
Copy link
Member

LGTM aswell.

@ajantha-bhat
Copy link
Member

PR title may need update as it is TException now.

@pvary pvary changed the title Hive: Unwrap RuntimeException for Hive InvalidOperationException with rename table Hive: Unwrap RuntimeException for Hive TException with rename table Jan 11, 2024
@nk1506
Copy link
Contributor Author

nk1506 commented Jan 17, 2024

@pvary , Should we merge this if there are no more comments ?

Comment on lines +255 to +261
if (e.getMessage() != null
&& e.getMessage().contains(String.format("new table %s already exists", to))) {
throw new org.apache.iceberg.exceptions.AlreadyExistsException(
"Table already exists: %s", to);
} else {
throw new RuntimeException("Failed to rename " + from + " to " + to, e);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

could we please move this under a catch (InvalidOperationException)?
But keep the old TException handler part

@pvary pvary merged commit 1da8055 into apache:main Jan 17, 2024
41 checks passed
@pvary
Copy link
Contributor

pvary commented Jan 17, 2024

Thanks @nk1506 for the PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants