Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions hadoop-hdds/common/src/main/resources/ozone-default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,16 @@
If this is empty, no column families are compacted.
</description>
</property>
<property>
<name>ozone.om.compaction.service.bottommostlevelcompaction</name>

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.

Please use - as separator in last part of the property name.

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.

Is this necessary? bottommostlevelcompaction is the original name as per org.rocksdb.CompactRangeOptions.BottommostLevelCompaction.

Most importantly, searching for bottommostlevelcompaction brings up all instances of it in both code and configuration in my IDE. Adding a separator would break that.

@adoroszlai adoroszlai Jul 14, 2026

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.

BottommostLevelCompaction uses CamelCase. The same with lower case needs some separator, e.g. - (kebab-case) or _ (snake_case) for readability. In config property names - is commonly used.

<value>0</value>

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.

Can we use kSkip/kForce for better readability.

@ptlrs ptlrs Jun 9, 2026

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.

While I agree that a string option will be more readable, I have kept it this way because I want to use the native org.rocksdb.CompactRangeOptions.BottommostLevelCompaction#fromRocksId method.

That method converts an integer to compaction type. There is no method that RocksDB provides that converts a string to target compaction type.

The reason for keeping it this way is that I want to avoid writing and updating our custom method as and when RocksDB provides new compaction type options.

For example, the latest RocksDB already has a kForceOptimized option which our current version doesn't support. I want to avoid creating such manual mappings and validations in Ozone code.

In case we fail to provide a mapping for some future option, we will not be able to use that option even if RocksDB supports it.

As a compromise, the helper text provides the relevant information that is needed.

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 want to avoid writing and updating our custom method as and when RocksDB provides new compaction type options

No custom method is needed if the config value is converted to enum BottommostLevelCompaction using Configuration.getEnum instead getInt.

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 have updated the code to use getEnum

@sarvekshayr sarvekshayr Jul 14, 2026

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.

getEnum expects enum names with this change. Please update the value as well as the description.

<tag>OZONE, OM, PERFORMANCE</tag>
<description>
Bottommost level compaction type for compaction.
Invalid values will default to kSkip.
Valid values: 0 (kSkip), 1 (kIfHaveCompactionFilter), 2 (kForce), 3 (kForceOptimized).
</description>
Comment on lines +1596 to +1602

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.

getEnum expects enum names with this change. Please update the value as well as the description.

Suggested change
<value>0</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
Bottommost level compaction type for compaction.
Invalid values will default to kSkip.
Valid values: 0 (kSkip), 1 (kIfHaveCompactionFilter), 2 (kForce), 3 (kForceOptimized).
</description>
<value>kSkip</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
Bottommost level compaction type for compaction.
Invalid values will default to kSkip.
Valid values: kSkip, kIfHaveCompactionFilter, kForce, kForceOptimized.
</description>

</property>

<property>
<name>ozone.om.snapshot.compact.non.snapshot.diff.tables</name>
Expand Down
4 changes: 4 additions & 0 deletions hadoop-ozone/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@
<groupId>org.apache.ozone</groupId>
<artifactId>hdds-interface-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ozone</groupId>
<artifactId>hdds-managed-rocksdb</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ozone</groupId>
<artifactId>ozone-interface-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.hdds.client.ReplicationFactor;
import org.apache.hadoop.hdds.client.ReplicationType;
import org.apache.hadoop.hdds.utils.db.managed.ManagedCompactRangeOptions;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.ratis.util.TimeDuration;

Expand Down Expand Up @@ -681,6 +682,18 @@ public final class OMConfigKeys {
public static final String OZONE_OM_COMPACTION_SERVICE_COLUMNFAMILIES_DEFAULT =
"keyTable,fileTable,directoryTable,deletedTable,deletedDirectoryTable,multipartInfoTable,multipartPartsTable";

/**
* Bottommost level compaction type for manual compaction.
* Invalid values will default to kSkip.
* Valid values: 0 (kSkip), 1 (kIfHaveCompactionFilter), 2 (kForce), 3 (kForceOptimized).
Comment on lines +686 to +688

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.

Suggested change
* Bottommost level compaction type for manual compaction.
* Invalid values will default to kSkip.
* Valid values: 0 (kSkip), 1 (kIfHaveCompactionFilter), 2 (kForce), 3 (kForceOptimized).
* Bottommost level compaction type for background compaction service.
* Invalid values will default to kSkip.
* Valid values: kSkip, kIfHaveCompactionFilter, kForce, kForceOptimized.

* Refer to {@code org.rocksdb.CompactRangeOptions.BottommostLevelCompaction}.
*/
public static final String OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION =
Comment thread
ptlrs marked this conversation as resolved.
"ozone.om.compaction.service.bottommostlevelcompaction";
public static final ManagedCompactRangeOptions.BottommostLevelCompaction
OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION_DEFAULT =
ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip;

/**
* Configuration to enable/disable non-snapshot diff table compaction when snapshots are evicted from cache.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.utils.db.RDBStore;
import org.apache.hadoop.hdds.utils.db.RocksDatabase;
import org.apache.hadoop.hdds.utils.db.managed.ManagedCompactRangeOptions;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.util.Time;
import org.slf4j.Logger;
Expand Down Expand Up @@ -74,6 +76,23 @@ public static CompletableFuture<Void> compactTableAsync(OMMetadataManager metada
});
}

public static ManagedCompactRangeOptions.BottommostLevelCompaction getBottommostLevelCompaction(
OzoneConfiguration configuration) {
ManagedCompactRangeOptions.BottommostLevelCompaction blc =
OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION_DEFAULT;

try {
blc = configuration.getEnum(
OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION,
OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION_DEFAULT);
} catch (IllegalArgumentException e) {
LOG.warn("Invalid value for bottommost level compaction configuration '{}'",
configuration.get(OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION), e);
}

return blc;
}

/**
* Converts the given RocksDB id to a
* {@link ManagedCompactRangeOptions.BottommostLevelCompaction} enum value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class CompactionService extends BackgroundService {
private final AtomicBoolean suspended;
// list of tables that can be compacted
private final List<String> compactableTables;
private final ManagedCompactRangeOptions.BottommostLevelCompaction bottommostLevelCompaction;

public CompactionService(OzoneManager ozoneManager, TimeUnit unit, long interval, long timeout,
List<String> tables) {
Expand All @@ -66,6 +67,7 @@ public CompactionService(OzoneManager ozoneManager, TimeUnit unit, long interval
this.numCompactions = new AtomicLong(0);
this.suspended = new AtomicBoolean(false);
this.compactableTables = validateTables(tables);
this.bottommostLevelCompaction = CompactDBUtil.getBottommostLevelCompaction(ozoneManager.getConfiguration());
}

private List<String> validateTables(List<String> tables) {
Expand Down Expand Up @@ -109,6 +111,11 @@ public List<String> getCompactableTables() {
return compactableTables;
}

@VisibleForTesting

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.

@VisibleForTesting annotation is no longer used, see HDDS-12725.

Suggested change
@VisibleForTesting

public ManagedCompactRangeOptions.BottommostLevelCompaction getBottommostLevelCompaction() {
return bottommostLevelCompaction;
}

/**
* Returns the number of manual compactions performed.
*
Expand Down Expand Up @@ -142,13 +149,11 @@ private boolean shouldRun() {
* @return CompletableFuture that completes when compaction finishes
*/
public CompletableFuture<Void> compactTableAsync(String tableName) {
return CompactDBUtil.compactTableAsync(omMetadataManager, tableName,
ManagedCompactRangeOptions.BottommostLevelCompaction.kForce);
return CompactDBUtil.compactTableAsync(omMetadataManager, tableName, bottommostLevelCompaction);
}

protected void compactFully(String tableName) throws IOException {
CompactDBUtil.compactTable(omMetadataManager, tableName,
ManagedCompactRangeOptions.BottommostLevelCompaction.kForce);
CompactDBUtil.compactTable(omMetadataManager, tableName, bottommostLevelCompaction);
}

private class CompactTask implements BackgroundTask {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hadoop.ozone.om.service;

import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION_DEFAULT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_DIRS;
import static org.apache.hadoop.ozone.om.service.CompactDBUtil.getBottommostLevelCompaction;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.File;
import java.io.IOException;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.utils.db.managed.ManagedCompactRangeOptions;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;

/**
* Tests for {@link CompactDBUtil}.
*/
class TestCompactDBUtil {

private OMMetadataManager omMetadataManager;

@BeforeEach
void setup(@TempDir File tempDir) throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(OZONE_OM_DB_DIRS, tempDir.getAbsolutePath());
omMetadataManager = new OmMetadataManagerImpl(conf, null);
}

@ParameterizedTest
@EnumSource(ManagedCompactRangeOptions.BottommostLevelCompaction.class)
void testCompactionAlgorithms(ManagedCompactRangeOptions.BottommostLevelCompaction bottommostLevelCompaction) {
assertDoesNotThrow(() ->
CompactDBUtil.compactTable(omMetadataManager, "keyTable", bottommostLevelCompaction));
}

@Test
void testCompactInvalidColumnFamily() {
assertThrows(IOException.class, () ->
CompactDBUtil.compactTable(omMetadataManager, "nonExistentTable",
ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip));
}

@Test
void testDefaultConfigValueMapsToKSkip() {
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip,
OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION_DEFAULT);
}

@ParameterizedTest
@ValueSource(strings = {"", "kForceeee"})
void testDefaultConfigKeyIsReadFromOzoneConfiguration(String compactionType) {
// unset or invalid values should use the default value
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, compactionType);
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip, getBottommostLevelCompaction(conf));
}

