HIVE-29694#6577
Conversation
| ACID_TXN_CLEANER_INTERVAL("metastore.acid.txn.cleaner.interval", | ||
| "hive.metastore.acid.txn.cleaner.interval", 10, TimeUnit.SECONDS, | ||
| "Time interval describing how often aborted and committed txns are cleaned."), | ||
| ADD_PARTITION_REUSE_EXISTING_COLUMN_DESCRIPTORS("metastore.add.partition.reuse.existing.column.descriptors", |
There was a problem hiding this comment.
since this is an optimization for large tables, for small tables, it shouldn't introduce too much overhead, should we set the default to true?
There was a problem hiding this comment.
This code got a lot of modifications since your last review. What do you think, should we still turn it true, by default?
There was a problem hiding this comment.
I think we should still make it true by default
There was a problem hiding this comment.
A few thoughts on simplifying the config name (it seems verbose) and the functionality, default value:
- the word 'existing' is already implied by 'reuse' since one can only reuse existing descriptors, so you could remove 'existing'.
- would this functionality only apply for explicit ADD PARTITION commands or would it also apply for MSCK REPAIR which also adds partitions under the covers that are missing in the metastore but are present in the filesystem. Once MSCK REPAIR adds partitions, then subsequent schema evolutions of those partitions should ideally also be reusing the column descriptors. But if this is not currently supported or tested, it is ok to file a separate ticket for followup.
- from an end user perspective, ADD COLUMNS and CHANGE COLUMN are also actions that trigger this duplicate column descriptors, so it may not be accurate to say 'add.partition' . Considering all these, we could simplify it to
metastore.partition.reuse.column.descriptors. WDYT ? - About the default value, since this is changing the generated SQL, it would be better to give it some more time for testing - not just functionally but also performance with order of 100K partitions. Metastore sql changes have significant impact and is in some sense a behavior change, so would be good to wait for further testing.
There was a problem hiding this comment.
metastore.partition.reuse.column.descriptors sounds fine. Thank you.
|
I have moved |
c307567 to
a7bb0eb
Compare
a7bb0eb to
ce44ed1
Compare
ce44ed1 to
988483d
Compare
|
|
||
| List<Long> latestColumnDescriptorIds = new ArrayList<>(); | ||
| for (Object cdId : sqlResult) { | ||
| latestColumnDescriptorIds.add(extractLongValue(cdId)); |
There was a problem hiding this comment.
@dengzhhu653 : I introduced `extractLongValue as the type of CD_ID is number in Oracle and executeWithArray returns with BigDecimal instead of Long.
Do we have an existing reusable code to handle the situation?
There was a problem hiding this comment.
you can use MetastoreDirectSqlUtils.extractSqlLong instead
There was a problem hiding this comment.
Thank you.
|
|
||
| @Override | ||
| protected MColumnDescriptor getSqlResult() throws MetaException { | ||
| if (MetastoreConf.getBoolVar(conf, ADD_PARTITION_REUSE_EXISTING_COLUMN_DESCRIPTORS)) { |
There was a problem hiding this comment.
can you check this boolean property in the else if branch with if (cols.equals(tableSchema)) {?
else if (MetastoreConf.getBoolVar(conf, ADD_PARTITION_REUSE_EXISTING_COLUMN_DESCRIPTORS) {
| // that's the easy and relatively fast-check since does not require | ||
| // round-tripe to the database | ||
| List<FieldSchema> tableSchema = mt.getSd() != null && mt.getSd().getCD() != null && mt.getSd() | ||
| .getCD() |
There was a problem hiding this comment.
nit: code format, change the mt.getSd() .getCD() .getCols() != null to one line
There was a problem hiding this comment.
It would violate the 120 long rule of the static code analyzer. However, I reformatted that statement to get better readability.
|
|
||
| } finally { | ||
| if (query != null) { | ||
| query.closeAll(); |
There was a problem hiding this comment.
nit: don't need to close the query any more, the TransactionHandler could take care of resource releasing
| break; | ||
| } | ||
| } | ||
| return candidate; |
There was a problem hiding this comment.
this could return a mistaken candidate even if !cols.get(i).equals(col)
| // Please note! In case you modify any of those methods, run TestHMSColumnDescriptorReuse with all the supported databases | ||
| List<Long> cdCandidates = findTheLatestColumnDescriptors(tblId); | ||
|
|
||
| cdCandidates = filterCandidatesByColumnCount(cdCandidates, cols.size()); |
There was a problem hiding this comment.
return early if cdCandidates is null
| } | ||
|
|
||
| private List<Long> filterCandidatesByColumnCount(List<Long> cdCandidates, int size) throws MetaException { | ||
| Query<?> query = null; |
There was a problem hiding this comment.
the cdCandidates might be null
| import org.apache.hadoop.hive.metastore.utils.MetaStoreUtils; | ||
| import org.apache.hive.common.util.BloomFilter; | ||
| import org.datanucleus.store.rdbms.query.ForwardQueryResult; | ||
| import org.jetbrains.annotations.Nullable; |
| JOIN "SDS" s ON s."SD_ID" = p."SD_ID" | ||
| WHERE p."TBL_ID" = ? | ||
| GROUP BY s."CD_ID" | ||
| ORDER BY MAX(p."PART_ID") DESC |
There was a problem hiding this comment.
may be we can add a limit here for saving time, e.g, limit 20, most recent schemas have the biggest match usually
There was a problem hiding this comment.
I cannot add a limit clause that is compatible with all the supported databases. However, I can add a limit to the executeWithArray call so that it will only read the first 20 records.
| return convertToMStorageDescriptor(sd, mcd); | ||
| } | ||
|
|
||
| @VisibleForTesting |
There was a problem hiding this comment.
nit: since we don't use this annotation, can we remove it?
amansinha100
left a comment
There was a problem hiding this comment.
Thanks for working in this PR. I mainly had comments about the config and related functionality.
| ACID_TXN_CLEANER_INTERVAL("metastore.acid.txn.cleaner.interval", | ||
| "hive.metastore.acid.txn.cleaner.interval", 10, TimeUnit.SECONDS, | ||
| "Time interval describing how often aborted and committed txns are cleaned."), | ||
| ADD_PARTITION_REUSE_EXISTING_COLUMN_DESCRIPTORS("metastore.add.partition.reuse.existing.column.descriptors", |
There was a problem hiding this comment.
A few thoughts on simplifying the config name (it seems verbose) and the functionality, default value:
- the word 'existing' is already implied by 'reuse' since one can only reuse existing descriptors, so you could remove 'existing'.
- would this functionality only apply for explicit ADD PARTITION commands or would it also apply for MSCK REPAIR which also adds partitions under the covers that are missing in the metastore but are present in the filesystem. Once MSCK REPAIR adds partitions, then subsequent schema evolutions of those partitions should ideally also be reusing the column descriptors. But if this is not currently supported or tested, it is ok to file a separate ticket for followup.
- from an end user perspective, ADD COLUMNS and CHANGE COLUMN are also actions that trigger this duplicate column descriptors, so it may not be accurate to say 'add.partition' . Considering all these, we could simplify it to
metastore.partition.reuse.column.descriptors. WDYT ? - About the default value, since this is changing the generated SQL, it would be better to give it some more time for testing - not just functionally but also performance with order of 100K partitions. Metastore sql changes have significant impact and is in some sense a behavior change, so would be good to wait for further testing.
2d9d504 to
3df1ec4
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in mechanism to reuse existing HMS column descriptors when adding partitions (to reduce metadata bloat during partition-level schema evolution, especially in replication scenarios), plus targeted direct-SQL support and a new unit test to validate the behavior.
Changes:
- Add a new metastore config (
metastore.partition.reuse.column.descriptors) and wire partition conversion to reuse matching column descriptors when enabled. - Implement DirectSQL lookup logic to find and reuse a suitable existing
MColumnDescriptorbased on recent partitions. - Add
TestHMSColumnDescriptorReuseto exercise reuse behavior across schema evolution cases.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestHMSColumnDescriptorReuse.java | Adds unit tests validating descriptor reuse across schema evolution and replication-like partition adds. |
| standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/metastore/impl/TableStoreImpl.java | Routes partition SD conversion through a reusable column-descriptor lookup (when enabled). |
| standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/directsql/MetastoreDirectSqlUtils.java | Adjusts SQL string extraction to handle Oracle/DataNucleus empty-string workaround values. |
| standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/directsql/MetaStoreDirectSql.java | Adds DirectSQL implementation for finding a reusable column descriptor from recent partitions. |
| standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/conf/MetastoreConf.java | Introduces the new configuration flag for enabling descriptor reuse (and reorders one existing conf var). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| private List<Long> findTheLatestColumnDescriptors(long tblId) throws MetaException { | ||
| final int limit = 20; |
| Object[] row = rows.get(i); | ||
| FieldSchema col = new FieldSchema( | ||
| extractSqlString(row[0]), | ||
| extractSqlClob(row[1]), | ||
| extractSqlString(row[2])); | ||
| if (!cols.get(i).equals(col)) { | ||
| break; | ||
| } |
|
Pushed a minor refactor to address the code-format from Copilot and Sonar and restart the CI test. |
|
|
I’m generally supportive of this PR. A couple of things I think we might need to pay attention later: Overall, I think this is a good and working fix. |



Details in the corresponding Jira: https://issues.apache.org/jira/browse/HIVE-29694
Notes about the implementation:
MetaStoreDirectSql.getColumnDescriptor runs multiple queries to get a reusable column descriptor.
I leave the possibility to reduce the amount of queries run as a future improvement as theoretically it would be possible to find a single query that covers all the candidates but practically Hive support so many HMS databases and their feature set is significant.
What changes were proposed in this pull request?
The problem came from replication when addPartition added a lot of extra records in case the table had schema evolution.
In this change, we try to re-use existing schema info based on the following algorithm:
Why are the changes needed?
We got a customer with huge tables (with hundreds of thousands of partitions) with schema evolution between partitions. Their HMS became bloated.
Does this PR introduce any user-facing change?
No
How was this patch tested?
Added new test class, TestHMSColumnDescriptorReuse.
Ran it with all the supported databases.