mirror of https://github.com/apache/cassandra
Tolerate missing DNS entry when completing host replacement
patch by Chris Lohfink; reviewed by Brandon Williams for CASSANDRA-16873 Co-authored by Chris Lohfink <clohfink@apple.com> Co-authored by Josh McKenzie <jmckenzie@apache.org>
This commit is contained in:
parent
2b6799a394
commit
f59411f1c9
|
|
@ -1,4 +1,5 @@
|
|||
4.0.1
|
||||
* Tolerate missing DNS entry when completing a host replacement (CASSANDRA-16873)
|
||||
* Harden PrunableArrayQueue against Pruner implementations that might throw exceptions (CASSANDRA-16866)
|
||||
* Move RepairedDataInfo to the execution controller rather than the ReadCommand to avoid unintended sharing (CASSANDRA-16721)
|
||||
* Bump zstd-jni version to 1.5.0-4 (CASSANDRA-16884)
|
||||
|
|
|
|||
|
|
@ -2719,6 +2719,38 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
|||
SystemKeyspace.updateTokens(endpoint, tokensToUpdateInSystemKeyspace);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public boolean isReplacingSameHostAddressAndHostId(UUID hostId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return isReplacingSameAddress() &&
|
||||
Gossiper.instance.getEndpointStateForEndpoint(DatabaseDescriptor.getReplaceAddress()) != null
|
||||
&& hostId.equals(Gossiper.instance.getHostId(DatabaseDescriptor.getReplaceAddress()));
|
||||
}
|
||||
catch (RuntimeException ex)
|
||||
{
|
||||
// If a host is decomissioned and the DNS entry is removed before the
|
||||
// bootstrap completes, when it completes and advertises NORMAL state to other nodes, they will be unable
|
||||
// to resolve it to an InetAddress unless it happens to be cached. This could happen on nodes
|
||||
// storing large amounts of data or with long index rebuild times or if new instances have been added
|
||||
// to the cluster through expansion or additional host replacement.
|
||||
//
|
||||
// The original host replacement must have been able to resolve the replacing address on startup
|
||||
// when setting StorageService.replacing, so if it is impossible to resolve now it is probably
|
||||
// decommissioned and did not have the same IP address or host id. Allow the handleStateNormal
|
||||
// handling to proceed, otherwise gossip state will be inconistent with some nodes believing the
|
||||
// replacement host to be normal, and nodes unable to resolve the hostname will be left in JOINING.
|
||||
if (ex.getCause() != null && ex.getCause().getClass() == UnknownHostException.class)
|
||||
{
|
||||
logger.info("Suppressed exception while checking isReplacingSameHostAddressAndHostId({}). Original host was probably decommissioned. ({})",
|
||||
hostId, ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
throw ex; // otherwise rethrow
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle node move to normal state. That is, node is entering token ring and participating
|
||||
* in reads.
|
||||
|
|
@ -2764,9 +2796,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
|||
// Order Matters, TM.updateHostID() should be called before TM.updateNormalToken(), (see CASSANDRA-4300).
|
||||
UUID hostId = Gossiper.instance.getHostId(endpoint);
|
||||
InetAddressAndPort existing = tokenMetadata.getEndpointForHostId(hostId);
|
||||
if (replacing && isReplacingSameAddress() && Gossiper.instance.getEndpointStateForEndpoint(DatabaseDescriptor.getReplaceAddress()) != null
|
||||
&& (hostId.equals(Gossiper.instance.getHostId(DatabaseDescriptor.getReplaceAddress()))))
|
||||
if (replacing && isReplacingSameHostAddressAndHostId(hostId))
|
||||
{
|
||||
logger.warn("Not updating token metadata for {} because I am replacing it", endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existing != null && !existing.equals(endpoint))
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ import java.io.FileWriter;
|
|||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import org.apache.cassandra.db.SystemKeyspace;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
|
@ -697,4 +699,34 @@ public class StorageServiceServerTest
|
|||
assertTrue(AuditLogManager.instance.isEnabled());
|
||||
StorageService.instance.disableAuditLog();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isReplacingSameHostAddressAndHostIdTest() throws UnknownHostException
|
||||
{
|
||||
try
|
||||
{
|
||||
UUID differentHostId = UUID.randomUUID();
|
||||
Assert.assertFalse(StorageService.instance.isReplacingSameHostAddressAndHostId(differentHostId));
|
||||
|
||||
final String hostAddress = FBUtilities.getBroadcastAddressAndPort().getHostAddress(false);
|
||||
UUID localHostId = SystemKeyspace.getLocalHostId();
|
||||
Gossiper.instance.initializeNodeUnsafe(FBUtilities.getBroadcastAddressAndPort(), localHostId, 1);
|
||||
|
||||
// Check detects replacing the same host address with the same hostid
|
||||
System.setProperty("cassandra.replace_address", hostAddress);
|
||||
Assert.assertTrue(StorageService.instance.isReplacingSameHostAddressAndHostId(localHostId));
|
||||
|
||||
// Check detects replacing the same host address with a different host id
|
||||
System.setProperty("cassandra.replace_address", hostAddress);
|
||||
Assert.assertFalse(StorageService.instance.isReplacingSameHostAddressAndHostId(differentHostId));
|
||||
|
||||
// Check tolerates the DNS entry going away for the replace_address
|
||||
System.setProperty("cassandra.replace_address", "unresolvable.host.local.");
|
||||
Assert.assertFalse(StorageService.instance.isReplacingSameHostAddressAndHostId(differentHostId));
|
||||
}
|
||||
finally
|
||||
{
|
||||
System.clearProperty("cassandra.replace_address");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue