Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
3 changes: 2 additions & 1 deletion hadoop-hdds/docs/content/feature/SCM-HA.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ ozone admin scm transfer -id <SCM_SERVICE_ID> -r
```

* `-id, --service-id`: Specifies the SCM Service ID.
* `-n, --new-leader-id`: The SCM UUID (Raft peer ID) of the SCM to which leadership will be transferred (e.g., `e6877ce5-56cd-4f0b-ad60-4c8ef9000882`).
* `-n, --new-leader-id`: The SCM to which leadership will be transferred, given either as the SCM UUID (Raft peer ID, e.g. `e6877ce5-56cd-4f0b-ad60-4c8ef9000882`) or as the configured node id (e.g. `scm1`).
* `-r, --random`: Randomly chooses a follower to transfer leadership to.

### Example
Expand All @@ -113,6 +113,7 @@ To transfer leadership to a specific SCM in a cluster with service ID `cluster1`

```bash
ozone admin scm transfer -id cluster1 -n e6877ce5-56cd-4f0b-ad60-4c8ef9000882
ozone admin scm transfer -id cluster1 -n scm1
```

To transfer leadership to a random follower:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ public List< SCMNodeDetails > getPeerNodeDetails() {
return peerNodeDetails;
}

/** Details of every SCM in the service, the local one included. */
public List<SCMNodeDetails> getAllNodeDetails() {
final List<SCMNodeDetails> allNodes = new ArrayList<>(peerNodeDetails);
allNodes.add(localNodeDetails);
return allNodes;
}

public static SCMHANodeDetails loadDefaultConfig(
OzoneConfiguration conf) throws IOException {
int ratisPort = conf.getInt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import org.apache.hadoop.hdds.scm.events.SCMEvents;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.hdds.scm.exceptions.SCMException.ResultCodes;
import org.apache.hadoop.hdds.scm.ha.SCMNodeDetails;
import org.apache.hadoop.hdds.scm.ha.SCMRatisServer;
import org.apache.hadoop.hdds.scm.ha.SCMRatisServerImpl;
import org.apache.hadoop.hdds.scm.node.DatanodeInfo;
Expand Down Expand Up @@ -1002,7 +1003,7 @@ public void transferLeadership(String newLeaderId)
.orElseThrow(() -> new IOException("Cannot" +
" find a new leader to transfer leadership."));
} else {
targetPeerId = RaftPeerId.valueOf(newLeaderId);
targetPeerId = resolveTargetPeerId(newLeaderId, group);
}
final GrpcTlsConfig tlsConfig =
createSCMRatisTLSConfig(new SecurityConfig(scm.getConfiguration()),
Expand All @@ -1019,6 +1020,23 @@ public void transferLeadership(String newLeaderId)
SCMAction.TRANSFER_LEADERSHIP, auditMap));
}

/** Resolves the transfer target, which may be given as an SCM UUID or as a configured SCM node id. */
private RaftPeerId resolveTargetPeerId(String newLeaderId, RaftGroup group) {
final RaftPeerId peerId = RaftPeerId.valueOf(newLeaderId);
if (group.getPeer(peerId) != null) {
return peerId;
}
// Raft peer ids are SCM UUIDs, so a node id only matches through its configured Ratis address.
return scm.getSCMHANodeDetails().getAllNodeDetails().stream()
Comment thread
rjgoyln marked this conversation as resolved.
Outdated
.filter(node -> newLeaderId.equals(node.getNodeId()))
.map(SCMNodeDetails::getRatisHostPortStr)
.flatMap(address -> group.getPeers().stream().filter(peer -> address.equals(peer.getAddress())))
.map(RaftPeer::getId)
.findFirst()
// Leave an unmatched id to RatisHelper, which reports the group it did not match.
.orElse(peerId);
}

@Deprecated
@Override
public List<DeletedBlocksTransactionInfo> getFailedDeletedBlockTxn(int count,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2221,10 +2221,7 @@ public String getPrimordialNode() {
String primordialNode = SCMHAUtils.getPrimordialSCM(configuration);
// primordialNode can be nodeId too . If it is then return hostname.
if (HddsUtils.getSCMNodeIds(configuration).contains(primordialNode)) {
List<SCMNodeDetails> localAndPeerNodes =
new ArrayList<>(scmHANodeDetails.getPeerNodeDetails());
localAndPeerNodes.add(getSCMHANodeDetails().getLocalNodeDetails());
for (SCMNodeDetails nodes : localAndPeerNodes) {
for (SCMNodeDetails nodes : scmHANodeDetails.getAllNodeDetails()) {
if (nodes.getNodeId().equals(primordialNode)) {
return nodes.getHostName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static class TransferOption {
@CommandLine.Option(
names = {"-n", "--new-leader-id"},
description = "The new leader id of SCM to transfer leadership. " +
"Should be ScmId(UUID)."
"Either the ScmId(UUID) or the configured node id (e.g. scm1)."
)
private String scmId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.ratis.RatisHelper;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
Expand All @@ -36,8 +39,12 @@
import org.apache.ratis.protocol.RaftPeer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import picocli.CommandLine.ExitCode;

/**
* Test transferLeadership with SCM HA setup.
Expand Down Expand Up @@ -100,29 +107,53 @@ public void testOmTransfer() throws Exception {
assertOMResetPriorities();
}

@Test
public void testScmTransfer() throws Exception {
StorageContainerManager oldLeader = getScmLeader(cluster);
List<StorageContainerManager> scmList = new ArrayList<>(cluster.
getStorageContainerManagersList());
assertThat(scmList).contains(oldLeader);
scmList.remove(oldLeader);
StorageContainerManager newLeader = scmList.get(0);
static Stream<Named<Function<StorageContainerManager, String>>> scmTargetIds() {
return Stream.of(
Named.of("scmId", StorageContainerManager::getScmId),
Named.of("nodeId", StorageContainerManager::getSCMNodeId));
}

String[] args1 = {"scm", "transfer", "-n", newLeader.getScmId()};
ozoneAdmin.execute(args1);
@ParameterizedTest
@MethodSource("scmTargetIds")
public void testScmTransfer(Function<StorageContainerManager, String> targetId) throws Exception {
StorageContainerManager newLeader = pickScmFollower();

String[] args = {"scm", "transfer", "-n", targetId.apply(newLeader)};
ozoneAdmin.execute(args);
cluster.waitForClusterToBeReady();
assertEquals(newLeader, getScmLeader(cluster));
assertSCMResetPriorities();
}

oldLeader = getScmLeader(cluster);
String[] args3 = {"scm", "transfer", "-r"};
ozoneAdmin.execute(args3);
@Test
public void testScmTransferToRandomFollower() throws Exception {
StorageContainerManager oldLeader = getScmLeader(cluster);

String[] args = {"scm", "transfer", "-r"};
ozoneAdmin.execute(args);
cluster.waitForClusterToBeReady();
assertNotSame(oldLeader, getScmLeader(cluster));
assertSCMResetPriorities();
}

@Test
public void testScmTransferToUnknownIdFails() {
Comment thread
rjgoyln marked this conversation as resolved.
Outdated
StorageContainerManager leader = getScmLeader(cluster);

String[] args = {"scm", "transfer", "-n", "no-such-scm"};
assertNotEquals(ExitCode.OK, ozoneAdmin.execute(args));
assertEquals(leader, getScmLeader(cluster));
}

private StorageContainerManager pickScmFollower() {
StorageContainerManager leader = getScmLeader(cluster);
List<StorageContainerManager> scmList = new ArrayList<>(cluster.
getStorageContainerManagersList());
assertThat(scmList).contains(leader);
scmList.remove(leader);
return scmList.get(0);
}

private void assertOMResetPriorities() {
final Collection<RaftPeer> raftPeers = cluster.getOMLeader()
.getOmRatisServer()
Expand Down