Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDDS-12050. Implement TransactionInfoRepair command for SCM #7689

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@
* permissions and
* limitations under the License.
*/
package org.apache.hadoop.ozone.repair.om;
package org.apache.hadoop.ozone.repair;

import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.scm.metadata.SCMDBDefinition;
import org.apache.hadoop.hdds.utils.IOUtils;
import org.apache.hadoop.hdds.utils.TransactionInfo;
import org.apache.hadoop.hdds.utils.db.DBColumnFamilyDefinition;
import org.apache.hadoop.hdds.utils.db.StringCodec;
import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB;
import org.apache.hadoop.ozone.debug.RocksDBUtils;
import org.apache.hadoop.ozone.repair.RepairTool;
import org.apache.hadoop.ozone.om.codec.OMDBDefinition;
import org.rocksdb.ColumnFamilyDescriptor;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.RocksDBException;
Expand All @@ -38,14 +40,13 @@
import java.util.List;

import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY;
import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TRANSACTION_INFO_TABLE;

/**
* Tool to update the highest term-index in transactionInfoTable.
* Tool to update the highest term-index in transaction info table.
*/
@CommandLine.Command(
name = "update-transaction",
description = "CLI to update the highest index in transactionInfoTable. Currently it is only supported for OM.",
description = "CLI to update the highest index in transaction info table.",
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class
)
Expand All @@ -58,27 +59,29 @@ public class TransactionInfoRepair extends RepairTool {

@CommandLine.Option(names = {"--term"},
required = true,
description = "Highest term of transactionInfoTable. The input should be non-zero long integer.")
description = "Highest term to set. The input should be non-zero long integer.")
private long highestTransactionTerm;

@CommandLine.Option(names = {"--index"},
required = true,
description = "Highest index of transactionInfoTable. The input should be non-zero long integer.")
description = "Highest index to set. The input should be non-zero long integer.")
private long highestTransactionIndex;

