Skip to content

HIVE-30229: Guard the unlocked Iceberg alter_table with a metadata_location CAS - #6930

Draft
deniskuzZ wants to merge 8 commits into
apache:masterfrom
deniskuzZ:HIVE-30229
Draft

deniskuzZ wants to merge 8 commits into
apache:masterfrom
deniskuzZ:HIVE-30229

Conversation

@deniskuzZ

@deniskuzZ deniskuzZ commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

HiveIcebergMetaHook protects the HMS alter_table it wraps the way HiveTableOperations.doCommit protects an Iceberg commit: either the HMS lock or a compare-and-swap (CAS) on metadata_location (expected_parameter_key/expected_parameter_value).

  • Alters without an operation type (stats updates from INSERT/UPDATE/DELETE/MERGE autogather and ANALYZE, TOUCH) commit nothing to Iceberg. They take no lock and don't load the Iceberg table. The hook reads the current metadata_location from HMS, as doCommit does, puts it on the outgoing table (the HIVE-26576 fixup) and adds the CAS. HMS then rejects the alter if a commit moved the pointer in the meantime.
  • Alters with an operation type (DDL) that write metadata_location to HMS take the lock Hive's Iceberg commits take, named after the table's Iceberg catalog (the hook passed the table identifier before). An alter that makes no HMS write (SKIP_METASTORE_ALTER), or a table without metadata_location, gets no lock.
  • The cross-table UUID check reads the caller's metadata.json only for a manual SET TBLPROPERTIES('metadata_location'=...). Every other alter takes the current location, so a stats alter no longer reads metadata.json and no longer fails when the caller's metadata file has expired.
  • ColStatsProcessor: if HMS rejects the COLUMN_STATS_ACCURATE alter, it logs a warning instead of failing a statement whose data and statistics are already committed. With hive.stats.reliable it still fails, as BasicStatsTask does.
  • Catalogs.hiveCatalog also recognizes a HiveCatalog configured by catalog-impl.

Why are the changes needed?

HIVE-28851 made the hook take no lock when engine.hive.lock-enabled=false and for every HiveOperation.QUERY, without adding a CAS. The hook refreshes metadata_location into the outgoing table, but alter_table replaces all table parameters. So a commit by another writer (Impala, Spark, another Hive session) that lands between the refresh and the HMS write gets reverted: metadata_location goes back to the old file and the new snapshot is lost.

Reproduced with Impala: the INSERT's stats alter paused after the hook, Impala committed 00003 -> 00004, and the alter then wrote 00003 back. Also reproduced with a second Hive session.

Does this PR introduce any user-facing change?

Yes, when an alter races with a concurrent commit:

  • A stats update that loses the race is skipped with a warning, or fails the statement with hive.stats.reliable=true. Before, it reverted the other commit.
  • Other alters without an operation type that lose the race (e.g. TOUCH) fail instead of reverting the other commit.
  • The CAS relies on HMS direct SQL (metastore.try.direct.sql=true, the default), the same requirement as Iceberg commits with engine.hive.lock-enabled=false.
  • On MySQL/MariaDB with useAffectedRows=true, an UPDATE that rewrites the same value reports 0 rows, so HMS rejects a stats alter even when no commit raced with it.

How was this patch tested?

  • TestHiveIcebergStatistics#testStatsUpdateDoesNotRevertConcurrentInsert and #testColumnStatsUpdateDoesNotFailOnConcurrentInsert run a second Hive session's INSERT between the hook and the HMS call. Both inserts survive and the statement succeeds.
  • TestHiveIcebergStorageHandlerNoScan covers:
    • an alter without an operation type: no lock, no metadata.json read, the current pointer plus the CAS;
    • an expired caller metadata file;
    • a HiveCatalog configured by catalog-impl;
    • a table without metadata_location;
    • which alters lock (another writer's lock in the table's catalog).
  • Each new test failed before its fix. Tests written after their fix were checked by reverting the fix.
  • The iceberg-catalog suites (TestHiveCatalog, TestHiveCommitLocks, TestHiveCommits, TestHiveTableConcurrency, TestHiveViewCatalog, TestHiveViewCommits) and the iceberg-handler suites (TestHiveIcebergStatistics, TestHiveIcebergStorageHandlerNoScan, TestHiveIcebergMigration, TestHiveIcebergSetCurrentSnapshot, TestConflictingDataFiles, TestOptimisticRetry, TestHiveIcebergWriteMetadataCleanup, TestConcurrentMultiTableTransactions) pass: 1355 tests.

