Skip to content

HIVE-27050: Restrict reducer extrapolation to contain number of small… - #4477

Merged
deniskuzZ merged 1 commit into
apache:masterfrom
difin:HIVE-27050_iceberg-reducers
Aug 1, 2023
Merged

deniskuzZ merged 1 commit into
apache:masterfrom
difin:HIVE-27050_iceberg-reducers

Conversation

@difin

@difin difin commented Jul 11, 2023

Copy link
Copy Markdown
Contributor

… files being created for Iceberg tables.

What changes were proposed in this pull request?

Setting "hive.tez.max.partition.factor" to "1.0" for Iceberg tables.

Why are the changes needed?

When Iceberg MOR tables are involved in update/delete/merges, Hive estimates the number of reducers as "1". But due to "hive.tez.max.partition.factor" which defaults to "2.0", it will double the number of reducers.
It creates very small positional delete files spreading across different reducers. This will cause problems during reading, as all files should be opened for reading.

Does this PR introduce any user-facing change?

No

Is the change a dependency upgrade?

No

How was this patch tested?

Passing pre-commit tests.

@difin
difin force-pushed the HIVE-27050_iceberg-reducers branch from a0db293 to 772a837 Compare July 11, 2023 17:37

@aturoczy aturoczy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1

// TODO: remove once we have both Fanout and ClusteredWriter available: HIVE-25948
HiveConf.setIntVar(configuration, HiveConf.ConfVars.HIVEOPTSORTDYNAMICPARTITIONTHRESHOLD, 1);
HiveConf.setVar(configuration, HiveConf.ConfVars.DYNAMICPARTITIONINGMODE, "nonstrict");
HiveConf.setFloatVar(configuration, HiveConf.ConfVars.TEZ_MAX_PARTITION_FACTOR, 1f);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it OK to apply this one to all vertices including non insert reducers?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I updated the solution to apply this only for write operations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@okumin Nice catch!

serDeProperties.getProperty(Catalogs.NAME));

