[ 
https://issues.apache.org/jira/browse/HDDS-17205?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng updated HDDS-17205:
------------------------------
    Description: 
When OM or SCM HA node addresses are configured as IP literals (for example 
{{ozone.scm.address.scmservice.scm1=[fd00:0:0:6::11]}}), the Ratis peer 
addresses are not the literals. They are whatever reverse DNS returns for the 
literal on the host that builds them. Different hosts can get different names 
for the same address (short name from {{/etc/hosts}} on the node itself, FQDN 
from DNS on the others), and a name may not be usable at all. Both SCM HA and 
OM HA fail to form a ring in the IPv6 compose cluster with literal addresses.

h3. Cause

{{NodeDetails}} builds the Ratis address from {{getRpcAddress().getHostName()}}:

{code:java}
public String getRatisHostPortStr() {
  return HddsUtils.getHostPortString(getHostName(), ratisPort);
}
{code}

For an {{InetSocketAddress}} created from a literal, {{getHostName()}} does a 
reverse lookup and caches the result in the address, so later 
{{getHostString()}} calls return the looked up name too. The same lookup 
happens earlier in {{NodeDetails}}' constructor ({{this.hostAddress = 
rpcAddress.getHostName()}}), the "Found matching ... address" log lines in 
{{SCMHANodeDetails}} and {{OMHANodeDetails}}, and the HTTP address helpers in 
{{OMHANodeDetails}} / {{OMNodeDetails}}. Host names in the configuration are 
not affected, because {{getHostName()}} returns the configured name without a 
lookup.

Lookups of the same address in the compose cluster:

||lookup of fd00:0:0:6::11 in||result||
|scm1 (itself)|7fdcd1080e27|
|scm2|ozone-ha-scm1-1.ozone-ha_default|

h3. Symptoms