…cation CAS

- Without a lock (HIVE-28851), HiveIcebergMetaHook refreshed metadata_location before alter_table, but HMS wrote it
    unconditionally, so an Iceberg commit landing in between was reverted (e.g. INSERT's stats update).
  - Send the refreshed location as the expected value (HiveOperationsBase.hmsEnvContext); HMS rejects the alter instead.
…UID only on a manual change

- ColStatsProcessor: a rejected COLUMN_STATS_ACCURATE update logs a warning instead of failing the committed write, unless hive.stats.reliable
- HiveIcebergMetaHook: read the caller's metadata.json for the UUID check only on a manual metadata_location change
- Tests: concurrent insert during the column stats update; alter with an expired caller metadata_location
…d the HMS lock

- HiveIcebergMetaHook: an alter without an operation type (statistics updates, incl. ANALYZE) takes NoLock and the metadata_location CAS, replacing the HiveOperation.QUERY check
- It reads the current metadata_location from HMS, as HiveTableOperations does, instead of loading the Iceberg table
- Catalogs.hiveTableOps: HiveCatalog table operations without loading metadata
- Fixup and CAS shared in keepCurrentMetadataLocation, gated on HiveOperationsBase
- Tests: no metadata file read; CAS with Hive locking enabled; expired caller metadata on a property alter
- Catalogs.hiveTableOps: empty unless the table's catalog is a HiveCatalog; an alter without an operation type falls back to the full load otherwise (a catalog-impl HiveCatalog kept the stale metadata_location)
- Tests: alter on a catalog-impl HiveCatalog; race tests pinned to one format, stub cleared after the insert
- ColStatsProcessor: comment also fits ANALYZE FOR COLUMNS
…akes the commits' lock

- MetastoreLock: lock and find the lock in the table's HMS catalog; the Iceberg catalog name only qualifies messages and the JVM lock (since HIVE-29244 locks of different Iceberg catalogs never conflicted)
- HiveTableOperations.metastoreLock: the lock the table's commits take
- HiveIcebergMetaHook: takes that lock instead of one named after the table identifier; NoLock outside a HiveCatalog
- Test: the meta hook's lock excludes a commit through another Iceberg catalog
@deniskuzZ
deniskuzZ marked this pull request as draft October 29, 2026 17:29
- HiveIcebergMetaHook: an alter without an operation type reads metadata_location through the hook's client pool and leaves a table without it alone; only an alter that commits to Iceberg and writes the pointer to HMS takes the lock (none with SKIP_METASTORE_ALTER), named after the Iceberg catalog; CAS keys via hive_metastoreConstants
- MetastoreLock: no catalog on the HMS lock and its lookup, so every engine's lock of the table conflicts; Catalogs and HiveTableOperations back to master
- Tests: another engine's lock per alter kind; a table without metadata_location; the no-read check merged into testMetaHookWithUndefinedAlterOperationType; renames
- MetastoreLock back to master: the HMS lock carries the Iceberg catalog name (HIVE-29244); the hook's lock uses the table's catalog name (Catalogs.catalogName) instead of the table identifier
- Catalogs.hiveCatalog: a HiveCatalog configured by catalog-impl counts
- HiveIcebergMetaHook: CAS instead of the lock under external locking; the manual metadata_location check and the fixup branch once, sharing expectMetadataLocation
- Tests: the alter callback moves into HiveIcebergStorageHandlerStub; lock tests take the table's catalog; external locking and catalog-impl cases
@deniskuzZ deniskuzZ changed the title Hive 30229: Guard the unlocked Iceberg alter_table with a metadata_location CAS HIVE-30229: Guard the unlocked Iceberg alter_table with a metadata_location CAS Oct 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

- External locking requires engine.hive.lock-enabled=false (getLockType rejects writes otherwise), where the hook already takes no lock

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants