apache / apache/hudi

[SUPPORT] AWS Glue Sync bug with "delete_partition" operation

Open
#9,805 10 comments 0 reactions 0 assignees View on GitHub
area:aws component:catalog-sync priority:high
Dominant language
Java
Stars
6.2k
Forks
2.5k
Avg merge
2d 8h
Merged PRs (30d)
111

Description

**Describe the problem you faced**

We recently ran "delete_partition" operation to clean up some specific partition's data due to bad data being ingested. We then re-ingested the correct data. After syncing to Glue using ./hudi-sync-tool the partitions are DELETED, when they should still exist.

These partitions should not be deleted from Glue. They were re-created with the good ingestion, and my users use Glue as a metastore.

**When I run a Glue sync manually using ./hudi-sync-tool, those partitions are actually removed. It appears the "delete_partition" replacecommit overrides the later deltacommit that has those partitions re-ingested.**

This appears to be a bug unless I am missing something with how `delete_partition` is expected to behave.

**To Reproduce**

Steps to reproduce the behavior:

1. Generate Hudi table with multiple partitions using bulk_insert e.g. [datasource=1/year=2000/month=1]
2. Run delete_partition operation to delete all partitions with `datasource=1/*`
3. Re-generate new partitions for datasource=1 with correct data.
4. Hive sync fails trying to delete 25+ partitions. (separate issue see #9806)
5. Manual hive sync leaves Glue table with only 1 new partition (datasource=1/year=2023/month=10).

**Expected behavior**

I expect the following:
1 - AWS Glue sync should not fail with 25+ partitions in total request. It should batch properly.
2 - My Glue table should not even be deleting the partitions. The final state **should** have all the partitions for datasource=1 but that is not being respected (it seems delete_partition replacecommit takes precedence)!

**Environment Description**

* Hudi version : 0.13.1-amzn-0

* Spark version : 3.4.0

* Hive version : 3.1.3

* Hadoop version : 3.3.3

* Storage (HDFS/S3/GCS..) : S3

* Running on Docker? (yes/no) : No

**Additional context**

Running on AWS EMR 6.12.

Many of my consumers use Glue as a data catalog. Will missing partitions reduce performance or prevent any new data from being accessed via Glue directly?

**Stacktrace**

```
23/09/28 16:32:47 ERROR Client: Application diagnostics message: User class threw exception: org.apache.hudi.exception.HoodieException: Could not sync using the meta sync class org.apache.hudi.aws.sync.AwsGlueCatalogSyncTool
at org.apache.hudi.sync.common.util.SyncUtilHelpers.runHoodieMetaSync(SyncUtilHelpers.java:61)
at org.apache.hudi.HoodieSparkSqlWriter$.$anonfun$metaSync$2(HoodieSparkSqlWriter.scala:888)
at scala.collection.mutable.HashSet.foreach(HashSet.scala:79)
at org.apache.hudi.HoodieSparkSqlWriter$.metaSync(HoodieSparkSqlWriter.scala:886)
at org.apache.hudi.HoodieSparkSqlWriter$.commitAndPerformPostOperations(HoodieSparkSqlWriter.scala:984)
at org.apache.hudi.HoodieSparkSqlWriter$.write(HoodieSparkSqlWriter.scala:381)
at org.apache.hudi.DefaultSource.createRelation(DefaultSource.scala:150)
at org.apache.spark.sql.execution.datasources.SaveIntoDataSourceCommand.run(SaveIntoDataSourceCommand.scala:47)
at org.apache.spark.sql.execution.command.ExecutedCommandExec.sideEffectResult$lzycompute(commands.scala:75)
at org.apache.spark.sql.execution.command.ExecutedCommandExec.sideEffectResult(commands.scala:73)
at org.apache.spark.sql.execution.command.ExecutedCommandExec.executeCollect(commands.scala:84)
at org.apache.spark.sql.execution.QueryExecution$$anonfun$eagerlyExecuteCommands$1.$anonfun$applyOrElse$1(QueryExecution.scala:104)
at org.apache.spark.sql.catalyst.QueryPlanningTracker$.withTracker(QueryPlanningTracker.scala:107)
at org.apache.spark.sql.execution.SQLExecution$.withTracker(SQLExecution.scala:250)
at org.apache.spark.sql.execution.SQLExecution$.executeQuery$1(SQLExecution.scala:123)
at org.apache.spark.sql.execution.SQLExecution$.$anonfun$withNewExecutionId$9(SQLExecution.scala:160)
at org.apache.spark.sql.catalyst.QueryPlanningTracker$.withTracker(QueryPlanningTracker.scala:107)
at org.apache.spark.sql.execution.SQLExecution$.withTracker(SQLExecution.scala:250)
at org.apache.spark.sql.execution.SQLExecution$.$anonfun$withNewExecutionId$8(SQLExecution.scala:160)
at org.apache.spark.sql.execution.SQLExecution$.withSQLConfPropagated(SQLExecution.scala:271)
at org.apache.spark.sql.execution.SQLExecution$.$anonfun$withNewExecutionId$1(SQLExecution.scala:159)
at org.apache.spark.sql.SparkSession.withActive(SparkSession.scala:827)
at org.apache.spark.sql.execution.SQLExecution$.withNewExecutionId(SQLExecution.scala:69)
at org.apache.spark.sql.execution.QueryExecution$$anonfun$eagerlyExecuteCommands$1.applyOrElse(QueryExecution.scala:101)
at org.apache.spark.sql.execution.QueryExecution$$anonfun$eagerlyExecuteCommands$1.applyOrElse(QueryExecution.scala:97)
at org.apache.spark.sql.catalyst.trees.TreeNode.$anonfun$transformDownWithPruning$1(TreeNode.scala:554)
at org.apache.spark.sql.catalyst.trees.CurrentOrigin$.withOrigin(TreeNode.scala:107)
at org.apache.spark.sql.catalyst.trees.TreeNode.transformDownWithPruning(TreeNode.scala:554)
at org.apache.spark.sql.catalyst.plans.logical.LogicalPlan.org$apache$spark$sql$catalyst$plans$logical$AnalysisHelper$$super$transformDownWithPruning(LogicalPlan.scala:32)
at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper.transformDownWithPruning(AnalysisHelper.scala:267)
at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper.transformDownWithPruning$(AnalysisHelper.scala:263)
at org.apache.spark.sql.catalyst.plans.logical.LogicalPlan.transformDownWithPruning(LogicalPlan.scala:32)
at org.apache.spark.sql.catalyst.plans.logical.LogicalPlan.transformDownWithPruning(LogicalPlan.scala:32)
at org.apache.spark.sql.catalyst.trees.TreeNode.transformDown(TreeNode.scala:530)
at org.apache.spark.sql.execution.QueryExecution.eagerlyExecuteCommands(QueryExecution.scala:97)
at org.apache.spark.sql.execution.QueryExecution.commandExecuted$lzycompute(QueryExecution.scala:84)
at org.apache.spark.sql.execution.QueryExecution.commandExecuted(QueryExecution.scala:82)
at org.apache.spark.sql.execution.QueryExecution.assertCommandExecuted(QueryExecution.scala:142)
at org.apache.spark.sql.DataFrameWriter.runCommand(DataFrameWriter.scala:856)
at org.apache.spark.sql.DataFrameWriter.saveToV1Source(DataFrameWriter.scala:387)
at org.apache.spark.sql.DataFrameWriter.saveInternal(DataFrameWriter.scala:360)
at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:239)
at com.example.spark.datalake.hudi.HudiDatalake.persist(HudiDatalake.java:62)
at com.example.spark.datalake.hudi.HudiDatalake.save(HudiDatalake.java:39)
at com.example.spark.datalake.FilteredDatalake.save(FilteredDatalake.java:24)
at com.example.spark.tier2.datalake.HudiDatalakeUpdater.saveToHudi(HudiDatalakeUpdater.java:86)
at com.example.spark.tier2.datalake.HudiDatalakeUpdater.upsert(HudiDatalakeUpdater.java:61)
at com.example.spark.tier2.extractor.BaseExtractor.extract(BaseExtractor.java:58)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at com.example.spark.tier2.extractor.KViewsExtractor.extract(KViewsExtractor.java:35)
at com.example.spark.tier2.DmsTierTwoExtractorRunner.run(DmsTierTwoExtractorRunner.java:239)
at com.example.spark.tier2.DmsTierTwoExtractorRunner.main(DmsTierTwoExtractorRunner.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.spark.deploy.yarn.ApplicationMaster$$anon$2.run(ApplicationMaster.scala:760)
Caused by: org.apache.hudi.exception.HoodieException: Got runtime exception when hive syncing table_all
at org.apache.hudi.hive.HiveSyncTool.syncHoodieTable(HiveSyncTool.java:165)
at org.apache.hudi.sync.common.util.SyncUtilHelpers.runHoodieMetaSync(SyncUtilHelpers.java:59)
... 56 more
Caused by: org.apache.hudi.hive.HoodieHiveSyncException: Failed to sync partitions for table table_all
at org.apache.hudi.hive.HiveSyncTool.syncPartitions(HiveSyncTool.java:429)
at org.apache.hudi.hive.HiveSyncTool.syncHoodieTable(HiveSyncTool.java:280)
at org.apache.hudi.hive.HiveSyncTool.doSync(HiveSyncTool.java:188)
at org.apache.hudi.hive.HiveSyncTool.syncHoodieTable(HiveSyncTool.java:162)
... 57 more
Caused by: org.apache.hudi.aws.sync.HoodieGlueSyncException: Fail to drop partitions to dms_hudi_db.table_all
at org.apache.hudi.aws.sync.AWSGlueCatalogSyncClient.dropPartitions(AWSGlueCatalogSyncClient.java:222)
at org.apache.hudi.hive.HiveSyncTool.syncPartitions(HiveSyncTool.java:457)
at org.apache.hudi.hive.HiveSyncTool.syncPartitions(HiveSyncTool.java:424)
... 60 more
Caused by: org.apache.hudi.com.amazonaws.services.glue.model.ValidationException: 1 validation error detected: Value '[PartitionValueList(values=[example@partition1.com, 2021, 11]), PartitionValueList(values=[example@partition1.com, 2021, 12]), PartitionValueList(values=[example@partition1.com, 2021, 10]), PartitionValueList(values=[example@partition1.com, 2021, 9]), PartitionValueList(values=[example@partition1.com, 2021, 8]), PartitionValueList(values=[example@partition1.com, 2021, 7]), PartitionValueList(values=[example@partition1.com, 2021, 6]), PartitionValueList(values=[example@partition1.com, 2019, 2]), PartitionValueList(values=[example@partition1.com, 2021, 5]), PartitionValueList(values=[example@partition1.com, 2021, 4]), PartitionValueList(values=[example@partition1.com, 2019, 1]), PartitionValueList(values=[example@partition1.com, 2018, 10]), PartitionValueList(values=[example@partition1.com, 2019, 4]), PartitionValueList(values=[example@partition1.com, 2019, 3]), PartitionValueList(values=[example@partition1.com, 2019, 6]), PartitionValueList(values=[example@partition1.com, 2019, 5]), PartitionValueList(values=[example@partition1.com, 2019, 8]), PartitionValueList(values=[example@partition1.com, 2019, 7]), PartitionValueList(values=[example@partition1.com, 2018, 12]), PartitionValueList(values=[example@partition1.com, 2018, 11]), PartitionValueList(values=[example@partition1.com, 2019, 9]), PartitionValueList(values=[example@partition1.com, 2017, 12]), PartitionValueList(values=[example@partition1.com, 2017, 10]), PartitionValueList(values=[example@partition1.com, 2017, 11]), PartitionValueList(values=[example@partition1.com, 2021, 3]), PartitionValueList(values=[example@partition1.com, 2021, 2]), PartitionValueList(values=[example@partition1.com, 2021, 1]), PartitionValueList(values=[example@partition1.com, 2016, 2]), PartitionValueList(values=[example@partition1.com, 2016, 3]), PartitionValueList(values=[example@partition1.com, 2016, 4]), PartitionValueList(values=[example@partition1.com, 2016, 5]), PartitionValueList(values=[example@partition1.com, 2016, 1]), PartitionValueList(values=[example@partition1.com, 2016, 6]), PartitionValueList(values=[example@partition1.com, 2016, 7]), PartitionValueList(values=[example@partition1.com, 2016, 8]), PartitionValueList(values=[example@partition1.com, 2016, 9]), PartitionValueList(values=[example@partition1.com, 2022, 6]), PartitionValueList(values=[example@partition1.com, 2022, 5]), PartitionValueList(values=[example@partition1.com, 2022, 4]), PartitionValueList(values=[example@partition1.com, 2022, 3]), PartitionValueList(values=[example@partition1.com, 2022, 9]), PartitionValueList(values=[example@partition1.com, 2022, 8]), PartitionValueList(values=[example@partition1.com, 2022, 7]), PartitionValueList(values=[example@partition1.com, 2022, 2]), PartitionValueList(values=[example@partition1.com, 2022, 1]), PartitionValueList(values=[example@partition1.com, 2001, 1]), PartitionValueList(values=[example@partition1.com, 2017, 5]), PartitionValueList(values=[example@partition1.com, 2017, 6]), PartitionValueList(values=[example@partition1.com, 2017, 7]), PartitionValueList(values=[example@partition1.com, 2017, 8]), PartitionValueList(values=[example@partition1.com, 2017, 9]), PartitionValueList(values=[example@partition1.com, 2017, 1]), PartitionValueList(values=[example@partition1.com, 2017, 2]), PartitionValueList(values=[example@partition1.com, 2017, 3]), PartitionValueList(values=[example@partition1.com, 2017, 4]), PartitionValueList(values=[example@partition1.com, 2014, 9]), PartitionValueList(values=[example@partition1.com, 2018, 9]), PartitionValueList(values=[example@partition1.com, 2018, 8]), PartitionValueList(values=[example@partition1.com, 2018, 5]), PartitionValueList(values=[example@partition1.com, 2018, 4]), PartitionValueList(values=[example@partition1.com, 2018, 7]), PartitionValueList(values=[example@partition1.com, 2018, 6]), PartitionValueList(values=[example@partition1.com, 2018, 1]), PartitionValueList(values=[example@partition1.com, 2018, 3]), PartitionValueList(values=[example@partition1.com, 2018, 2]), PartitionValueList(values=[example@partition1.com, 2016, 10]), PartitionValueList(values=[example@partition1.com, 2016, 12]), PartitionValueList(values=[example@partition1.com, 2016, 11]), PartitionValueList(values=[example@partition1.com, 2020, 8]), PartitionValueList(values=[example@partition1.com, 2020, 7]), PartitionValueList(values=[example@partition1.com, 2020, 6]), PartitionValueList(values=[example@partition1.com, 2022, 10]), PartitionValueList(values=[example@partition1.com, 2022, 11]), PartitionValueList(values=[example@partition1.com, 2020, 5]), PartitionValueList(values=[example@partition1.com, 2022, 12]), PartitionValueList(values=[example@partition1.com, __HIVE_DEFAULT_PARTITION__, __HIVE_DEFAULT_PARTITION__]), PartitionValueList(values=[example@partition1.com, 2020, 9]), PartitionValueList(values=[example@partition1.com, 2023, 1]), PartitionValueList(values=[example@partition1.com, 2023, 2]), PartitionValueList(values=[example@partition1.com, 2023, 3]), PartitionValueList(values=[example@partition1.com, 2023, 4]), PartitionValueList(values=[example@partition1.com, 2023, 5]), PartitionValueList(values=[example@partition1.com, 2023, 6]), PartitionValueList(values=[example@partition1.com, 2023, 7]), PartitionValueList(values=[example@partition1.com, 2023, 8]), PartitionValueList(values=[example@partition1.com, 2023, 9]), PartitionValueList(values=[example@partition1.com, 2020, 4]), PartitionValueList(values=[example@partition1.com, 2020, 3]), PartitionValueList(values=[example@partition1.com, 2020, 2]), PartitionValueList(values=[example@partition1.com, 2020, 1]), PartitionValueList(values=[example@partition1.com, 2019, 12]), PartitionValueList(values=[example@partition1.com, 2019, 11]), PartitionValueList(values=[example@partition1.com, 2019, 10]), PartitionValueList(values=[example@partition1.com, 2020, 11]), PartitionValueList(values=[example@partition1.com, 2020, 10]), PartitionValueList(values=[example@partition1.com, 2015, 4]), PartitionValueList(values=[example@partition1.com, 2020, 12]), PartitionValueList(values=[example@partition1.com, 2015, 1]), PartitionValueList(values=[example@partition1.com, 2027, 10]), PartitionValueList(values=[example@partition1.com, 2012, 12])]' at 'partitionsToDelete' failed to satisfy constraint: Member must have length less than or equal to 25 (Service: AWSGlue; Status Code: 400; Error Code: ValidationException; Request ID: xxx; Proxy: null)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1879)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleServiceErrorResponse(AmazonHttpClient.java:1418)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1387)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1157)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:814)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:781)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:755)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:715)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:697)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:561)
at org.apache.hudi.com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:541)
at org.apache.hudi.com.amazonaws.services.glue.AWSGlueClient.doInvoke(AWSGlueClient.java:13784)
at org.apache.hudi.com.amazonaws.services.glue.AWSGlueClient.invoke(AWSGlueClient.java:13751)
at org.apache.hudi.com.amazonaws.services.glue.AWSGlueClient.invoke(AWSGlueClient.java:13740)
at org.apache.hudi.com.amazonaws.services.glue.AWSGlueClient.executeBatchDeletePartition(AWSGlueClient.java:406)
at org.apache.hudi.com.amazonaws.services.glue.AWSGlueClient.batchDeletePartition(AWSGlueClient.java:375)
at org.apache.hudi.aws.sync.AWSGlueCatalogSyncClient.dropPartitions(AWSGlueCatalogSyncClient.java:214)
... 62 more

Exception in thread "main" org.apache.spark.SparkException: Application application_1695917956184_0001 finished with failed status
at org.apache.spark.deploy.yarn.Client.run(Client.scala:1337)
at org.apache.spark.deploy.yarn.YarnClusterApplication.start(Client.scala:1770)
at org.apache.spark.deploy.SparkSubmit.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:1066)
at org.apache.spark.deploy.SparkSubmit.doRunMain$1(SparkSubmit.scala:192)
at org.apache.spark.deploy.SparkSubmit.submit(SparkSubmit.scala:215)
at org.apache.spark.deploy.SparkSubmit.doSubmit(SparkSubmit.scala:91)
at org.apache.spark.deploy.SparkSubmit$$anon$2.doSubmit(SparkSubmit.scala:1158)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:1167)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start at AWSGlueCatalogSyncClient.dropPartitions and follow the calls from HiveSyncTool.syncPartitions, using the reported delete_partition and re-ingestion sequence as the reproduction. Run ./hudi-sync-tool against the described Hudi table and inspect the Glue sync failure in the supplied stack trace. Done means the sync handles more than 25 partitions and the final Glue table retains the re-ingested partitions.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, java, spark
Domain
cloud, data-engineering, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.