@Override
public void execute() throws Exception {
if (checkIfServiceIsRunning("OM")) {
final Component component = getComponent();
if (checkIfServiceIsRunning(component.name())) {
return;
}
List<ColumnFamilyHandle> cfHandleList = new ArrayList<>();
List<ColumnFamilyDescriptor> cfDescList = RocksDBUtils.getColumnFamilyDescriptors(
dbPath);

try (ManagedRocksDB db = ManagedRocksDB.open(dbPath, cfDescList, cfHandleList)) {
ColumnFamilyHandle transactionInfoCfh = RocksDBUtils.getColumnFamilyHandle(TRANSACTION_INFO_TABLE, cfHandleList);
String columnFamilyName = component.columnFamilyDefinition.getName();
ColumnFamilyHandle transactionInfoCfh = RocksDBUtils.getColumnFamilyHandle(columnFamilyName, cfHandleList);
if (transactionInfoCfh == null) {
throw new IllegalArgumentException(TRANSACTION_INFO_TABLE +
throw new IllegalArgumentException(columnFamilyName +
" is not in a column family in DB for the given path.");
}
TransactionInfo originalTransactionInfo =
Expand All @@ -97,11 +100,32 @@ public void execute() throws Exception {
TransactionInfo.getCodec()).getTermIndex());
} catch (RocksDBException exception) {
error("Failed to update the RocksDB for the given path: %s", dbPath);
error(
"Make sure that Ozone entity (OM) is not running for the give database path and current host.");
throw new IOException("Failed to update RocksDB.", exception);
} finally {
IOUtils.closeQuietly(cfHandleList);
}
}

private Component getComponent() {
final String parent = spec().parent().name();
switch (parent) {
case "om":
return Component.OM;
case "scm":
return Component.SCM;
default:
throw new IllegalStateException("Unknown component: " + parent);
}
}

private enum Component {
OM(OMDBDefinition.TRANSACTION_INFO_TABLE),
SCM(SCMDBDefinition.TRANSACTIONINFO);

private final DBColumnFamilyDefinition<String, TransactionInfo> columnFamilyDefinition;

Component(DBColumnFamilyDefinition<String, TransactionInfo> columnFamilyDefinition) {
this.columnFamilyDefinition = columnFamilyDefinition;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.hadoop.ozone.repair.om;

import org.apache.hadoop.hdds.cli.RepairSubcommand;
import org.apache.hadoop.ozone.repair.TransactionInfoRepair;
import org.apache.hadoop.ozone.repair.om.quota.QuotaRepair;
import org.kohsuke.MetaInfServices;
import picocli.CommandLine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.hadoop.ozone.repair.scm;

import org.apache.hadoop.hdds.cli.RepairSubcommand;
import org.apache.hadoop.ozone.repair.TransactionInfoRepair;
import org.apache.hadoop.ozone.repair.scm.cert.CertRepair;
import org.kohsuke.MetaInfServices;
import picocli.CommandLine;
Expand All @@ -30,6 +31,7 @@
description = "Operational tool to repair SCM.",
subcommands = {
CertRepair.class,
TransactionInfoRepair.class
}
)
@MetaInfServices(RepairSubcommand.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.ozone.repair.om;
package org.apache.hadoop.ozone.repair;

import org.apache.hadoop.hdds.scm.metadata.SCMDBDefinition;
import org.apache.hadoop.hdds.utils.IOUtils;
import org.apache.hadoop.hdds.utils.TransactionInfo;
import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB;
import org.apache.hadoop.ozone.debug.RocksDBUtils;
import org.apache.hadoop.ozone.repair.OzoneRepair;
import org.apache.hadoop.ozone.om.codec.OMDBDefinition;
import org.apache.ozone.test.GenericTestUtils;
import org.apache.ratis.server.protocol.TermIndex;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.MockedStatic;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.RocksDB;
Expand All @@ -35,7 +37,6 @@

import static org.apache.ozone.test.IntLambda.withTextFromSystemIn;
import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY;
import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TRANSACTION_INFO_TABLE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
Expand All @@ -51,7 +52,6 @@
*/
public class TestTransactionInfoRepair {


private static final String DB_PATH = "testDBPath";
private static final long TEST_TERM = 1;
private static final long TEST_INDEX = 1;
Expand All @@ -69,10 +69,11 @@ void cleanup() {
IOUtils.closeQuietly(out, err);
}

@Test
public void testUpdateTransactionInfoTableSuccessful() {
@ParameterizedTest
@ValueSource(strings = {"om", "scm"})
public void testUpdateTransactionInfoTableSuccessful(String component) {
ManagedRocksDB mdb = mockRockDB();
testCommand(mdb, mock(ColumnFamilyHandle.class));
testCommand(component, mdb, mock(ColumnFamilyHandle.class));

assertThat(out.getOutput())
.contains(
Expand All @@ -81,35 +82,37 @@ public void testUpdateTransactionInfoTableSuccessful() {
);
}

@Test
public void testCommandWhenTableNotInDBForGivenPath() {
@ParameterizedTest
@ValueSource(strings = {"om", "scm"})
public void testCommandWhenTableNotInDBForGivenPath(String component) {
ManagedRocksDB mdb = mockRockDB();
testCommand(mdb, null);
testCommand(component, mdb, null);
assertThat(err.getOutput())
.contains(TRANSACTION_INFO_TABLE + " is not in a column family in DB for the given path");
.contains(getColumnFamilyName(component) + " is not in a column family in DB for the given path");
}

@Test
public void testCommandWhenFailToUpdateRocksDBForGivenPath() throws Exception {
@ParameterizedTest
@ValueSource(strings = {"om", "scm"})
public void testCommandWhenFailToUpdateRocksDBForGivenPath(String component) throws Exception {
ManagedRocksDB mdb = mockRockDB();
RocksDB rdb = mdb.get();

ColumnFamilyHandle mock = mock(ColumnFamilyHandle.class);
doThrow(RocksDBException.class).when(rdb)
.put(eq(mock), any(byte[].class), any(byte[].class));

testCommand(mdb, mock);
testCommand(component, mdb, mock);

assertThat(err.getOutput())
.contains("Failed to update RocksDB.");
}


private void testCommand(ManagedRocksDB mdb, ColumnFamilyHandle columnFamilyHandle) {
private void testCommand(String component, ManagedRocksDB mdb, ColumnFamilyHandle columnFamilyHandle) {
final String expectedColumnFamilyName = getColumnFamilyName(component);
try (MockedStatic<ManagedRocksDB> mocked = mockStatic(ManagedRocksDB.class);
MockedStatic<RocksDBUtils> mockUtil = mockStatic(RocksDBUtils.class)) {
mocked.when(() -> ManagedRocksDB.open(anyString(), anyList(), anyList())).thenReturn(mdb);
mockUtil.when(() -> RocksDBUtils.getColumnFamilyHandle(anyString(), anyList()))
mockUtil.when(() -> RocksDBUtils.getColumnFamilyHandle(eq(expectedColumnFamilyName), anyList()))
.thenReturn(columnFamilyHandle);

mockUtil.when(() -> RocksDBUtils.getValue(eq(mdb), eq(columnFamilyHandle), eq(TRANSACTION_INFO_KEY),
Expand All @@ -128,7 +131,7 @@ private void testCommand(ManagedRocksDB mdb, ColumnFamilyHandle columnFamilyHand
CommandLine cli = new OzoneRepair().getCmd();
withTextFromSystemIn("y")
.execute(() -> cli.execute(
"om",
component,
"update-transaction",
"--db", DB_PATH,
"--term", String.valueOf(TEST_TERM),
Expand All @@ -137,6 +140,14 @@ private void testCommand(ManagedRocksDB mdb, ColumnFamilyHandle columnFamilyHand
}
}

private String getColumnFamilyName(String component) {
switch (component) {
case "om": return OMDBDefinition.TRANSACTION_INFO_TABLE.getName();
case "scm": return SCMDBDefinition.TRANSACTIONINFO.getName();
default: return "";
}
}

private ManagedRocksDB mockRockDB() {
ManagedRocksDB db = mock(ManagedRocksDB.class);
RocksDB rocksDB = mock(RocksDB.class);
Expand Down
Loading