if (operation != null) {
HiveConf.setFloatVar(configuration, HiveConf.ConfVars.TEZ_MAX_PARTITION_FACTOR, 1f);

@okumin okumin Jul 16, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I tested how this works as I am not confident. Please not that I am still a beginner of Iceberg and something could be wrong with my test.

Test queries

Prep

beeline -e "
DROP TABLE IF EXISTS test;
CREATE TABLE test (id INT) STORED BY ICEBERG TBLPROPERTIES('format-version'='2');
INSERT INTO test VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
"

DELETE

beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true

Test result

The original version

This is the tested revision. Two reducers are set up and launched as reported.

$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-16 04:55:39,740	Map 1: 0(+1)/1	Reducer 2: 0/2	
INFO  : 2023-07-16 04:55:42,259	Map 1: 1/1	Reducer 2: 0(+1)/2
$ ./bin/logs hive-hiveserver2-655b558bb-gzwh8 | grep HIVE-27050 | tail -n 1
hive-hiveserver2-655b558bb-gzwh8: 2023-07-16T04:55:31,760  INFO [f3ecdcb4-f45b-405f-986e-0dbf525c7c87 HiveServer2-Handler-Pool: Thread-61] parse.GenTezUtils: HIVE-27050: max partition factor=2.0, max partition=2

The first patch

This is the tested revision. One reducer is set up and launched.

$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-16 05:22:30,905	Map 1: 0/1	Reducer 2: 0/1	
INFO  : 2023-07-16 05:22:35,454	Map 1: 0(+1)/1	Reducer 2: 0/1
$ ./bin/logs hive-hiveserver2-75bc6bc94c-bhxxc | grep HIVE-27050 | tail -n 1
hive-hiveserver2-75bc6bc94c-bhxxc: 2023-07-16T05:22:23,324  INFO [18f9ace5-2016-4430-8155-ae95c254148b HiveServer2-Handler-Pool: Thread-61] parse.GenTezUtils: HIVE-27050: max partition factor=1.0, max partition=1

The second(current) path

This is the tested revision. It is unlikely to be working as expected.

$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-16 05:58:27,235	Map 1: 1/1	Reducer 2: 0/2	
INFO  : 2023-07-16 05:58:27,741	Map 1: 1/1	Reducer 2: 0(+1)/2
$ ./bin/logs hive-hiveserver2-5876b658cd-8zxwp | grep HIVE-27050 | tail -n 3
hive-hiveserver2-5876b658cd-8zxwp: 2023-07-16T05:58:13,110  INFO [62373162-813c-409b-834d-335f2f72efc1 HiveServer2-Handler-Pool: Thread-61] parse.GenTezUtils: HIVE-27050: max partition factor=2.0, max partition=2
hive-hiveserver2-5876b658cd-8zxwp: 2023-07-16T05:58:13,112  INFO [62373162-813c-409b-834d-335f2f72efc1 HiveServer2-Handler-Pool: Thread-61] hive.HiveIcebergSerDe: HIVE-27050: operation: null
hive-hiveserver2-5876b658cd-8zxwp: 2023-07-16T05:58:28,411  INFO [HiveServer2-Background-Pool: Thread-111] hive.HiveIcebergSerDe: HIVE-27050: operation: null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some random thoughts. I would say these are minor.

  • Is it best to disable over-provisioning only for DELETE reducers using a Hive hook or something?
    • Over-provisioning might work for INSERT or UPDATE
    • It could work also for DELETE if rows are filtered
    • Maybe, the optimization should be applied to only the last reducer in the case like Map -> Reduce -> Reduce?
  • hive.tez.auto.reducer.parallelism.min.threshold=0.0 can be an option?

@difin difin Jul 17, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @okumin,
Thank you for all the info!
I tried a DELETE query with your test table with hive.tez.auto.reducer.parallelism.min.threshold=0.0 and it still gave 2 reducers.
I also noticed in debug that GenTezUtils.createReduceWork() is called before HiveIcebergSerDe.initialize() with a write operation where I tried to set max partition factor to 1 which probably explains why my change doesn't work.
What do you think, can I set max partition factor to 1.0 in GenTezUtils.createReduceWork() if the operation is a write operation for Iceberg table?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I personally think it is reasonable to explicitly inject the logic into GenTezUtils or somewhere. One request is I'd like to make it pluggable because other formats would hit the same issue. As far as I checked on my machine, Hive ACID shares the problem. Note that these are my opinions and committers could have different ideas, or they might think it is an expected behavior.

As for the parameter, I guess you tested it with 4.0.0-alpha-2 since the param was merged recently. It enforces auto reduce parallelism.

$ beeline -e "
> DROP TABLE IF EXISTS test;
> CREATE TABLE test (id INT) STORED BY ICEBERG TBLPROPERTIES('format-version'='2');
> INSERT INTO test VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
> "
$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-18 14:19:45,200	Map 1: 0(+1)/1	Reducer 2: 0/2	
INFO  : 2023-07-18 14:19:48,227	Map 1: 1/1	Reducer 2: 0(+1)/2
...
$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true --hiveconf hive.tez.auto.reducer.parallelism.min.threshold=0.0
...
INFO  : 2023-07-18 14:20:23,730	Map 1: 0(+1)/1	Reducer 2: 0/2	
INFO  : 2023-07-18 14:20:27,271	Map 1: 1/1	Reducer 2: 0(+1)/1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I updated the solution and placed it in GenTezUtils and made it for Iceberg and Hive ACID.

if (context.parseContext.getContext().getOperation() == Context.Operation.DELETE &&
isRestrictReducerExtrapolation(context)) {
LOG.debug("Overriding maxPartitionFactor to 1.0 to prevent creation of small files after delete operation");
maxPartitionFactor = 1f;

@okumin okumin Jul 20, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I quickly double-checked it works as expected. Let's wait for the comment of committers about this approach.

ACID

$ beeline -e "
> DROP TABLE IF EXISTS test;
> CREATE TABLE test (id INT) STORED AS ORC TBLPROPERTIES ('transactional'='true');
> INSERT INTO test VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
> "
...
$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-20 12:52:55,637	Map 1: -/-	Reducer 2: 0/1	
INFO  : 2023-07-20 12:52:57,161	Map 1: 0/1	Reducer 2: 0/1

Iceberg

$ beeline -e "
> DROP TABLE IF EXISTS test;
> CREATE TABLE test (id INT) STORED BY ICEBERG TBLPROPERTIES('format-version'='2');
> INSERT INTO test VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
> "
...
$ beeline -e 'DELETE FROM test WHERE id = 5' --hiveconf hive.server2.in.place.progress=false --hiveconf hive.tez.auto.reducer.parallelism=true
...
INFO  : 2023-07-20 12:55:56,104	Map 1: -/-	Reducer 2: 0/1	
INFO  : 2023-07-20 12:55:58,337	Map 1: 0/1	Reducer 2: 0/1

@@ -231,10 +231,17 @@ public static void validateTableIsIceberg(org.apache.hadoop.hive.ql.metadata.Tab
}

public static boolean isIcebergTable(Table table) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wow, I originally thought the change be available also for Delta Lake, Hudi, etc, but looks like we hardcode many for Iceberg...
I will create a ticket to push this kind of logic to StorageHandler...

table.getStorageHandler().getType() == StorageHandlerTypes.ICEBERG;
return table.isNonNative() &&
((table.getStorageHandler() != null && table.getStorageHandler().getType() == StorageHandlerTypes.ICEBERG) ||
isIcebergTableType(table.getTTable().getParameters()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there any case where StorageHandler#getType != ICEBERG but isIcebergTableType = true?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no, shouldn't be

@difin difin Jul 21, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed @ retested using MiniHS2Cluster for Iceberg and Hive ACID tables.

return unionWork;
}

private static boolean isRestrictReducerExtrapolation(GenTezProcContext context) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could we refactor to

  private static boolean isRestrictReducerExtrapolation(Context context) {
    return context.getOperation() == Context.Operation.DELETE && context.getLoadTableOutputMap().values()
      .stream().map(WriteEntity::getTable).anyMatch(DDLUtils::isIcebergTable);
  }
-> isRestrictReducerExtrapolation(context.parseContext.getContext())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@difin
difin force-pushed the HIVE-27050_iceberg-reducers branch from 8ad5316 to 6115012 Compare July 29, 2023 02:35
@sonarqubecloud

Copy link
Copy Markdown

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@deniskuzZ deniskuzZ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM +1

@deniskuzZ
deniskuzZ merged commit 4ad7d16 into apache:master Aug 1, 2023
tarak271 pushed a commit to tarak271/hive-1 that referenced this pull request Dec 19, 2023
… of small files being created for Iceberg tables (Dmitriy Fingerman, reviewed by Denys Kuzmenko, okumin)

Closes apache#4477
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.

5 participants