*SCM HA:* the leader announces itself as {{7fdcd1080e27:9894}}. 
{{SCMStateMachine#notifyInstallSnapshotFromLeader}} looks the leader up by 
comparing that string with {{getRatisHostPortStr()}} of each peer 
({{ozone-ha-scm1-1.ozone-ha_default:9894}}), finds nothing, and fails 
{{Preconditions.checkState(leaderDetails.isPresent())}} with no message. Ratis 
sends the next install snapshot notification immediately, so the bootstrapping 
SCM loops forever (scm1 wrote about 90 MB of log in 4 minutes) and SCM stays in 
safe mode. {{SCMClientProtocolServer#resolveTargetPeerId}} (leader transfer by 
node ID) uses the same string comparison.

{noformat}
ERROR impl.SnapshotInstallationHandler: ...: installSnapshot failed
java.lang.IllegalStateException
        at 
com.google.common.base.Preconditions.checkState(Preconditions.java:519)
        at 
org.apache.hadoop.hdds.scm.ha.SCMStateMachine.notifyInstallSnapshotFromLeader(SCMStateMachine.java:285)
{noformat}

*OM HA:* the peers are {{om2|ozone-ha-om2-1.ozone-ha_default:9872}} and so on, 
and no leader is elected. In this environment gRPC rejects the name because of 
the underscore in the Docker network name ({{Invalid DNS name: 
ozone-ha-om2-1.ozone-ha_default:9872}}). That part is specific to Docker, but 
the general problem is that the configured literal is replaced by a name that 
must then be resolved again. On a dual-stack host, that name can resolve to an 
IPv4 address even though an IPv6 literal was configured.

h3. Reproduction

{{compose/ozone-ha}} on an IPv6-only network (HDDS-16969) with static addresses 
for scm1..3 and om1..3, and {{ozone.scm.address.scmservice.scmN}} / 
{{ozone.om.address.omservice.omN}} set to the bracketed literals. Found while 
testing the HDDS-15763 patches (including HDDS-17135) against Hadoop 
branch-3.4.4 on Docker Desktop.

h3. Proposed fix

Keep the configured host string (literal or name) for Ratis peer addresses and 
for matching the leader, and do not call {{getHostName()}} on these addresses 
before it is captured. Only changing {{getRatisHostPortStr()}} to 
{{getHostString()}} is not enough: it was tried locally and still returned the 
looked up name because of the earlier {{getHostName()}} calls. Matching the 
leader by resolved socket address (IP and port) instead of by string would also 
make the SCM lookup robust. The tight retry loop after a failed notification is 
tracked separately in HDDS-17127.

  was:
When SCM HA addresses are configured as IP literals 
({{ozone.scm.address.<service>.<node>=[fd00:0:0:6::11]}}), a bootstrapping SCM 
cannot install a snapshot from the leader and never joins the ring. SCM stays 
in safe mode and the leader and follower log thousands of lines per second.

h3. Cause

{{SCMStateMachine#notifyInstallSnapshotFromLeader}} looks up the leader by 
comparing the leader's Ratis address string with {{getRatisHostPortStr()}} of 
each configured peer:

{code:java}
.filter(p -> p.getRatisHostPortStr().equals(leaderAddress))
...
Preconditions.checkState(leaderDetails.isPresent());
{code}

{{NodeDetails#getRatisHostPortStr}} uses {{getRpcAddress().getHostName()}}. For 
an address configured as a literal, that is a reverse lookup, and its result 
depends on the host doing the lookup. The leader resolves its own address 
through {{/etc/hosts}} (short host name), while the follower resolves the same 
address through DNS (FQDN). In the IPv6 compose cluster:

||lookup of fd00:0:0:6::11 in||result||
|scm1 (leader, itself)|7fdcd1080e27|
|scm2 (follower)|ozone-ha-scm1-1.ozone-ha_default|

So the leader announces {{7fdcd1080e27:9894}}, the follower finds no matching 
peer, and {{checkState}} throws an {{IllegalStateException}} with no message. 
Ratis immediately sends the next install snapshot notification, which fails the 
same way, so this becomes a tight loop (scm1 wrote about 90 MB of log in 4 
minutes).

Host names in the configuration are not affected, because {{getHostName()}} 
returns the configured name without a lookup. Any literal (IPv4 or IPv6) is 
affected when reverse lookups disagree between hosts, which is common (short 
name in {{/etc/hosts}}, FQDN in DNS). IPv6 deployments without DNS names for 
every node are the most likely to configure literals.

h3. Reproduction

{{compose/ozone-ha}} on an IPv6-only network (HDDS-16969) with static addresses 
for scm1..3 and {{ozone.scm.address.scmservice.scmN}} set to the bracketed 
literals. Found while testing the HDDS-15763 patches (including HDDS-17135) on 
Docker Desktop.

{noformat}
ERROR impl.SnapshotInstallationHandler: ...: installSnapshot failed
java.lang.IllegalStateException
        at 
com.google.common.base.Preconditions.checkState(Preconditions.java:519)
        at 
org.apache.hadoop.hdds.scm.ha.SCMStateMachine.notifyInstallSnapshotFromLeader(SCMStateMachine.java:285)
{noformat}

h3. Proposed fix

Match the leader by resolved socket address (IP and port) instead of the host 
name string, and fail with a message naming the leader address when no peer 
matches. The tight retry loop after a failed notification is tracked separately 
in HDDS-17127.

        Summary: OM and SCM HA use reverse DNS names instead of configured IP 
literals for Ratis peer addresses  (was: SCM follower cannot install a snapshot 
when SCM addresses are configured as IP literals)

> OM and SCM HA use reverse DNS names instead of configured IP literals for 
> Ratis peer addresses
> ----------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17205
>                 URL: https://issues.apache.org/jira/browse/HDDS-17205
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Assignee: Siyao Meng
>            Priority: Major
>
> When OM or SCM HA node addresses are configured as IP literals (for example 
> {{ozone.scm.address.scmservice.scm1=[fd00:0:0:6::11]}}), the Ratis peer 
> addresses are not the literals. They are whatever reverse DNS returns for the 
> literal on the host that builds them. Different hosts can get different names 
> for the same address (short name from {{/etc/hosts}} on the node itself, FQDN 
> from DNS on the others), and a name may not be usable at all. Both SCM HA and 
> OM HA fail to form a ring in the IPv6 compose cluster with literal addresses.
> h3. Cause
> {{NodeDetails}} builds the Ratis address from 
> {{getRpcAddress().getHostName()}}:
> {code:java}
> public String getRatisHostPortStr() {
>   return HddsUtils.getHostPortString(getHostName(), ratisPort);
> }
> {code}
> For an {{InetSocketAddress}} created from a literal, {{getHostName()}} does a 
> reverse lookup and caches the result in the address, so later 
> {{getHostString()}} calls return the looked up name too. The same lookup 
> happens earlier in {{NodeDetails}}' constructor ({{this.hostAddress = 
> rpcAddress.getHostName()}}), the "Found matching ... address" log lines in 
> {{SCMHANodeDetails}} and {{OMHANodeDetails}}, and the HTTP address helpers in 
> {{OMHANodeDetails}} / {{OMNodeDetails}}. Host names in the configuration are 
> not affected, because {{getHostName()}} returns the configured name without a 
> lookup.
> Lookups of the same address in the compose cluster:
> ||lookup of fd00:0:0:6::11 in||result||
> |scm1 (itself)|7fdcd1080e27|
> |scm2|ozone-ha-scm1-1.ozone-ha_default|
> h3. Symptoms
> *SCM HA:* the leader announces itself as {{7fdcd1080e27:9894}}. 
> {{SCMStateMachine#notifyInstallSnapshotFromLeader}} looks the leader up by 
> comparing that string with {{getRatisHostPortStr()}} of each peer 
> ({{ozone-ha-scm1-1.ozone-ha_default:9894}}), finds nothing, and fails 
> {{Preconditions.checkState(leaderDetails.isPresent())}} with no message. 
> Ratis sends the next install snapshot notification immediately, so the 
> bootstrapping SCM loops forever (scm1 wrote about 90 MB of log in 4 minutes) 
> and SCM stays in safe mode. {{SCMClientProtocolServer#resolveTargetPeerId}} 
> (leader transfer by node ID) uses the same string comparison.
> {noformat}
> ERROR impl.SnapshotInstallationHandler: ...: installSnapshot failed
> java.lang.IllegalStateException
>       at 
> com.google.common.base.Preconditions.checkState(Preconditions.java:519)
>       at 
> org.apache.hadoop.hdds.scm.ha.SCMStateMachine.notifyInstallSnapshotFromLeader(SCMStateMachine.java:285)
> {noformat}
> *OM HA:* the peers are {{om2|ozone-ha-om2-1.ozone-ha_default:9872}} and so 
> on, and no leader is elected. In this environment gRPC rejects the name 
> because of the underscore in the Docker network name ({{Invalid DNS name: 
> ozone-ha-om2-1.ozone-ha_default:9872}}). That part is specific to Docker, but 
> the general problem is that the configured literal is replaced by a name that 
> must then be resolved again. On a dual-stack host, that name can resolve to 
> an IPv4 address even though an IPv6 literal was configured.
> h3. Reproduction
> {{compose/ozone-ha}} on an IPv6-only network (HDDS-16969) with static 
> addresses for scm1..3 and om1..3, and {{ozone.scm.address.scmservice.scmN}} / 
> {{ozone.om.address.omservice.omN}} set to the bracketed literals. Found while 
> testing the HDDS-15763 patches (including HDDS-17135) against Hadoop 
> branch-3.4.4 on Docker Desktop.
> h3. Proposed fix
> Keep the configured host string (literal or name) for Ratis peer addresses 
> and for matching the leader, and do not call {{getHostName()}} on these 
> addresses before it is captured. Only changing {{getRatisHostPortStr()}} to 
> {{getHostString()}} is not enough: it was tried locally and still returned 
> the looked up name because of the earlier {{getHostName()}} calls. Matching 
> the leader by resolved socket address (IP and port) instead of by string 
> would also make the SCM lookup robust. The tight retry loop after a failed 
> notification is tracked separately in HDDS-17127.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to