@ParameterizedTest
@ValueSource(strings = {"kForce", " kForce", "kForce ", " kForce "})
void testConfigKeyIsReadFromOzoneConfiguration(String compactionType) {
// have trailing spaces in the config values to ensure they are trimmed and handled correctly
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, compactionType);
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kForce, getBottommostLevelCompaction(conf));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

package org.apache.hadoop.ozone.om.service;

import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_ENABLED;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_COMPACTION_SERVICE_RUN_INTERVAL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -39,6 +41,7 @@
import org.apache.hadoop.hdds.server.ServerUtils;
import org.apache.hadoop.hdds.utils.db.DBConfigFromFile;
import org.apache.hadoop.hdds.utils.db.TypedTable;
import org.apache.hadoop.hdds.utils.db.managed.ManagedCompactRangeOptions;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OzoneManager;
Expand Down Expand Up @@ -74,6 +77,7 @@ void setup(@TempDir Path tempDir) {
ozoneManager = mock(OzoneManager.class);
OMMetadataManager metadataManager = mock(OMMetadataManager.class);
when(ozoneManager.getMetadataManager()).thenReturn(metadataManager);
when(ozoneManager.getConfiguration()).thenReturn(conf);
TypedTable table = mock(TypedTable.class);

Set<String> tables = new HashSet<>();
Expand Down Expand Up @@ -159,6 +163,35 @@ public void testCompactFailure() {
() -> getCompactionService(compactTables));
}

@Test
public void testDefaultCompactionLevelIsKSkip() {
CompactionService compactionService = getCompactionService(Arrays.asList("keyTable", "fileTable"));
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip,
compactionService.getBottommostLevelCompaction());
}

@Test
public void testConfiguredCompactionLevelKForce() {
OzoneConfiguration conf = new OzoneConfiguration();
conf.setInt(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, 2);

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.

Suggested change
conf.setInt(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, 2);
conf.set(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, "kForce");

when(ozoneManager.getConfiguration()).thenReturn(conf);

CompactionService compactionService = getCompactionService(Arrays.asList("keyTable", "fileTable"));
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kForce,
compactionService.getBottommostLevelCompaction());
}

@Test
public void testInvalidCompactionLevelFallsBackToDefault() {
OzoneConfiguration conf = new OzoneConfiguration();
conf.setInt(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, 99);

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.

Any invalid string can be used here -

Suggested change
conf.setInt(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, 99);
conf.set(OZONE_OM_COMPACTION_SERVICE_BOTTOMMOSTLEVELCOMPACTION, "abc");

when(ozoneManager.getConfiguration()).thenReturn(conf);

CompactionService compactionService = getCompactionService(Arrays.asList("keyTable", "fileTable"));
assertEquals(ManagedCompactRangeOptions.BottommostLevelCompaction.kSkip,
compactionService.getBottommostLevelCompaction());
}

private CompactionService getCompactionService(List<String> compactTables) {
CompactionService compactionService = new CompactionService(ozoneManager, TimeUnit.MILLISECONDS,
TimeUnit.SECONDS.toMillis(SERVICE_INTERVAL), TimeUnit.SECONDS.toMillis(60), compactTables) {
Expand Down