Zhenqi Li created HIVE-29843:
--------------------------------

             Summary: Concurrent create_database requests may recursively 
delete a shared database directory
                 Key: HIVE-29843
                 URL: https://issues.apache.org/jira/browse/HIVE-29843
             Project: Hive
          Issue Type: Bug
          Components: Standalone Metastore
    Affects Versions: 4.0.0-beta-1
         Environment: Hive Metastore: 4.0.0-beta-1
MySQL: 8.0.30
Hadoop: 3.3.1
FileSystem: S3A-compatible object storage
Standalone reproduction object storage: MinIO
Iceberg: 1.6.1
Concurrency: 16 independent Hive Metastore clients
            Reporter: Zhenqi Li
            Assignee: Zhenqi Li


When multiple clients concurrently initialize the same Iceberg namespace, Hive 
Metastore may process identical {{create_database}} requests and create the 
same database directory. Only one request can successfully insert the database 
metadata; the other requests fail because of the database unique constraint.

The failed requests then use a local {{madeDir}} or {{madeExternalDir}} flag to 
decide whether to recursively clean up the entire database directory. However, 
this flag only records that the request created or observed a usable directory; 
it does not prove that the request still has exclusive ownership of that 
directory. Another request may already have committed metadata for the same 
path or written data below it.

As a result, a failed {{create_database}} request may move or delete a shared 
database directory, leaving valid Metastore metadata pointing to missing 
object-storage files.
h2. Affected Versions

The issue was reproduced against Hive Metastore {{{}4.0.0-beta-1{}}}.

Source inspection found the same directory-ownership and rollback-cleanup 
pattern in Hive branches from {{1.0}} through {{{}4.2.1{}}}. I did not find an 
upstream fix specifically addressing the {{create_database}} directory 
ownership race.
h2. Description

Multiple clients initializing the same Iceberg namespace can concurrently call 
Hive Metastore with identical {{create_database}} requests.

A typical target path is:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamls3a://warehouse/iceberg/test1.db
{code}
The following sequence can occur:
 # Multiple requests check that the database does not exist.
 # Multiple requests check that the target directory does not exist.
 # Multiple requests call {{mkdirs}} for the same path.
 # Multiple requests set a local {{madeDir}} or {{madeExternalDir}} flag to 
{{{}true{}}}.
 # One request successfully inserts the database into the Metastore {{DBS}} 
table.
 # The other requests fail because of the {{DBS.UNIQUE_DATABASE}} unique 
constraint.
 # The failed requests enter the rollback cleanup path.
 # Based on their local flag, they assume that they own the database directory.
 # A failed request recursively moves or deletes the entire database directory.

There is also a more general interleaving, similar to the one documented in 
HIVE-23437:
 # Request R1 creates the directory and is paused before committing metadata.
 # Request R2 observes that the directory already exists, so R2 does not create 
it.
 # R2 successfully commits database metadata referencing that directory.
 # R1 resumes, loses the metadata race, and rolls back.
 # R1 still has {{madeDir=true}} and recursively deletes the directory already 
adopted by R2.

This second interleaving shows that even a perfectly atomic {{mkdir}} 
implementation would not be sufficient. Directory creation provenance is not 
equivalent to current deletion ownership.
h2. Steps to Reproduce
h3. 1. Prepare Hive Metastore

Start Hive Metastore with a MySQL metadata database and S3A-compatible object 
storage. Confirm that the target database path does not exist.

For example:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamls3a://warehouse/hms_direct_race.db
{code}
h3. 2. Concurrently invoke create_database

Use multiple independent Hive Metastore clients and concurrently call:
{code:java}
client.createDatabase(database);
{code}
All requests must use the same catalog, database name, and location. The 
standalone reproduction used:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlcatalog: hive
database: hms_direct_race
concurrency: 16
{code}
h3. 3. Observe the Metastore result

The reproduced result was:
 * 1 request succeeded.
 * 15 requests failed.
 * The failed requests reported a MySQL unique-key conflict:

{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlDuplicate entry 'hms_direct_race-hive'
for key 'DBS.UNIQUE_DATABASE'
{code}
 * Multiple handlers logged that the same database path was created.
 * Failed requests entered the database-directory cleanup path.

h3. 4. Observe directory cleanup

If another request writes content below the database directory before rollback 
cleanup runs, the failed request may move or delete that content together with 
the entire database directory.

For example, affected Iceberg content may include:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamliceberg/test1.db/test3/metadata/00000-....metadata.json
{code}
When Hadoop Trash is enabled, the path may be moved under a location similar to:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamls3a://warehouse/user/root/.Trash/Current/iceberg/test1.db
{code}
h2. Reproduction Log Excerpts

The following logs are from a standalone reproduction that bypassed all 
higher-level application code. It used 16 independent Hive Metastore clients. 
The run started at {{2026-08-27 02:23:40 UTC}} and targeted database 
{{{}hms_direct_race_20260827T022340Z{}}}.
h3. Client result
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlHMS_CREATE_DATABASE_RACE host=hive-metastore port=9083 
database=hms_direct_race_20260827T022340Z workers=16 
location=s3a://warehouse/hms_direct_race_20260827T022340Z.db
worker=00 result=FAILURE error=MetaException: JDODataStoreException: Exception 
thrown flushing changes to datastore Root cause: 
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 
'hms_direct_race_20260827t022340z-hive' for key 'DBS.UNIQUE_DATABASE'
worker=01 result=FAILURE error=MetaException: JDODataStoreException: Exception 
thrown flushing changes to datastore Root cause: 
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 
'hms_direct_race_20260827t022340z-hive' for key 'DBS.UNIQUE_DATABASE'
...
worker=11 result=SUCCESS
...
worker=15 result=FAILURE error=MetaException: JDODataStoreException: Exception 
thrown flushing changes to datastore Root cause: 
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 
'hms_direct_race_20260827t022340z-hive' for key 'DBS.UNIQUE_DATABASE'
HMS_CREATE_DATABASE_RACE_COMPLETE elapsed_ms=10380
{code}
The final result was one successful request and 15 failures caused by 
{{{}DBS.UNIQUE_DATABASE{}}}.
h3. HMS audit and directory creation

Hive Metastore audit logs recorded multiple concurrent {{create_database}} 
requests for the same database. One example is:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yaml2026-08-27T02:23:48,142 INFO [Metastore-Handler-Pool: Thread-1341] 
HiveMetaStore.audit: ugi=root ip=172.19.0.6 cmd=source:172.19.0.6 
create_database: Database(name:hms_direct_race_20260827T022340Z, 
description:direct concurrent create_database test, 
locationUri:s3a://warehouse/hms_direct_race_20260827T022340Z.db, parameters:{}, 
catalogName:hive)
{code}
Multiple handlers then processed the same database path:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yaml2026-08-27T02:23:48,401 INFO [Metastore-Handler-Pool: Thread-1340] 
HMSHandler: Creating database path in external directory 
s3a://warehouse/hms_direct_race_20260827T022340Z.db
2026-08-27T02:23:48,403 INFO [Metastore-Handler-Pool: Thread-1334] HMSHandler: 
Creating database path in external directory 
s3a://warehouse/hms_direct_race_20260827T022340Z.db
2026-08-27T02:23:48,434 INFO [Metastore-Handler-Pool: Thread-1338] HMSHandler: 
Created database path in external directory 
s3a://warehouse/hms_direct_race_20260827T022340Z.db
2026-08-27T02:23:48,438 INFO [Metastore-Handler-Pool: Thread-1334] HMSHandler: 
Created database path in external directory 
s3a://warehouse/hms_direct_race_20260827T022340Z.db
{code}
h3. Sentinel and cleanup

A watcher observed the database directory during the race window and uploaded a 
sentinel object below it:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlWATCHER_START 2026-08-27T02:23:41Z
DIRECTORY_VISIBLE 2026-08-27T02:23:48Z 
s3://warehouse/hms_direct_race_20260827T022340Z.db
46 bytes -> 
`minio/warehouse/hms_direct_race_20260827T022340Z.db/sentinel/metadata/race-sentinel.txt`
SENTINEL_UPLOAD_DONE 2026-08-27T02:23:48Z
WATCHER_END 2026-08-27T02:23:48Z
{code}
The unique-key conflict and subsequent cleanup appeared together in the Hive 
Metastore logs:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlCaused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate 
entry 'hms_direct_race_20260827t022340z-hive' for key 'DBS.UNIQUE_DATABASE'
at 
org.apache.hadoop.hive.metastore.HMSHandler.create_database_core(HMSHandler.java:1274)
at 
org.apache.hadoop.hive.metastore.HMSHandler.create_database(HMSHandler.java:1343)
2026-08-27T02:23:58,482 WARN [Metastore-Handler-Pool: Thread-1342] FileUtils: 
No such file or directory: s3a://warehouse/hms_direct_race_20260827T022340Z.db; 
Force to delete it.
2026-08-27T02:23:58,487 ERROR [Metastore-Handler-Pool: Thread-1341] FileUtils: 
Failed to delete s3a://warehouse/hms_direct_race_20260827T022340Z.db
{code}
After the test, no objects remained under the original path or the 
corresponding Trash path:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml=== 
OBJECTS UNDER DIRECT PATH ===
=== OBJECTS UNDER TRASH PATH ===
=== DIRECT PATH STAT ===
mc: <ERROR> Unable to stat 
`minio/warehouse/hms_direct_race_20260827T022340Z.db/`. Object does not exist.
{code}
At the same time, the database row still existed in the MySQL {{DBS}} table:
{code}
Unable to find source-code formatter for language: text. Available languages 
are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, 
groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, 
php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, 
yamlDB_ID  NAME                                  CTLG_NAME  DB_LOCATION_URI
104    hms_direct_race_20260827t022340z      hive       
s3a://warehouse/hms_direct_race_20260827T022340Z.db
{code}
Trash was disabled in this standalone reproduction, so cleanup directly deleted 
the directory instead of moving it to {{{}.Trash/Current{}}}.
h2. Actual Result

A failed {{create_database}} request may recursively clean up the entire final 
database directory rather than cleaning up only request-private resources.

This can leave the system in the following state:
 * Database metadata still exists in Hive Metastore.
 * Table metadata may still exist in Hive Metastore.
 * Iceberg {{metadata_location}} values still reference the original 
object-storage paths.
 * The corresponding Iceberg metadata files have been moved to Trash or 
recursively deleted.
 * Subsequent Iceberg queries, startup recovery, or metadata commits receive 
404/not-found errors.
 * Iceberg tables become unreadable or unwritable.

h2. Expected Result

When multiple requests concurrently create the same database:
 * One request should successfully create the database.
 * Other requests should receive {{AlreadyExistsException}} or an equivalent 
database-already-exists error.
 * Failed requests must not delete, move, or otherwise modify the shared final 
database directory.
 * Failed requests may only recursively clean up resources that are provably 
private to that request, such as a request-scoped staging path.

h2. Root Cause

{{create_database}} uses a non-atomic check-then-create sequence:
{code:java}
get_database_core(...);

if (database does not exist) {
    create_database_core(...);
}
{code}
Inside {{{}create_database_core{}}}, filesystem ownership is inferred from 
logic similar to:
{code:java}
if (!wh.isDir(dbPath)) {
    if (!wh.mkdirs(dbPath)) {
        throw new MetaException(...);
    }
    madeDir = true;
}
{code}
A successful {{mkdirs}} call only shows that the directory became available 
through this request. It does not prove continuing exclusive ownership. 
Multiple requests may set {{madeDir}} to {{{}true{}}}, and even a directory 
created exclusively by one request may subsequently be adopted by a different 
request that wins the metadata transaction.

When one request commits the Metastore metadata and other requests fail because 
of {{{}DBS.UNIQUE_DATABASE{}}}, the failed requests execute cleanup similar to:
{code:java}
if (!success) {
    ms.rollbackTransaction();

    if (madeDir) {
        wh.deleteDir(dbPath, true);
    }
}
{code}
The corresponding external database path cleanup in Hive {{4.0.0-beta-1}} is 
equivalent to:
{code:java}
if (madeExternalDir && dbExtPath != null) {
    wh.deleteDir(dbExtPath, true, db);
}
{code}
The cleanup scope is the entire final database directory, not an empty 
request-private directory or an object written only by the failed request.

The unique-key conflict itself is an expected concurrency outcome. The 
data-loss risk comes from treating local directory-creation provenance as 
authorization to recursively delete a shared final location after metadata 
rollback.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to