From 59b5b6bef0fa76bf5740b688fcd4d9cf525760d0 Mon Sep 17 00:00:00 2001 From: Ariel Weisberg Date: Thu, 9 Nov 2017 11:33:48 -0500 Subject: [PATCH] Allow storage port to be configurable per node Patch by Ariel Weisberg; Reviewed by Jason Brown for CASSANDRA-7544 --- CHANGES.txt | 1 + NEWS.txt | 18 +- bin/cqlsh.py | 12 +- build.xml | 10 +- conf/cassandra.yaml | 9 +- ...dra-driver-core-4.0.0-SNAPSHOT-shaded.jar} | Bin 2618371 -> 2621460 bytes ...r-internal-only-3.12.0.post0-9ee88ded.zip} | Bin 264882 -> 265110 bytes .../cassandra/batchlog/BatchlogManager.java | 52 +- .../cassandra/config/DatabaseDescriptor.java | 27 +- .../apache/cassandra/db/ConsistencyLevel.java | 40 +- .../db/CounterMutationVerbHandler.java | 2 +- .../cassandra/db/DiskBoundaryManager.java | 4 +- .../org/apache/cassandra/db/Keyspace.java | 1 + .../org/apache/cassandra/db/Mutation.java | 3 - .../cassandra/db/MutationVerbHandler.java | 43 +- .../cassandra/db/SizeEstimatesRecorder.java | 4 +- .../apache/cassandra/db/SystemKeyspace.java | 516 ++++++----- .../db/SystemKeyspaceMigrator40.java | 184 ++++ .../apache/cassandra/db/view/ViewUtils.java | 18 +- .../apache/cassandra/dht/BootStrapper.java | 12 +- .../dht/RangeFetchMapCalculator.java | 37 +- .../apache/cassandra/dht/RangeStreamer.java | 96 +- .../dht/tokenallocator/TokenAllocation.java | 50 +- .../tokenallocator/TokenAllocatorFactory.java | 8 +- .../exceptions/ReadFailureException.java | 4 +- .../exceptions/RequestFailureException.java | 6 +- .../exceptions/WriteFailureException.java | 4 +- .../cassandra/gms/ApplicationState.java | 11 +- .../apache/cassandra/gms/EndpointState.java | 8 +- .../apache/cassandra/gms/FailureDetector.java | 74 +- .../cassandra/gms/FailureDetectorMBean.java | 9 +- .../apache/cassandra/gms/GossipDigest.java | 14 +- .../apache/cassandra/gms/GossipDigestAck.java | 22 +- .../cassandra/gms/GossipDigestAck2.java | 22 +- .../gms/GossipDigestAck2VerbHandler.java | 6 +- .../gms/GossipDigestAckVerbHandler.java | 10 +- .../gms/GossipDigestSynVerbHandler.java | 12 +- .../org/apache/cassandra/gms/Gossiper.java | 274 +++--- .../gms/IEndpointStateChangeSubscriber.java | 16 +- .../gms/IFailureDetectionEventListener.java | 4 +- .../cassandra/gms/IFailureDetector.java | 12 +- .../apache/cassandra/gms/VersionedValue.java | 17 + .../apache/cassandra/hadoop/ConfigHelper.java | 12 + .../hadoop/cql3/CqlBulkRecordWriter.java | 20 +- .../hadoop/cql3/CqlConfigHelper.java | 12 + .../cassandra/hints/HintVerbHandler.java | 6 +- .../hints/HintsDispatchExecutor.java | 12 +- .../cassandra/hints/HintsDispatchTrigger.java | 2 +- .../cassandra/hints/HintsDispatcher.java | 10 +- .../apache/cassandra/hints/HintsService.java | 8 +- .../apache/cassandra/hints/HintsStore.java | 6 +- .../io/DummyByteVersionedSerializer.java | 55 ++ .../io/ShortVersionedSerializer.java | 47 + .../cassandra/io/sstable/SSTableLoader.java | 31 +- .../locator/AbstractEndpointSnitch.java | 19 +- .../AbstractNetworkTopologySnitch.java | 8 +- .../locator/AbstractReplicationStrategy.java | 41 +- .../cassandra/locator/CloudstackSnitch.java | 11 +- .../locator/DynamicEndpointSnitch.java | 64 +- .../locator/DynamicEndpointSnitchMBean.java | 2 + .../locator/Ec2MultiRegionSnitch.java | 11 + .../apache/cassandra/locator/Ec2Snitch.java | 11 +- .../cassandra/locator/EndpointSnitchInfo.java | 9 +- .../cassandra/locator/GoogleCloudSnitch.java | 11 +- .../locator/GossipingPropertyFileSnitch.java | 15 +- .../cassandra/locator/IEndpointSnitch.java | 13 +- .../cassandra/locator/ILatencySubscriber.java | 4 +- .../cassandra/locator/InetAddressAndPort.java | 203 +++++ .../cassandra/locator/LocalStrategy.java | 11 +- .../locator/NetworkTopologyStrategy.java | 23 +- .../locator/OldNetworkTopologyStrategy.java | 5 +- .../cassandra/locator/PendingRangeMaps.java | 57 +- .../cassandra/locator/PropertyFileSnitch.java | 29 +- .../locator/RackInferringSnitch.java | 10 +- .../locator/ReconnectableSnitchHelper.java | 60 +- .../cassandra/locator/SeedProvider.java | 3 +- .../cassandra/locator/SimpleSeedProvider.java | 8 +- .../cassandra/locator/SimpleSnitch.java | 9 +- .../cassandra/locator/SimpleStrategy.java | 7 +- .../cassandra/locator/TokenMetadata.java | 221 +++-- .../cassandra/metrics/ConnectionMetrics.java | 8 +- .../metrics/HintedHandoffMetrics.java | 25 +- .../metrics/HintsServiceMetrics.java | 11 +- .../cassandra/metrics/MessagingMetrics.java | 4 +- .../cassandra/metrics/StreamingMetrics.java | 11 +- .../cassandra/net/BackPressureState.java | 4 +- .../cassandra/net/BackPressureStrategy.java | 5 +- .../apache/cassandra/net/CallbackInfo.java | 7 +- .../CompactEndpointSerializationHelper.java | 108 ++- .../cassandra/net/ForwardToContainer.java | 43 + .../cassandra/net/ForwardToSerializer.java | 86 ++ .../apache/cassandra/net/IAsyncCallback.java | 7 +- .../net/IAsyncCallbackWithFailure.java | 5 +- .../apache/cassandra/net/IMessageSink.java | 4 +- .../cassandra/net/MessageDeliveryTask.java | 14 +- .../org/apache/cassandra/net/MessageIn.java | 74 +- .../org/apache/cassandra/net/MessageOut.java | 81 +- .../cassandra/net/MessagingService.java | 271 ++++-- .../cassandra/net/MessagingServiceMBean.java | 26 + .../apache/cassandra/net/ParameterType.java | 69 ++ .../cassandra/net/RateBasedBackPressure.java | 4 +- .../net/RateBasedBackPressureState.java | 8 +- .../cassandra/net/WriteCallbackInfo.java | 7 +- .../net/async/HandshakeProtocol.java | 18 +- .../net/async/InboundHandshakeHandler.java | 7 +- .../cassandra/net/async/MessageInHandler.java | 21 +- .../net/async/MessageOutHandler.java | 12 +- .../cassandra/net/async/NettyFactory.java | 15 +- .../async/OutboundConnectionIdentifier.java | 48 +- .../async/OutboundMessagingConnection.java | 13 +- .../net/async/OutboundMessagingPool.java | 15 +- .../repair/AsymmetricLocalSyncTask.java | 8 +- .../repair/AsymmetricRemoteSyncTask.java | 6 +- .../cassandra/repair/AsymmetricSyncTask.java | 8 +- .../cassandra/repair/LocalSyncTask.java | 13 +- .../org/apache/cassandra/repair/NodePair.java | 25 +- .../cassandra/repair/RemoteSyncTask.java | 4 +- .../apache/cassandra/repair/RepairJob.java | 56 +- .../repair/RepairMessageVerbHandler.java | 7 +- .../cassandra/repair/RepairRunnable.java | 31 +- .../cassandra/repair/RepairSession.java | 38 +- .../apache/cassandra/repair/SnapshotTask.java | 10 +- .../cassandra/repair/StreamingRepairTask.java | 17 +- .../repair/SystemDistributedKeyspace.java | 98 ++- .../apache/cassandra/repair/TreeResponse.java | 7 +- .../cassandra/repair/ValidationTask.java | 7 +- .../apache/cassandra/repair/Validator.java | 12 +- .../repair/asymmetric/DifferenceHolder.java | 16 +- .../repair/asymmetric/HostDifferences.java | 14 +- .../IncomingRepairStreamTracker.java | 4 +- .../repair/asymmetric/PreferedNodeFilter.java | 5 +- .../repair/asymmetric/ReduceHelper.java | 42 +- .../repair/asymmetric/StreamFromOptions.java | 16 +- .../repair/consistent/ConsistentSession.java | 21 +- .../repair/consistent/CoordinatorSession.java | 24 +- .../consistent/CoordinatorSessions.java | 4 +- .../repair/consistent/LocalSessionInfo.java | 6 +- .../repair/consistent/LocalSessions.java | 82 +- .../repair/consistent/SyncStatSummary.java | 14 +- .../messages/AsymmetricSyncRequest.java | 25 +- .../repair/messages/FinalizePromise.java | 18 +- .../messages/PrepareConsistentRequest.java | 31 +- .../messages/PrepareConsistentResponse.java | 17 +- .../repair/messages/SyncComplete.java | 4 +- .../repair/messages/SyncRequest.java | 26 +- .../cassandra/schema/MigrationManager.java | 24 +- .../cassandra/schema/MigrationTask.java | 6 +- .../service/AbstractReadExecutor.java | 42 +- .../service/AbstractWriteResponseHandler.java | 16 +- .../service/ActiveRepairService.java | 64 +- .../service/BatchlogResponseHandler.java | 6 +- .../cassandra/service/CassandraDaemon.java | 7 +- .../apache/cassandra/service/ClientState.java | 2 +- .../cassandra/service/DataResolver.java | 18 +- .../DatacenterSyncWriteResponseHandler.java | 8 +- .../DatacenterWriteResponseHandler.java | 8 +- .../service/IEndpointLifecycleSubscriber.java | 12 +- .../cassandra/service/LoadBroadcaster.java | 20 +- .../cassandra/service/ReadCallback.java | 20 +- .../cassandra/service/StartupChecks.java | 4 +- .../cassandra/service/StorageProxy.java | 318 ++++--- .../cassandra/service/StorageProxyMBean.java | 3 +- .../cassandra/service/StorageService.java | 818 ++++++++++++------ .../service/StorageServiceMBean.java | 57 +- .../apache/cassandra/service/TokenRange.java | 50 +- .../service/WriteResponseHandler.java | 12 +- .../service/paxos/PrepareCallback.java | 10 +- .../cassandra/streaming/ProgressInfo.java | 14 +- .../cassandra/streaming/SessionInfo.java | 12 +- .../cassandra/streaming/SessionSummary.java | 23 +- .../streaming/StreamCoordinator.java | 24 +- .../cassandra/streaming/StreamEvent.java | 4 +- .../cassandra/streaming/StreamManager.java | 10 +- .../cassandra/streaming/StreamPlan.java | 16 +- .../streaming/StreamResultFuture.java | 15 +- .../cassandra/streaming/StreamSession.java | 43 +- .../async/NettyStreamingMessageSender.java | 4 +- .../async/StreamingInboundHandler.java | 10 +- .../management/ProgressInfoCompositeData.java | 30 +- .../SessionCompleteEventCompositeData.java | 8 +- .../management/SessionInfoCompositeData.java | 44 +- .../streaming/messages/FileMessageHeader.java | 14 +- .../messages/OutgoingFileMessage.java | 2 +- .../streaming/messages/StreamInitMessage.java | 12 +- .../tools/BulkLoadConnectionFactory.java | 10 +- .../apache/cassandra/tools/BulkLoader.java | 27 +- .../apache/cassandra/tools/LoaderOptions.java | 77 +- .../org/apache/cassandra/tools/NodeProbe.java | 63 +- .../org/apache/cassandra/tools/NodeTool.java | 26 + .../tools/nodetool/DescribeCluster.java | 4 +- .../tools/nodetool/DescribeRing.java | 4 +- .../tools/nodetool/FailureDetectorInfo.java | 2 +- .../tools/nodetool/GetEndpoints.java | 18 +- .../cassandra/tools/nodetool/GossipInfo.java | 4 +- .../tools/nodetool/HostStatWithPort.java | 44 + .../cassandra/tools/nodetool/NetStats.java | 10 +- .../cassandra/tools/nodetool/RemoveNode.java | 6 +- .../cassandra/tools/nodetool/RepairAdmin.java | 6 +- .../apache/cassandra/tools/nodetool/Ring.java | 223 +++-- .../tools/nodetool/SetHostStatWithPort.java | 56 ++ .../cassandra/tools/nodetool/Status.java | 214 +++-- .../cassandra/tracing/ExpiredTraceState.java | 2 +- .../cassandra/tracing/TraceKeyspace.java | 48 +- .../apache/cassandra/tracing/TraceState.java | 6 +- .../cassandra/tracing/TraceStateImpl.java | 4 +- .../org/apache/cassandra/tracing/Tracing.java | 52 +- .../apache/cassandra/tracing/TracingImpl.java | 3 +- .../org/apache/cassandra/transport/Event.java | 21 +- .../cassandra/transport/ProtocolVersion.java | 9 +- .../apache/cassandra/transport/Server.java | 60 +- .../transport/messages/ErrorMessage.java | 13 +- .../apache/cassandra/utils/FBUtilities.java | 53 +- .../cassandra/utils/JMXServerUtils.java | 2 +- .../org/apache/cassandra/utils/Mx4jTool.java | 2 +- .../utils/NativeSSTableLoaderClient.java | 46 +- .../org/apache/cassandra/utils/UUIDGen.java | 2 +- test/conf/cassandra.yaml | 3 +- .../serialization/4.0/gms.EndpointState.bin | Bin 73 -> 73 bytes test/data/serialization/4.0/gms.Gossip.bin | Bin 158 -> 166 bytes .../4.0/service.SyncComplete.bin | Bin 538 -> 554 bytes .../serialization/4.0/service.SyncRequest.bin | Bin 227 -> 241 bytes .../4.0/service.ValidationComplete.bin | Bin 1251 -> 1257 bytes .../4.0/service.ValidationRequest.bin | Bin 167 -> 169 bytes .../DynamicEndpointSnitchLongTest.java | 15 +- .../streaming/LongStreamingTest.java | 4 +- .../test/microbench/PendingRangesBench.java | 10 +- .../OffsetAwareConfigurationLoader.java | 32 + test/unit/org/apache/cassandra/Util.java | 9 +- .../batchlog/BatchlogEndpointFilterTest.java | 84 +- .../batchlog/BatchlogManagerTest.java | 6 +- .../config/DatabaseDescriptorRefTest.java | 2 + .../org/apache/cassandra/cql3/CQLTester.java | 12 +- .../cql3/PreparedStatementsTest.java | 1 - .../cassandra/cql3/ViewComplexTest.java | 8 +- .../validation/operations/CreateTest.java | 8 +- .../org/apache/cassandra/db/CleanupTest.java | 22 +- .../cassandra/db/DiskBoundaryManagerTest.java | 6 +- .../apache/cassandra/db/ReadCommandTest.java | 12 +- .../org/apache/cassandra/db/RowCacheTest.java | 6 +- .../db/SystemKeyspaceMigrator40Test.java | 192 ++++ .../cassandra/db/SystemKeyspaceTest.java | 6 +- .../compaction/AbstractPendingRepairTest.java | 4 +- .../db/compaction/AntiCompactionTest.java | 4 +- ...onManagerGetSSTablesForValidationTest.java | 6 +- .../LeveledCompactionStrategyTest.java | 4 +- .../cassandra/db/view/ViewUtilsTest.java | 48 +- .../cassandra/dht/BootStrapperTest.java | 34 +- .../dht/RangeFetchMapCalculatorTest.java | 116 +-- .../cassandra/dht/StreamStateStoreTest.java | 4 +- .../cassandra/gms/ArrivalWindowTest.java | 15 +- .../cassandra/gms/EndpointStateTest.java | 8 +- .../cassandra/gms/FailureDetectorTest.java | 8 +- .../cassandra/gms/GossipDigestTest.java | 5 +- .../apache/cassandra/gms/GossiperTest.java | 6 +- .../PendingRangeCalculatorServiceTest.java | 8 +- .../cassandra/gms/SerializationsTest.java | 12 +- .../org/apache/cassandra/hints/HintTest.java | 12 +- .../cassandra/hints/HintsServiceTest.java | 14 +- .../io/sstable/CQLSSTableWriterTest.java | 2 +- .../io/sstable/LegacySSTableTest.java | 2 +- .../io/sstable/SSTableLoaderTest.java | 2 +- .../locator/CloudstackSnitchTest.java | 7 +- .../locator/DynamicEndpointSnitchTest.java | 17 +- .../cassandra/locator/EC2SnitchTest.java | 8 +- .../locator/GoogleCloudSnitchTest.java | 7 +- .../GossipingPropertyFileSnitchTest.java | 15 +- .../locator/InetAddressAndPortTest.java | 143 +++ .../locator/NetworkTopologyStrategyTest.java | 73 +- .../OldNetworkTopologyStrategyTest.java | 21 +- .../locator/PendingRangeMapsTest.java | 37 +- .../locator/PropertyFileSnitchTest.java | 106 ++- .../ReconnectableSnitchHelperTest.java | 2 +- .../ReplicationStrategyEndpointCacheTest.java | 35 +- .../cassandra/locator/SimpleStrategyTest.java | 19 +- .../cassandra/locator/TokenMetadataTest.java | 49 +- .../metrics/HintedHandOffMetricsTest.java | 10 +- ...ompactEndpointSerializationHelperTest.java | 72 ++ .../cassandra/net/ForwardToContainerTest.java | 100 +++ .../org/apache/cassandra/net/Matcher.java | 4 +- .../apache/cassandra/net/MatcherResponse.java | 11 +- .../cassandra/net/MessagingServiceTest.java | 82 +- .../cassandra/net/MockMessagingService.java | 15 +- .../net/MockMessagingServiceTest.java | 6 +- .../net/RateBasedBackPressureTest.java | 42 +- .../cassandra/net/WriteCallbackInfoTest.java | 4 +- .../net/async/ChannelWriterTest.java | 6 +- .../net/async/HandshakeHandlersTest.java | 8 +- .../net/async/HandshakeProtocolTest.java | 2 +- .../async/InboundHandshakeHandlerTest.java | 18 +- .../net/async/MessageInHandlerTest.java | 39 +- .../net/async/MessageOutHandlerTest.java | 16 +- .../cassandra/net/async/NettyFactoryTest.java | 15 +- .../async/OutboundHandshakeHandlerTest.java | 6 +- .../OutboundMessagingConnectionTest.java | 48 +- .../net/async/OutboundMessagingPoolTest.java | 11 +- .../cassandra/repair/AbstractRepairTest.java | 20 +- .../cassandra/repair/LocalSyncTaskTest.java | 12 +- .../cassandra/repair/RepairRunnableTest.java | 3 +- .../cassandra/repair/RepairSessionTest.java | 7 +- .../cassandra/repair/ValidatorTest.java | 12 +- .../asymmetric/DifferenceHolderTest.java | 10 +- .../repair/asymmetric/ReduceHelperTest.java | 74 +- .../asymmetric/StreamFromOptionsTest.java | 36 +- .../AbstractConsistentSessionTest.java | 20 +- .../consistent/CoordinatorSessionTest.java | 30 +- .../consistent/CoordinatorSessionsTest.java | 12 +- .../consistent/LocalSessionAccessor.java | 4 +- .../repair/consistent/LocalSessionTest.java | 17 +- .../consistent/PendingAntiCompactionTest.java | 6 +- .../RepairMessageSerializationsTest.java | 12 +- .../messages/RepairMessageSerializerTest.java | 14 +- .../service/ActiveRepairServiceTest.java | 58 +- .../cassandra/service/DataResolverTest.java | 84 +- .../service/LeaveAndBootstrapTest.java | 63 +- .../apache/cassandra/service/MoveTest.java | 146 ++-- .../service/ProtocolBetaVersionTest.java | 2 +- .../cassandra/service/ReadExecutorTest.java | 6 +- .../apache/cassandra/service/RemoveTest.java | 14 +- .../cassandra/service/SerializationsTest.java | 30 +- .../cassandra/service/StorageProxyTest.java | 6 +- .../service/StorageServiceServerTest.java | 170 ++-- .../service/WriteResponseHandlerTest.java | 25 +- .../cassandra/streaming/SessionInfoTest.java | 4 +- .../streaming/StreamTransferTaskTest.java | 6 +- .../streaming/StreamingTransferTest.java | 4 +- .../NettyStreamingMessageSenderTest.java | 8 +- .../async/StreamingInboundHandlerTest.java | 12 +- .../apache/cassandra/tracing/TracingTest.java | 3 +- .../cassandra/transport/ErrorMessageTest.java | 20 +- .../transport/ProtocolVersionTest.java | 9 +- .../cassandra/transport/SerDeserTest.java | 12 +- .../cassandra/utils/FBUtilitiesTest.java | 8 +- .../cassandra/stress/CompactionStress.java | 4 +- .../stress/settings/SettingsNode.java | 16 +- .../stress/util/JavaDriverClient.java | 7 +- 335 files changed, 6464 insertions(+), 3780 deletions(-) rename lib/{cassandra-driver-core-3.3.2-0461ed35-SNAPSHOT-shaded.jar => cassandra-driver-core-4.0.0-SNAPSHOT-shaded.jar} (78%) rename lib/{cassandra-driver-internal-only-3.11.zip => cassandra-driver-internal-only-3.12.0.post0-9ee88ded.zip} (50%) create mode 100644 src/java/org/apache/cassandra/db/SystemKeyspaceMigrator40.java create mode 100644 src/java/org/apache/cassandra/io/DummyByteVersionedSerializer.java create mode 100644 src/java/org/apache/cassandra/io/ShortVersionedSerializer.java create mode 100644 src/java/org/apache/cassandra/locator/InetAddressAndPort.java create mode 100644 src/java/org/apache/cassandra/net/ForwardToContainer.java create mode 100644 src/java/org/apache/cassandra/net/ForwardToSerializer.java create mode 100644 src/java/org/apache/cassandra/net/ParameterType.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java create mode 100644 src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java create mode 100644 test/unit/org/apache/cassandra/db/SystemKeyspaceMigrator40Test.java create mode 100644 test/unit/org/apache/cassandra/locator/InetAddressAndPortTest.java create mode 100644 test/unit/org/apache/cassandra/net/CompactEndpointSerializationHelperTest.java create mode 100644 test/unit/org/apache/cassandra/net/ForwardToContainerTest.java diff --git a/CHANGES.txt b/CHANGES.txt index b29ae784b2..d8ae88ff19 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.0 + * Allow storage port to be configurable per node (CASSANDRA-7544) * Make sub-range selection for non-frozen collections return null instead of empty (CASSANDRA-14182) * BloomFilter serialization format should not change byte ordering (CASSANDRA-9067) * Remove unused on-heap BloomFilter implementation (CASSANDRA-14152) diff --git a/NEWS.txt b/NEWS.txt index 06d73eace2..c314030656 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -39,7 +39,7 @@ New features This property can be modified at runtime through both JMX and the new `setconcurrentviewbuilders` and `getconcurrentviewbuilders` nodetool commands. See CASSANDRA-12245 for more details. - There is now a binary full query log based on Chronicle Queue that can be controlled using - nodetool enablefullquerylog, disablefullquerylog, and resetfullquerylog. The log + nodetool enablefullquerylog, disablefullquerylog, and resetfullquerylog. The log contains all queries invoked, approximate time they were invoked, any parameters necessary to bind wildcard values, and all query options. A human readable version of the log can be dumped or tailed using the new bin/fqltool utility. The full query log is designed to be safe @@ -103,6 +103,22 @@ Upgrading but blocks for up to a configurable number of milliseconds between disk flushes. - nodetool clearsnapshot now required the --all flag to remove all snapshots. Previous behavior would delete all snapshots by default. + - Nodes are now identified by a combination of IP, and storage port. + Existing JMX APIs, nodetool, and system tables continue to work + and accept/return just an IP, but there is a new + version of each that works with the full unambiguous identifier. + You should prefer these over the deprecated ambiguous versions that only + work with an IP. This was done to support multiple instances per IP. + Additionally we are moving to only using a single port for encrypted and + unencrypted traffic and if you want multiple instances per IP you must + first switch encrypted traffic to the storage port and not a separate + encrypted port. If you want to use multiple instances per IP + with SSL you will need to use StartTLS on storage_port and set + outgoing_encrypted_port_source to gossip outbound connections + know what port to connect to for each instance. Before changing + storage port or native port at nodes you must first upgrade the entire cluster + and clients to 4.0 so they can handle the port not being consistent across + the cluster. Materialized Views ------------------- diff --git a/bin/cqlsh.py b/bin/cqlsh.py index 61ea1605ce..3055110d1c 100644 --- a/bin/cqlsh.py +++ b/bin/cqlsh.py @@ -455,7 +455,8 @@ class Shell(cmd.Cmd): single_statement=None, request_timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS, protocol_version=None, - connect_timeout=DEFAULT_CONNECT_TIMEOUT_SECONDS): + connect_timeout=DEFAULT_CONNECT_TIMEOUT_SECONDS, + allow_server_port_discovery=False): cmd.Cmd.__init__(self, completekey=completekey) self.hostname = hostname self.port = port @@ -470,6 +471,7 @@ class Shell(cmd.Cmd): self.tracing_enabled = tracing_enabled self.page_size = self.default_page_size self.expand_enabled = expand_enabled + self.allow_server_port_discovery = allow_server_port_discovery if use_conn: self.conn = use_conn else: @@ -482,6 +484,7 @@ class Shell(cmd.Cmd): load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]), control_connection_timeout=connect_timeout, connect_timeout=connect_timeout, + allow_server_port_discovery=allow_server_port_discovery, **kwargs) self.owns_connection = not use_conn @@ -1768,7 +1771,8 @@ class Shell(cmd.Cmd): display_timezone=self.display_timezone, max_trace_wait=self.max_trace_wait, ssl=self.ssl, request_timeout=self.session.default_timeout, - connect_timeout=self.conn.connect_timeout) + connect_timeout=self.conn.connect_timeout, + allow_server_port_discovery=self.allow_server_port_discovery) subshell.cmdloop() f.close() @@ -2252,6 +2256,7 @@ def read_options(cmdlineargs, environment): optvalues.connect_timeout = option_with_default(configs.getint, 'connection', 'timeout', DEFAULT_CONNECT_TIMEOUT_SECONDS) optvalues.request_timeout = option_with_default(configs.getint, 'connection', 'request_timeout', DEFAULT_REQUEST_TIMEOUT_SECONDS) optvalues.execute = None + optvalues.allow_server_port_discovery = option_with_default(configs.getboolean, 'connection', 'allow_server_port_discovery', 'False') (options, arguments) = parser.parse_args(cmdlineargs, values=optvalues) @@ -2415,7 +2420,8 @@ def main(options, hostname, port): single_statement=options.execute, request_timeout=options.request_timeout, connect_timeout=options.connect_timeout, - encoding=options.encoding) + encoding=options.encoding, + allow_server_port_discovery=options.allow_server_port_discovery) except KeyboardInterrupt: sys.exit('Connection aborted.') except CQL_ERRORS, e: diff --git a/build.xml b/build.xml index 8a033e0e54..579686888a 100644 --- a/build.xml +++ b/build.xml @@ -306,7 +306,7 @@ - + @@ -437,7 +437,7 @@ @@ -543,7 +543,7 @@ + --> @@ -624,7 +624,7 @@ --> - + diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index 80e4515431..3bed3a6bd1 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -416,7 +416,7 @@ seed_provider: parameters: # seeds is actually a comma-delimited list of addresses. # Ex: ",," - - seeds: "127.0.0.1" + - seeds: "127.0.0.1:7000" # For workloads with more data than can fit in memory, Cassandra's # bottleneck will be reads that need to fetch data from @@ -945,6 +945,13 @@ dynamic_snitch_badness_threshold: 0.1 # the keystore and truststore. For instructions on generating these files, see: # http://download.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore # +# If you are taking advantage of StartTLS outbound connections will have the issue that they can't know +# what encrypted port to connect to in a foolproof way. outgoing_encrypted_port_source deals with this confusion +# by allowing you to specify how you want a node to pick an outgoing port for intra-cluster connections. +# Valid values are "gossip" and "yaml". Gossip will always connect to the storage port for a node that is +# published via a gossip which is always going to be the plain storage port. "yaml" will always select +# the port configured as ssl_storage_port on THIS node. If you want to use SSL and have different storage +# ports across the cluster you must select "gossip" and use StartTLS on storage_port. server_encryption_options: # set to true for allowing secure incoming connections enabled: false diff --git a/lib/cassandra-driver-core-3.3.2-0461ed35-SNAPSHOT-shaded.jar b/lib/cassandra-driver-core-4.0.0-SNAPSHOT-shaded.jar similarity index 78% rename from lib/cassandra-driver-core-3.3.2-0461ed35-SNAPSHOT-shaded.jar rename to lib/cassandra-driver-core-4.0.0-SNAPSHOT-shaded.jar index a7be9cbe9e247726441e0973cd98c9ccaaa79d63..609c393f353e6e1224139ee279f8e7ee32ec22fb 100644 GIT binary patch delta 279675 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࿑q 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 MICN-N*%>td55!Q6y#N3J delta 277359 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

qv2ucR*F)nwlar?gX6OA^0P$BJ+$d1o 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%0AE^8rPC%az}-xa!*{rgqfqrHuOQm{ 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�+LSxfY>K4#>J;mTN@;hp3)_9p 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 X8W|Z?Cpz}p_1f*gHO#aT1vLK$;R4a~ diff --git a/lib/cassandra-driver-internal-only-3.11.zip b/lib/cassandra-driver-internal-only-3.12.0.post0-9ee88ded.zip similarity index 50% rename from lib/cassandra-driver-internal-only-3.11.zip rename to lib/cassandra-driver-internal-only-3.12.0.post0-9ee88ded.zip index f7760af4058b37564d9b08de2b6ac0f59326ec23..4aa91b7046043c112d9a3139b6a218417e780c26 100644 GIT binary patch delta 116133 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 delta 115489 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%0AP6G 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 Qz%KzK1d_A}I627w0UP$W_5c6? diff --git a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java index 807e970fa3..f232bdc653 100644 --- a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java +++ b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java @@ -19,7 +19,6 @@ package org.apache.cassandra.batchlog; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.*; @@ -34,6 +33,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.concurrent.DebuggableScheduledThreadPoolExecutor; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.*; @@ -250,7 +250,7 @@ public class BatchlogManager implements BatchlogManagerMBean int positionInPage = 0; ArrayList unfinishedBatches = new ArrayList<>(pageSize); - Set hintedNodes = new HashSet<>(); + Set hintedNodes = new HashSet<>(); Set replayedBatches = new HashSet<>(); // Sending out batches for replay without waiting for them, so that one stuck batch doesn't affect others @@ -295,7 +295,7 @@ public class BatchlogManager implements BatchlogManagerMBean replayedBatches.forEach(BatchlogManager::remove); } - private void finishAndClearBatches(ArrayList batches, Set hintedNodes, Set replayedBatches) + private void finishAndClearBatches(ArrayList batches, Set hintedNodes, Set replayedBatches) { // schedule hints for timed out deliveries for (ReplayingBatch batch : batches) @@ -330,7 +330,7 @@ public class BatchlogManager implements BatchlogManagerMBean this.replayedBytes = addMutations(version, serializedMutations); } - public int replay(RateLimiter rateLimiter, Set hintedNodes) throws IOException + public int replay(RateLimiter rateLimiter, Set hintedNodes) throws IOException { logger.trace("Replaying batch {}", id); @@ -348,7 +348,7 @@ public class BatchlogManager implements BatchlogManagerMBean return replayHandlers.size(); } - public void finish(Set hintedNodes) + public void finish(Set hintedNodes) { for (int i = 0; i < replayHandlers.size(); i++) { @@ -396,7 +396,7 @@ public class BatchlogManager implements BatchlogManagerMBean mutations.add(mutation); } - private void writeHintsForUndeliveredEndpoints(int startFrom, Set hintedNodes) + private void writeHintsForUndeliveredEndpoints(int startFrom, Set hintedNodes) { int gcgs = gcgs(mutations); @@ -420,7 +420,7 @@ public class BatchlogManager implements BatchlogManagerMBean private static List> sendReplays(List mutations, long writtenAt, - Set hintedNodes) + Set hintedNodes) { List> handlers = new ArrayList<>(mutations.size()); for (Mutation mutation : mutations) @@ -440,15 +440,15 @@ public class BatchlogManager implements BatchlogManagerMBean */ private static ReplayWriteResponseHandler sendSingleReplayMutation(final Mutation mutation, long writtenAt, - Set hintedNodes) + Set hintedNodes) { - Set liveEndpoints = new HashSet<>(); + Set liveEndpoints = new HashSet<>(); String ks = mutation.getKeyspaceName(); Token tk = mutation.key().getToken(); - for (InetAddress endpoint : StorageService.instance.getNaturalAndPendingEndpoints(ks, tk)) + for (InetAddressAndPort endpoint : StorageService.instance.getNaturalAndPendingEndpoints(ks, tk)) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) { mutation.apply(); } @@ -469,7 +469,7 @@ public class BatchlogManager implements BatchlogManagerMBean ReplayWriteResponseHandler handler = new ReplayWriteResponseHandler<>(liveEndpoints, System.nanoTime()); MessageOut message = mutation.createMessage(); - for (InetAddress endpoint : liveEndpoints) + for (InetAddressAndPort endpoint : liveEndpoints) MessagingService.instance().sendRR(message, endpoint, handler, false); return handler; } @@ -488,11 +488,11 @@ public class BatchlogManager implements BatchlogManagerMBean */ private static class ReplayWriteResponseHandler extends WriteResponseHandler { - private final Set undelivered = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set undelivered = Collections.newSetFromMap(new ConcurrentHashMap<>()); - ReplayWriteResponseHandler(Collection writeEndpoints, long queryStartNanoTime) + ReplayWriteResponseHandler(Collection writeEndpoints, long queryStartNanoTime) { - super(writeEndpoints, Collections.emptySet(), null, null, null, WriteType.UNLOGGED_BATCH, queryStartNanoTime); + super(writeEndpoints, Collections.emptySet(), null, null, null, WriteType.UNLOGGED_BATCH, queryStartNanoTime); undelivered.addAll(writeEndpoints); } @@ -505,7 +505,7 @@ public class BatchlogManager implements BatchlogManagerMBean @Override public void response(MessageIn m) { - boolean removed = undelivered.remove(m == null ? FBUtilities.getBroadcastAddress() : m.from); + boolean removed = undelivered.remove(m == null ? FBUtilities.getBroadcastAddressAndPort() : m.from); assert removed; super.response(m); } @@ -515,9 +515,9 @@ public class BatchlogManager implements BatchlogManagerMBean public static class EndpointFilter { private final String localRack; - private final Multimap endpoints; + private final Multimap endpoints; - public EndpointFilter(String localRack, Multimap endpoints) + public EndpointFilter(String localRack, Multimap endpoints) { this.localRack = localRack; this.endpoints = endpoints; @@ -526,15 +526,15 @@ public class BatchlogManager implements BatchlogManagerMBean /** * @return list of candidates for batchlog hosting. If possible these will be two nodes from different racks. */ - public Collection filter() + public Collection filter() { // special case for single-node data centers if (endpoints.values().size() == 1) return endpoints.values(); // strip out dead endpoints and localhost - ListMultimap validated = ArrayListMultimap.create(); - for (Map.Entry entry : endpoints.entries()) + ListMultimap validated = ArrayListMultimap.create(); + for (Map.Entry entry : endpoints.entries()) if (isValid(entry.getValue())) validated.put(entry.getKey(), entry.getValue()); @@ -554,7 +554,7 @@ public class BatchlogManager implements BatchlogManagerMBean * pick two random nodes from there; we are guaranteed to have at least two nodes in the single remaining rack * because of the preceding if block. */ - List otherRack = Lists.newArrayList(validated.values()); + List otherRack = Lists.newArrayList(validated.values()); shuffle(otherRack); return otherRack.subList(0, 2); } @@ -572,10 +572,10 @@ public class BatchlogManager implements BatchlogManagerMBean } // grab a random member of up to two racks - List result = new ArrayList<>(2); + List result = new ArrayList<>(2); for (String rack : Iterables.limit(racks, 2)) { - List rackMembers = validated.get(rack); + List rackMembers = validated.get(rack); result.add(rackMembers.get(getRandomInt(rackMembers.size()))); } @@ -583,9 +583,9 @@ public class BatchlogManager implements BatchlogManagerMBean } @VisibleForTesting - protected boolean isValid(InetAddress input) + protected boolean isValid(InetAddressAndPort input) { - return !input.equals(FBUtilities.getBroadcastAddress()) && FailureDetector.instance.isAlive(input); + return !input.equals(FBUtilities.getBroadcastAddressAndPort()) && FailureDetector.instance.isAlive(input); } @VisibleForTesting diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index a656d1f1ba..9012e3ac88 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -55,6 +55,7 @@ import org.apache.cassandra.io.util.SsdDiskOptimizationStrategy; import org.apache.cassandra.locator.DynamicEndpointSnitch; import org.apache.cassandra.locator.EndpointSnitchInfo; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.SeedProvider; import org.apache.cassandra.net.BackPressureStrategy; import org.apache.cassandra.net.RateBasedBackPressure; @@ -110,7 +111,7 @@ public class DatabaseDescriptor private static long indexSummaryCapacityInMB; private static String localDC; - private static Comparator localComparator; + private static Comparator localComparator; private static EncryptionContext encryptionContext; private static boolean hasLoggedConfig; @@ -307,6 +308,7 @@ public class DatabaseDescriptor private static void applyAll() throws ConfigurationException { + //InetAddressAndPort cares that applySimpleConfig runs first applySimpleConfig(); applyPartitioner(); @@ -324,6 +326,9 @@ public class DatabaseDescriptor private static void applySimpleConfig() { + //Doing this first before all other things in case other pieces of config want to construct + //InetAddressAndPort and get the right defaults + InetAddressAndPort.initializeDefaultPort(getStoragePort()); if (conf.commitlog_sync == null) { @@ -827,7 +832,7 @@ public class DatabaseDescriptor } else { - rpcAddress = FBUtilities.getLocalAddress(); + rpcAddress = FBUtilities.getJustLocalAddress(); } /* RPC address to broadcast */ @@ -956,10 +961,10 @@ public class DatabaseDescriptor snitch = createEndpointSnitch(conf.dynamic_snitch, conf.endpoint_snitch); EndpointSnitchInfo.create(); - localDC = snitch.getDatacenter(FBUtilities.getBroadcastAddress()); - localComparator = new Comparator() + localDC = snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort()); + localComparator = new Comparator() { - public int compare(InetAddress endpoint1, InetAddress endpoint2) + public int compare(InetAddressAndPort endpoint1, InetAddressAndPort endpoint2) { boolean local1 = localDC.equals(snitch.getDatacenter(endpoint1)); boolean local2 = localDC.equals(snitch.getDatacenter(endpoint2)); @@ -1319,14 +1324,14 @@ public class DatabaseDescriptor return conf.num_tokens; } - public static InetAddress getReplaceAddress() + public static InetAddressAndPort getReplaceAddress() { try { if (System.getProperty(Config.PROPERTY_PREFIX + "replace_address", null) != null) - return InetAddress.getByName(System.getProperty(Config.PROPERTY_PREFIX + "replace_address", null)); + return InetAddressAndPort.getByName(System.getProperty(Config.PROPERTY_PREFIX + "replace_address", null)); else if (System.getProperty(Config.PROPERTY_PREFIX + "replace_address_first_boot", null) != null) - return InetAddress.getByName(System.getProperty(Config.PROPERTY_PREFIX + "replace_address_first_boot", null)); + return InetAddressAndPort.getByName(System.getProperty(Config.PROPERTY_PREFIX + "replace_address_first_boot", null)); return null; } catch (UnknownHostException e) @@ -1651,9 +1656,9 @@ public class DatabaseDescriptor return conf.saved_caches_directory; } - public static Set getSeeds() + public static Set getSeeds() { - return ImmutableSet.builder().addAll(seedProvider.getSeeds()).build(); + return ImmutableSet.builder().addAll(seedProvider.getSeeds()).build(); } public static InetAddress getListenAddress() @@ -2206,7 +2211,7 @@ public class DatabaseDescriptor return localDC; } - public static Comparator getLocalComparator() + public static Comparator getLocalComparator() { return localComparator; } diff --git a/src/java/org/apache/cassandra/db/ConsistencyLevel.java b/src/java/org/apache/cassandra/db/ConsistencyLevel.java index 2214d8d64a..f93e737ebd 100644 --- a/src/java/org/apache/cassandra/db/ConsistencyLevel.java +++ b/src/java/org/apache/cassandra/db/ConsistencyLevel.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.db; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -28,6 +27,7 @@ import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.service.ReadRepairDecision; @@ -145,21 +145,21 @@ public enum ConsistencyLevel return isDCLocal; } - public boolean isLocal(InetAddress endpoint) + public boolean isLocal(InetAddressAndPort endpoint) { return DatabaseDescriptor.getLocalDataCenter().equals(DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint)); } - public int countLocalEndpoints(Iterable liveEndpoints) + public int countLocalEndpoints(Iterable liveEndpoints) { int count = 0; - for (InetAddress endpoint : liveEndpoints) + for (InetAddressAndPort endpoint : liveEndpoints) if (isLocal(endpoint)) count++; return count; } - private Map countPerDCEndpoints(Keyspace keyspace, Iterable liveEndpoints) + private Map countPerDCEndpoints(Keyspace keyspace, Iterable liveEndpoints) { NetworkTopologyStrategy strategy = (NetworkTopologyStrategy) keyspace.getReplicationStrategy(); @@ -167,7 +167,7 @@ public enum ConsistencyLevel for (String dc: strategy.getDatacenters()) dcEndpoints.put(dc, 0); - for (InetAddress endpoint : liveEndpoints) + for (InetAddressAndPort endpoint : liveEndpoints) { String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint); dcEndpoints.put(dc, dcEndpoints.get(dc) + 1); @@ -175,12 +175,12 @@ public enum ConsistencyLevel return dcEndpoints; } - public List filterForQuery(Keyspace keyspace, List liveEndpoints) + public List filterForQuery(Keyspace keyspace, List liveEndpoints) { return filterForQuery(keyspace, liveEndpoints, ReadRepairDecision.NONE); } - public List filterForQuery(Keyspace keyspace, List liveEndpoints, ReadRepairDecision readRepair) + public List filterForQuery(Keyspace keyspace, List liveEndpoints, ReadRepairDecision readRepair) { /* * If we are doing an each quorum query, we have to make sure that the endpoints we select @@ -206,9 +206,9 @@ public enum ConsistencyLevel case GLOBAL: return liveEndpoints; case DC_LOCAL: - List local = new ArrayList(); - List other = new ArrayList(); - for (InetAddress add : liveEndpoints) + List local = new ArrayList<>(); + List other = new ArrayList<>(); + for (InetAddressAndPort add : liveEndpoints) { if (isLocal(add)) local.add(add); @@ -225,7 +225,7 @@ public enum ConsistencyLevel } } - private List filterForEachQuorum(Keyspace keyspace, List liveEndpoints, ReadRepairDecision readRepair) + private List filterForEachQuorum(Keyspace keyspace, List liveEndpoints, ReadRepairDecision readRepair) { NetworkTopologyStrategy strategy = (NetworkTopologyStrategy) keyspace.getReplicationStrategy(); @@ -233,20 +233,20 @@ public enum ConsistencyLevel if (readRepair == ReadRepairDecision.GLOBAL) return liveEndpoints; - Map> dcsEndpoints = new HashMap<>(); + Map> dcsEndpoints = new HashMap<>(); for (String dc: strategy.getDatacenters()) dcsEndpoints.put(dc, new ArrayList<>()); - for (InetAddress add : liveEndpoints) + for (InetAddressAndPort add : liveEndpoints) { String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(add); dcsEndpoints.get(dc).add(add); } - List waitSet = new ArrayList<>(); - for (Map.Entry> dcEndpoints : dcsEndpoints.entrySet()) + List waitSet = new ArrayList<>(); + for (Map.Entry> dcEndpoints : dcsEndpoints.entrySet()) { - List dcEndpoint = dcEndpoints.getValue(); + List dcEndpoint = dcEndpoints.getValue(); if (readRepair == ReadRepairDecision.DC_LOCAL && dcEndpoints.getKey().equals(DatabaseDescriptor.getLocalDataCenter())) waitSet.addAll(dcEndpoint); else @@ -256,7 +256,7 @@ public enum ConsistencyLevel return waitSet; } - public boolean isSufficientLiveNodes(Keyspace keyspace, Iterable liveEndpoints) + public boolean isSufficientLiveNodes(Keyspace keyspace, Iterable liveEndpoints) { switch (this) { @@ -283,7 +283,7 @@ public enum ConsistencyLevel } } - public void assureSufficientLiveNodes(Keyspace keyspace, Iterable liveEndpoints) throws UnavailableException + public void assureSufficientLiveNodes(Keyspace keyspace, Iterable liveEndpoints) throws UnavailableException { int blockFor = blockFor(keyspace); switch (this) @@ -302,7 +302,7 @@ public enum ConsistencyLevel if (logger.isTraceEnabled()) { StringBuilder builder = new StringBuilder("Local replicas ["); - for (InetAddress endpoint : liveEndpoints) + for (InetAddressAndPort endpoint : liveEndpoints) { if (isLocal(endpoint)) builder.append(endpoint).append(","); diff --git a/src/java/org/apache/cassandra/db/CounterMutationVerbHandler.java b/src/java/org/apache/cassandra/db/CounterMutationVerbHandler.java index bd273e4255..95d791618a 100644 --- a/src/java/org/apache/cassandra/db/CounterMutationVerbHandler.java +++ b/src/java/org/apache/cassandra/db/CounterMutationVerbHandler.java @@ -37,7 +37,7 @@ public class CounterMutationVerbHandler implements IVerbHandler final CounterMutation cm = message.payload; logger.trace("Applying forwarded {}", cm); - String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); // We should not wait for the result of the write in this thread, // otherwise we could have a distributed deadlock between replicas // running this VerbHandler (see #4578). diff --git a/src/java/org/apache/cassandra/db/DiskBoundaryManager.java b/src/java/org/apache/cassandra/db/DiskBoundaryManager.java index 03cbf7bb90..72b5e2ae89 100644 --- a/src/java/org/apache/cassandra/db/DiskBoundaryManager.java +++ b/src/java/org/apache/cassandra/db/DiskBoundaryManager.java @@ -80,14 +80,14 @@ public class DiskBoundaryManager && !StorageService.isReplacingSameAddress()) // When replacing same address, the node marks itself as UN locally { PendingRangeCalculatorService.instance.blockUntilFinished(); - localRanges = tmd.getPendingRanges(cfs.keyspace.getName(), FBUtilities.getBroadcastAddress()); + localRanges = tmd.getPendingRanges(cfs.keyspace.getName(), FBUtilities.getBroadcastAddressAndPort()); } else { // Reason we use use the future settled TMD is that if we decommission a node, we want to stream // from that node to the correct location on disk, if we didn't, we would put new files in the wrong places. // We do this to minimize the amount of data we need to move in rebalancedisks once everything settled - localRanges = cfs.keyspace.getReplicationStrategy().getAddressRanges(tmd.cloneAfterAllSettled()).get(FBUtilities.getBroadcastAddress()); + localRanges = cfs.keyspace.getReplicationStrategy().getAddressRanges(tmd.cloneAfterAllSettled()).get(FBUtilities.getBroadcastAddressAndPort()); } logger.debug("Got local ranges {} (ringVersion = {})", localRanges, ringVersion); } diff --git a/src/java/org/apache/cassandra/db/Keyspace.java b/src/java/org/apache/cassandra/db/Keyspace.java index cebf6ebce6..ae778f1b5c 100644 --- a/src/java/org/apache/cassandra/db/Keyspace.java +++ b/src/java/org/apache/cassandra/db/Keyspace.java @@ -352,6 +352,7 @@ public class Keyspace private void createReplicationStrategy(KeyspaceMetadata ksm) { + logger.info("Creating replication strategy " + ksm .name + " params " + ksm.params); replicationStrategy = AbstractReplicationStrategy.createReplicationStrategy(ksm.name, ksm.params.replication.klass, StorageService.instance.getTokenMetadata(), diff --git a/src/java/org/apache/cassandra/db/Mutation.java b/src/java/org/apache/cassandra/db/Mutation.java index a6a920c213..6195fe4c87 100644 --- a/src/java/org/apache/cassandra/db/Mutation.java +++ b/src/java/org/apache/cassandra/db/Mutation.java @@ -43,9 +43,6 @@ public class Mutation implements IMutation { public static final MutationSerializer serializer = new MutationSerializer(); - public static final String FORWARD_TO = "FWD_TO"; - public static final String FORWARD_FROM = "FWD_FRM"; - // todo this is redundant // when we remove it, also restore SerializationsTest.testMutationRead to not regenerate new Mutations each test private final String keyspaceName; diff --git a/src/java/org/apache/cassandra/db/MutationVerbHandler.java b/src/java/org/apache/cassandra/db/MutationVerbHandler.java index 59247a25d2..8386048f60 100644 --- a/src/java/org/apache/cassandra/db/MutationVerbHandler.java +++ b/src/java/org/apache/cassandra/db/MutationVerbHandler.java @@ -17,18 +17,17 @@ */ package org.apache.cassandra.db; -import java.io.DataInputStream; import java.io.IOException; -import java.net.InetAddress; +import java.util.Iterator; import org.apache.cassandra.exceptions.WriteTimeoutException; -import org.apache.cassandra.io.util.FastByteArrayInputStream; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.*; import org.apache.cassandra.tracing.Tracing; public class MutationVerbHandler implements IVerbHandler { - private void reply(int id, InetAddress replyTo) + private void reply(int id, InetAddressAndPort replyTo) { Tracing.trace("Enqueuing response to {}", replyTo); MessagingService.instance().sendReply(WriteResponse.createMessage(), id, replyTo); @@ -42,18 +41,19 @@ public class MutationVerbHandler implements IVerbHandler public void doVerb(MessageIn message, int id) throws IOException { // Check if there were any forwarding headers in this message - byte[] from = message.parameters.get(Mutation.FORWARD_FROM); - InetAddress replyTo; + InetAddressAndPort from = (InetAddressAndPort)message.parameters.get(ParameterType.FORWARD_FROM); + InetAddressAndPort replyTo; if (from == null) { replyTo = message.from; - byte[] forwardBytes = message.parameters.get(Mutation.FORWARD_TO); - if (forwardBytes != null) - forwardToLocalNodes(message.payload, message.verb, forwardBytes, message.from); + ForwardToContainer forwardTo = (ForwardToContainer)message.parameters.get(ParameterType.FORWARD_TO); + if (forwardTo != null) + forwardToLocalNodes(message.payload, message.verb, forwardTo, message.from); } else { - replyTo = InetAddress.getByAddress(from); + + replyTo = from; } try @@ -69,22 +69,17 @@ public class MutationVerbHandler implements IVerbHandler } } - private static void forwardToLocalNodes(Mutation mutation, MessagingService.Verb verb, byte[] forwardBytes, InetAddress from) throws IOException + private static void forwardToLocalNodes(Mutation mutation, MessagingService.Verb verb, ForwardToContainer forwardTo, InetAddressAndPort from) throws IOException { - try (DataInputStream in = new DataInputStream(new FastByteArrayInputStream(forwardBytes))) + // tell the recipients who to send their ack to + MessageOut message = new MessageOut<>(verb, mutation, Mutation.serializer).withParameter(ParameterType.FORWARD_FROM, from); + Iterator iterator = forwardTo.targets.iterator(); + // Send a message to each of the addresses on our Forward List + for (int i = 0; i < forwardTo.targets.size(); i++) { - int size = in.readInt(); - - // tell the recipients who to send their ack to - MessageOut message = new MessageOut<>(verb, mutation, Mutation.serializer).withParameter(Mutation.FORWARD_FROM, from.getAddress()); - // Send a message to each of the addresses on our Forward List - for (int i = 0; i < size; i++) - { - InetAddress address = CompactEndpointSerializationHelper.deserialize(in); - int id = in.readInt(); - Tracing.trace("Enqueuing forwarded write to {}", address); - MessagingService.instance().sendOneWay(message, id, address); - } + InetAddressAndPort address = iterator.next(); + Tracing.trace("Enqueuing forwarded write to {}", address); + MessagingService.instance().sendOneWay(message, forwardTo.messageIds[i], address); } } } diff --git a/src/java/org/apache/cassandra/db/SizeEstimatesRecorder.java b/src/java/org/apache/cassandra/db/SizeEstimatesRecorder.java index 066b2fe178..151e7d3202 100644 --- a/src/java/org/apache/cassandra/db/SizeEstimatesRecorder.java +++ b/src/java/org/apache/cassandra/db/SizeEstimatesRecorder.java @@ -60,7 +60,7 @@ public class SizeEstimatesRecorder extends SchemaChangeListener implements Runna public void run() { TokenMetadata metadata = StorageService.instance.getTokenMetadata().cloneOnlyTokenMap(); - if (!metadata.isMember(FBUtilities.getBroadcastAddress())) + if (!metadata.isMember(FBUtilities.getBroadcastAddressAndPort())) { logger.debug("Node is not part of the ring; not recording size estimates"); return; @@ -71,7 +71,7 @@ public class SizeEstimatesRecorder extends SchemaChangeListener implements Runna for (Keyspace keyspace : Keyspace.nonLocalStrategy()) { Collection> localRanges = StorageService.instance.getPrimaryRangesForEndpoint(keyspace.getName(), - FBUtilities.getBroadcastAddress()); + FBUtilities.getBroadcastAddressAndPort()); for (ColumnFamilyStore table : keyspace.getColumnFamilyStores()) { long start = System.nanoTime(); diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java index 4469384cec..91a1bffc97 100644 --- a/src/java/org/apache/cassandra/db/SystemKeyspace.java +++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java @@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.google.common.io.ByteStreams; +import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,6 +53,7 @@ import org.apache.cassandra.dht.*; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.util.*; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.RestorableMeter; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.*; @@ -91,47 +93,52 @@ public final class SystemKeyspace public static final String PAXOS = "paxos"; public static final String BUILT_INDEXES = "IndexInfo"; public static final String LOCAL = "local"; - public static final String PEERS = "peers"; - public static final String PEER_EVENTS = "peer_events"; + public static final String PEERS_V2 = "peers_v2"; + public static final String PEER_EVENTS_V2 = "peer_events_v2"; public static final String RANGE_XFERS = "range_xfers"; public static final String COMPACTION_HISTORY = "compaction_history"; public static final String SSTABLE_ACTIVITY = "sstable_activity"; public static final String SIZE_ESTIMATES = "size_estimates"; public static final String AVAILABLE_RANGES = "available_ranges"; public static final String TRANSFERRED_RANGES = "transferred_ranges"; + public static final String TRANSFERRED_RANGES_V2 = "transferred_ranges_v2"; public static final String VIEW_BUILDS_IN_PROGRESS = "view_builds_in_progress"; public static final String BUILT_VIEWS = "built_views"; public static final String PREPARED_STATEMENTS = "prepared_statements"; public static final String REPAIRS = "repairs"; + @Deprecated public static final String LEGACY_PEERS = "peers"; + @Deprecated public static final String LEGACY_PEER_EVENTS = "peer_events"; + @Deprecated public static final String LEGACY_TRANSFERRED_RANGES = "transferred_ranges"; + public static final TableMetadata Batches = parse(BATCHES, - "batches awaiting replay", - "CREATE TABLE %s (" - + "id timeuuid," - + "mutations list," - + "version int," - + "PRIMARY KEY ((id)))") - .partitioner(new LocalPartitioner(TimeUUIDType.instance)) - .compaction(CompactionParams.scts(singletonMap("min_threshold", "2"))) - .build(); + "batches awaiting replay", + "CREATE TABLE %s (" + + "id timeuuid," + + "mutations list," + + "version int," + + "PRIMARY KEY ((id)))") + .partitioner(new LocalPartitioner(TimeUUIDType.instance)) + .compaction(CompactionParams.scts(singletonMap("min_threshold", "2"))) + .build(); private static final TableMetadata Paxos = parse(PAXOS, - "in-progress paxos proposals", - "CREATE TABLE %s (" - + "row_key blob," - + "cf_id UUID," - + "in_progress_ballot timeuuid," - + "most_recent_commit blob," - + "most_recent_commit_at timeuuid," - + "most_recent_commit_version int," - + "proposal blob," - + "proposal_ballot timeuuid," - + "proposal_version int," - + "PRIMARY KEY ((row_key), cf_id))") - .compaction(CompactionParams.lcs(emptyMap())) - .build(); + "in-progress paxos proposals", + "CREATE TABLE %s (" + + "row_key blob," + + "cf_id UUID," + + "in_progress_ballot timeuuid," + + "most_recent_commit blob," + + "most_recent_commit_at timeuuid," + + "most_recent_commit_version int," + + "proposal blob," + + "proposal_ballot timeuuid," + + "proposal_version int," + + "PRIMARY KEY ((row_key), cf_id))") + .compaction(CompactionParams.lcs(emptyMap())) + .build(); private static final TableMetadata BuiltIndexes = parse(BUILT_INDEXES, @@ -145,122 +152,130 @@ public final class SystemKeyspace private static final TableMetadata Local = parse(LOCAL, - "information about the local node", - "CREATE TABLE %s (" - + "key text," - + "bootstrapped text," - + "broadcast_address inet," - + "cluster_name text," - + "cql_version text," - + "data_center text," - + "gossip_generation int," - + "host_id uuid," - + "listen_address inet," - + "native_protocol_version text," - + "partitioner text," - + "rack text," - + "release_version text," - + "rpc_address inet," - + "schema_version uuid," - + "tokens set," - + "truncated_at map," - + "PRIMARY KEY ((key)))") - .recordDeprecatedSystemColumn("thrift_version", UTF8Type.instance) - .build(); + "information about the local node", + "CREATE TABLE %s (" + + "key text," + + "bootstrapped text," + + "broadcast_address inet," + + "broadcast_port int," + + "cluster_name text," + + "cql_version text," + + "data_center text," + + "gossip_generation int," + + "host_id uuid," + + "listen_address inet," + + "listen_port int," + + "native_protocol_version text," + + "partitioner text," + + "rack text," + + "release_version text," + + "rpc_address inet," + + "rpc_port int," + + "schema_version uuid," + + "tokens set," + + "truncated_at map," + + "PRIMARY KEY ((key)))" + ).recordDeprecatedSystemColumn("thrift_version", UTF8Type.instance) + .build(); - private static final TableMetadata Peers = - parse(PEERS, - "information about known peers in the cluster", - "CREATE TABLE %s (" - + "peer inet," - + "data_center text," - + "host_id uuid," - + "preferred_ip inet," - + "rack text," - + "release_version text," - + "rpc_address inet," - + "schema_version uuid," - + "tokens set," - + "PRIMARY KEY ((peer)))") - .build(); + private static final TableMetadata PeersV2 = + parse(PEERS_V2, + "information about known peers in the cluster", + "CREATE TABLE %s (" + + "peer inet," + + "peer_port int," + + "data_center text," + + "host_id uuid," + + "preferred_ip inet," + + "preferred_port int," + + "rack text," + + "release_version text," + + "native_address inet," + + "native_port int," + + "schema_version uuid," + + "tokens set," + + "PRIMARY KEY ((peer), peer_port))") + .build(); - private static final TableMetadata PeerEvents = - parse(PEER_EVENTS, - "events related to peers", - "CREATE TABLE %s (" - + "peer inet," - + "hints_dropped map," - + "PRIMARY KEY ((peer)))") - .build(); + private static final TableMetadata PeerEventsV2 = + parse(PEER_EVENTS_V2, + "events related to peers", + "CREATE TABLE %s (" + + "peer inet," + + "peer_port int," + + "hints_dropped map," + + "PRIMARY KEY ((peer), peer_port))") + .build(); private static final TableMetadata RangeXfers = parse(RANGE_XFERS, - "ranges requested for transfer", - "CREATE TABLE %s (" - + "token_bytes blob," - + "requested_at timestamp," - + "PRIMARY KEY ((token_bytes)))") - .build(); + "ranges requested for transfer", + "CREATE TABLE %s (" + + "token_bytes blob," + + "requested_at timestamp," + + "PRIMARY KEY ((token_bytes)))") + .build(); private static final TableMetadata CompactionHistory = parse(COMPACTION_HISTORY, - "week-long compaction history", - "CREATE TABLE %s (" - + "id uuid," - + "bytes_in bigint," - + "bytes_out bigint," - + "columnfamily_name text," - + "compacted_at timestamp," - + "keyspace_name text," - + "rows_merged map," - + "PRIMARY KEY ((id)))") - .defaultTimeToLive((int) TimeUnit.DAYS.toSeconds(7)) - .build(); + "week-long compaction history", + "CREATE TABLE %s (" + + "id uuid," + + "bytes_in bigint," + + "bytes_out bigint," + + "columnfamily_name text," + + "compacted_at timestamp," + + "keyspace_name text," + + "rows_merged map," + + "PRIMARY KEY ((id)))") + .defaultTimeToLive((int) TimeUnit.DAYS.toSeconds(7)) + .build(); private static final TableMetadata SSTableActivity = parse(SSTABLE_ACTIVITY, - "historic sstable read rates", - "CREATE TABLE %s (" - + "keyspace_name text," - + "columnfamily_name text," - + "generation int," - + "rate_120m double," - + "rate_15m double," - + "PRIMARY KEY ((keyspace_name, columnfamily_name, generation)))") - .build(); + "historic sstable read rates", + "CREATE TABLE %s (" + + "keyspace_name text," + + "columnfamily_name text," + + "generation int," + + "rate_120m double," + + "rate_15m double," + + "PRIMARY KEY ((keyspace_name, columnfamily_name, generation)))") + .build(); private static final TableMetadata SizeEstimates = parse(SIZE_ESTIMATES, - "per-table primary range size estimates", - "CREATE TABLE %s (" - + "keyspace_name text," - + "table_name text," - + "range_start text," - + "range_end text," - + "mean_partition_size bigint," - + "partitions_count bigint," - + "PRIMARY KEY ((keyspace_name), table_name, range_start, range_end))") - .build(); + "per-table primary range size estimates", + "CREATE TABLE %s (" + + "keyspace_name text," + + "table_name text," + + "range_start text," + + "range_end text," + + "mean_partition_size bigint," + + "partitions_count bigint," + + "PRIMARY KEY ((keyspace_name), table_name, range_start, range_end))") + .build(); private static final TableMetadata AvailableRanges = parse(AVAILABLE_RANGES, - "available keyspace/ranges during bootstrap/replace that are ready to be served", - "CREATE TABLE %s (" - + "keyspace_name text," - + "ranges set," - + "PRIMARY KEY ((keyspace_name)))") - .build(); + "available keyspace/ranges during bootstrap/replace that are ready to be served", + "CREATE TABLE %s (" + + "keyspace_name text," + + "ranges set," + + "PRIMARY KEY ((keyspace_name)))") + .build(); - private static final TableMetadata TransferredRanges = - parse(TRANSFERRED_RANGES, - "record of transferred ranges for streaming operation", - "CREATE TABLE %s (" - + "operation text," - + "peer inet," - + "keyspace_name text," - + "ranges set," - + "PRIMARY KEY ((operation, keyspace_name), peer))") - .build(); + private static final TableMetadata TransferredRangesV2 = + parse(TRANSFERRED_RANGES_V2, + "record of transferred ranges for streaming operation", + "CREATE TABLE %s (" + + "operation text," + + "peer inet," + + "peer_port int," + + "keyspace_name text," + + "ranges set," + + "PRIMARY KEY ((operation, keyspace_name), peer, peer_port))") + .build(); private static final TableMetadata ViewBuildsInProgress = parse(VIEW_BUILDS_IN_PROGRESS, @@ -277,38 +292,79 @@ public final class SystemKeyspace private static final TableMetadata BuiltViews = parse(BUILT_VIEWS, - "built views", - "CREATE TABLE %s (" - + "keyspace_name text," - + "view_name text," - + "status_replicated boolean," - + "PRIMARY KEY ((keyspace_name), view_name))") - .build(); + "built views", + "CREATE TABLE %s (" + + "keyspace_name text," + + "view_name text," + + "status_replicated boolean," + + "PRIMARY KEY ((keyspace_name), view_name))") + .build(); private static final TableMetadata PreparedStatements = parse(PREPARED_STATEMENTS, - "prepared statements", - "CREATE TABLE %s (" - + "prepared_id blob," - + "logged_keyspace text," - + "query_string text," - + "PRIMARY KEY ((prepared_id)))") - .build(); + "prepared statements", + "CREATE TABLE %s (" + + "prepared_id blob," + + "logged_keyspace text," + + "query_string text," + + "PRIMARY KEY ((prepared_id)))") + .build(); private static final TableMetadata Repairs = parse(REPAIRS, - "repairs", - "CREATE TABLE %s (" - + "parent_id timeuuid, " - + "started_at timestamp, " - + "last_update timestamp, " - + "repaired_at timestamp, " - + "state int, " - + "coordinator inet, " - + "participants set, " - + "ranges set, " - + "cfids set, " - + "PRIMARY KEY (parent_id))").build(); + "repairs", + "CREATE TABLE %s (" + + "parent_id timeuuid, " + + "started_at timestamp, " + + "last_update timestamp, " + + "repaired_at timestamp, " + + "state int, " + + "coordinator inet, " + + "coordinator_port int," + + "participants set," + + "participants_wp set," + + "ranges set, " + + "cfids set, " + + "PRIMARY KEY (parent_id))").build(); + + @Deprecated + private static final TableMetadata LegacyPeers = + parse(LEGACY_PEERS, + "information about known peers in the cluster", + "CREATE TABLE %s (" + + "peer inet," + + "data_center text," + + "host_id uuid," + + "preferred_ip inet," + + "rack text," + + "release_version text," + + "rpc_address inet," + + "schema_version uuid," + + "tokens set," + + "PRIMARY KEY ((peer)))") + .build(); + + @Deprecated + private static final TableMetadata LegacyPeerEvents = + parse(LEGACY_PEER_EVENTS, + "events related to peers", + "CREATE TABLE %s (" + + "peer inet," + + "hints_dropped map," + + "PRIMARY KEY ((peer)))") + .build(); + + @Deprecated + private static final TableMetadata LegacyTransferredRanges = + parse(LEGACY_TRANSFERRED_RANGES, + "record of transferred ranges for streaming operation", + "CREATE TABLE %s (" + + "operation text," + + "peer inet," + + "keyspace_name text," + + "ranges set," + + "PRIMARY KEY ((operation, keyspace_name), peer))") + .build(); private static TableMetadata.Builder parse(String table, String description, String cql) { @@ -331,14 +387,17 @@ public final class SystemKeyspace Batches, Paxos, Local, - Peers, - PeerEvents, + PeersV2, + LegacyPeers, + PeerEventsV2, + LegacyPeerEvents, RangeXfers, CompactionHistory, SSTableActivity, SizeEstimates, AvailableRanges, - TransferredRanges, + TransferredRangesV2, + LegacyTransferredRanges, ViewBuildsInProgress, BuiltViews, PreparedStatements, @@ -384,9 +443,12 @@ public final class SystemKeyspace "rack," + "partitioner," + "rpc_address," + + "rpc_port," + "broadcast_address," + - "listen_address" + - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + "broadcast_port," + + "listen_address," + + "listen_port" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); executeOnceInternal(format(req, LOCAL), LOCAL, @@ -394,12 +456,15 @@ public final class SystemKeyspace FBUtilities.getReleaseVersionString(), QueryProcessor.CQL_VERSION.toString(), String.valueOf(ProtocolVersion.CURRENT.asInt()), - snitch.getDatacenter(FBUtilities.getBroadcastAddress()), - snitch.getRack(FBUtilities.getBroadcastAddress()), + snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort()), + snitch.getRack(FBUtilities.getBroadcastAddressAndPort()), DatabaseDescriptor.getPartitioner().getClass().getName(), DatabaseDescriptor.getRpcAddress(), - FBUtilities.getBroadcastAddress(), - FBUtilities.getLocalAddress()); + DatabaseDescriptor.getNativeTransportPort(), + FBUtilities.getJustBroadcastAddress(), + DatabaseDescriptor.getStoragePort(), + FBUtilities.getJustLocalAddress(), + DatabaseDescriptor.getStoragePort()); } public static void updateCompactionHistory(String ksname, @@ -461,11 +526,10 @@ public final class SystemKeyspace { String buildReq = "DELETE FROM %s.%s WHERE keyspace_name = ? AND view_name = ?"; executeInternal(String.format(buildReq, SchemaConstants.SYSTEM_KEYSPACE_NAME, VIEW_BUILDS_IN_PROGRESS), keyspaceName, viewName); - forceBlockingFlush(VIEW_BUILDS_IN_PROGRESS); String builtReq = "DELETE FROM %s.\"%s\" WHERE keyspace_name = ? AND view_name = ? IF EXISTS"; executeInternal(String.format(builtReq, SchemaConstants.SYSTEM_KEYSPACE_NAME, BUILT_VIEWS), keyspaceName, viewName); - forceBlockingFlush(BUILT_VIEWS); + forceBlockingFlush(VIEW_BUILDS_IN_PROGRESS, BUILT_VIEWS); } public static void finishViewBuildStatus(String ksname, String viewName) @@ -609,39 +673,64 @@ public final class SystemKeyspace /** * Record tokens being used by another node */ - public static synchronized void updateTokens(InetAddress ep, Collection tokens) + public static synchronized void updateTokens(InetAddressAndPort ep, Collection tokens) { - if (ep.equals(FBUtilities.getBroadcastAddress())) + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) return; String req = "INSERT INTO system.%s (peer, tokens) VALUES (?, ?)"; - executeInternal(format(req, PEERS), ep, tokensAsSet(tokens)); + executeInternal(String.format(req, LEGACY_PEERS), ep.address, tokensAsSet(tokens)); + req = "INSERT INTO system.%s (peer, peer_port, tokens) VALUES (?, ?, ?)"; + executeInternal(String.format(req, PEERS_V2), ep.address, ep.port, tokensAsSet(tokens)); } - public static synchronized void updatePreferredIP(InetAddress ep, InetAddress preferred_ip) + public static synchronized void updatePreferredIP(InetAddressAndPort ep, InetAddressAndPort preferred_ip) { if (getPreferredIP(ep) == preferred_ip) return; String req = "INSERT INTO system.%s (peer, preferred_ip) VALUES (?, ?)"; - executeInternal(format(req, PEERS), ep, preferred_ip); - forceBlockingFlush(PEERS); + executeInternal(String.format(req, LEGACY_PEERS), ep.address, preferred_ip.address); + req = "INSERT INTO system.%s (peer, peer_port, preferred_ip, preferred_port) VALUES (?, ?, ?, ?)"; + executeInternal(String.format(req, PEERS_V2), ep.address, ep.port, preferred_ip.address, preferred_ip.port); + forceBlockingFlush(LEGACY_PEERS, PEERS_V2); } - public static synchronized void updatePeerInfo(InetAddress ep, String columnName, Object value) + public static synchronized void updatePeerInfo(InetAddressAndPort ep, String columnName, Object value) { - if (ep.equals(FBUtilities.getBroadcastAddress())) + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) return; String req = "INSERT INTO system.%s (peer, %s) VALUES (?, ?)"; - executeInternal(format(req, PEERS, columnName), ep, value); + executeInternal(String.format(req, LEGACY_PEERS, columnName), ep.address, value); + //This column doesn't match across the two tables + if (columnName.equals("rpc_address")) + { + columnName = "native_address"; + } + req = "INSERT INTO system.%s (peer, peer_port, %s) VALUES (?, ?, ?)"; + executeInternal(String.format(req, PEERS_V2, columnName), ep.address, ep.port, value); } - public static synchronized void updateHintsDropped(InetAddress ep, UUID timePeriod, int value) + public static synchronized void updatePeerNativeAddress(InetAddressAndPort ep, InetAddressAndPort address) + { + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) + return; + + String req = "INSERT INTO system.%s (peer, rpc_address) VALUES (?, ?)"; + executeInternal(String.format(req, LEGACY_PEERS), ep.address, address.address); + req = "INSERT INTO system.%s (peer, peer_port, native_address, native_port) VALUES (?, ?, ?, ?)"; + executeInternal(String.format(req, PEERS_V2), ep.address, ep.port, address.address, address.port); + } + + + public static synchronized void updateHintsDropped(InetAddressAndPort ep, UUID timePeriod, int value) { // with 30 day TTL String req = "UPDATE system.%s USING TTL 2592000 SET hints_dropped[ ? ] = ? WHERE peer = ?"; - executeInternal(format(req, PEER_EVENTS), timePeriod, value, ep); + executeInternal(String.format(req, LEGACY_PEER_EVENTS), timePeriod, value, ep.address); + req = "UPDATE system.%s USING TTL 2592000 SET hints_dropped[ ? ] = ? WHERE peer = ? AND peer_port = ?"; + executeInternal(String.format(req, PEER_EVENTS_V2), timePeriod, value, ep.address, ep.port); } public static synchronized void updateSchemaVersion(UUID version) @@ -673,11 +762,13 @@ public final class SystemKeyspace /** * Remove stored tokens being used by another node */ - public static synchronized void removeEndpoint(InetAddress ep) + public static synchronized void removeEndpoint(InetAddressAndPort ep) { String req = "DELETE FROM system.%s WHERE peer = ?"; - executeInternal(format(req, PEERS), ep); - forceBlockingFlush(PEERS); + executeInternal(String.format(req, LEGACY_PEERS), ep.address); + req = String.format("DELETE FROM system.%s WHERE peer = ? AND peer_port = ?", PEERS_V2); + executeInternal(req, ep.address, ep.port); + forceBlockingFlush(LEGACY_PEERS, PEERS_V2); } /** @@ -696,22 +787,32 @@ public final class SystemKeyspace forceBlockingFlush(LOCAL); } - public static void forceBlockingFlush(String cfname) + public static void forceBlockingFlush(String ...cfnames) { if (!DatabaseDescriptor.isUnsafeSystem()) - FBUtilities.waitOnFuture(Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(cfname).forceFlush()); + { + List> futures = new ArrayList<>(); + + for (String cfname : cfnames) + { + futures.add(Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(cfname).forceFlush()); + } + FBUtilities.waitOnFutures(futures); + } } /** * Return a map of stored tokens to IP addresses * */ - public static SetMultimap loadTokens() + public static SetMultimap loadTokens() { - SetMultimap tokenMap = HashMultimap.create(); - for (UntypedResultSet.Row row : executeInternal("SELECT peer, tokens FROM system." + PEERS)) + SetMultimap tokenMap = HashMultimap.create(); + for (UntypedResultSet.Row row : executeInternal("SELECT peer, peer_port, tokens FROM system." + PEERS_V2)) { - InetAddress peer = row.getInetAddress("peer"); + InetAddress address = row.getInetAddress("peer"); + Integer port = row.getInt("peer_port"); + InetAddressAndPort peer = InetAddressAndPort.getByAddressOverrideDefaults(address, port); if (row.has("tokens")) tokenMap.putAll(peer, deserializeTokens(row.getSet("tokens", UTF8Type.instance))); } @@ -723,12 +824,14 @@ public final class SystemKeyspace * Return a map of store host_ids to IP addresses * */ - public static Map loadHostIds() + public static Map loadHostIds() { - Map hostIdMap = new HashMap<>(); - for (UntypedResultSet.Row row : executeInternal("SELECT peer, host_id FROM system." + PEERS)) + Map hostIdMap = new HashMap<>(); + for (UntypedResultSet.Row row : executeInternal("SELECT peer, peer_port, host_id FROM system." + PEERS_V2)) { - InetAddress peer = row.getInetAddress("peer"); + InetAddress address = row.getInetAddress("peer"); + Integer port = row.getInt("peer_port"); + InetAddressAndPort peer = InetAddressAndPort.getByAddressOverrideDefaults(address, port); if (row.has("host_id")) { hostIdMap.put(peer, row.getUUID("host_id")); @@ -743,24 +846,29 @@ public final class SystemKeyspace * @param ep endpoint address to check * @return Preferred IP for given endpoint if present, otherwise returns given ep */ - public static InetAddress getPreferredIP(InetAddress ep) + public static InetAddressAndPort getPreferredIP(InetAddressAndPort ep) { - String req = "SELECT preferred_ip FROM system.%s WHERE peer=?"; - UntypedResultSet result = executeInternal(format(req, PEERS), ep); + String req = "SELECT preferred_ip, preferred_port FROM system.%s WHERE peer=? AND peer_port = ?"; + UntypedResultSet result = executeInternal(String.format(req, PEERS_V2), ep.address, ep.port); if (!result.isEmpty() && result.one().has("preferred_ip")) - return result.one().getInetAddress("preferred_ip"); + { + UntypedResultSet.Row row = result.one(); + return InetAddressAndPort.getByAddressOverrideDefaults(row.getInetAddress("preferred_ip"), row.getInt("preferred_port")); + } return ep; } /** * Return a map of IP addresses containing a map of dc and rack info */ - public static Map> loadDcRackInfo() + public static Map> loadDcRackInfo() { - Map> result = new HashMap<>(); - for (UntypedResultSet.Row row : executeInternal("SELECT peer, data_center, rack from system." + PEERS)) + Map> result = new HashMap<>(); + for (UntypedResultSet.Row row : executeInternal("SELECT peer, peer_port, data_center, rack from system." + PEERS_V2)) { - InetAddress peer = row.getInetAddress("peer"); + InetAddress address = row.getInetAddress("peer"); + Integer port = row.getInt("peer_port"); + InetAddressAndPort peer = InetAddressAndPort.getByAddressOverrideDefaults(address, port); if (row.has("data_center") && row.has("rack")) { Map dcRack = new HashMap<>(); @@ -779,16 +887,16 @@ public final class SystemKeyspace * @param ep endpoint address to check * @return Release version or null if version is unknown. */ - public static CassandraVersion getReleaseVersion(InetAddress ep) + public static CassandraVersion getReleaseVersion(InetAddressAndPort ep) { try { - if (FBUtilities.getBroadcastAddress().equals(ep)) + if (FBUtilities.getBroadcastAddressAndPort().equals(ep)) { return new CassandraVersion(FBUtilities.getReleaseVersionString()); } - String req = "SELECT release_version FROM system.%s WHERE peer=?"; - UntypedResultSet result = executeInternal(format(req, PEERS), ep); + String req = "SELECT release_version FROM system.%s WHERE peer=? AND peer_port=?"; + UntypedResultSet result = executeInternal(String.format(req, PEERS_V2), ep.address, ep.port); if (result != null && result.one().has("release_version")) { return new CassandraVersion(result.one().getString("release_version")); @@ -1197,7 +1305,7 @@ public final class SystemKeyspace } public static synchronized void updateTransferredRanges(StreamOperation streamOperation, - InetAddress peer, + InetAddressAndPort peer, String keyspace, Collection> streamedRanges) { @@ -1207,17 +1315,21 @@ public final class SystemKeyspace { rangesToUpdate.add(rangeToBytes(range)); } - executeInternal(format(cql, TRANSFERRED_RANGES), rangesToUpdate, streamOperation.getDescription(), peer, keyspace); + executeInternal(format(cql, LEGACY_TRANSFERRED_RANGES), rangesToUpdate, streamOperation.getDescription(), peer.address, keyspace); + cql = "UPDATE system.%s SET ranges = ranges + ? WHERE operation = ? AND peer = ? AND peer_port = ? AND keyspace_name = ?"; + executeInternal(String.format(cql, TRANSFERRED_RANGES_V2), rangesToUpdate, streamOperation.getDescription(), peer.address, peer.port, keyspace); } - public static synchronized Map>> getTransferredRanges(String description, String keyspace, IPartitioner partitioner) + public static synchronized Map>> getTransferredRanges(String description, String keyspace, IPartitioner partitioner) { - Map>> result = new HashMap<>(); + Map>> result = new HashMap<>(); String query = "SELECT * FROM system.%s WHERE operation = ? AND keyspace_name = ?"; - UntypedResultSet rs = executeInternal(format(query, TRANSFERRED_RANGES), description, keyspace); + UntypedResultSet rs = executeInternal(String.format(query, TRANSFERRED_RANGES_V2), description, keyspace); for (UntypedResultSet.Row row : rs) { - InetAddress peer = row.getInetAddress("peer"); + InetAddress peerAddress = row.getInetAddress("peer"); + int port = row.getInt("peer_port"); + InetAddressAndPort peer = InetAddressAndPort.getByAddressOverrideDefaults(peerAddress, port); Set rawRanges = row.getSet("ranges", BytesType.instance); Set> ranges = Sets.newHashSetWithExpectedSize(rawRanges.size()); for (ByteBuffer rawRange : rawRanges) diff --git a/src/java/org/apache/cassandra/db/SystemKeyspaceMigrator40.java b/src/java/org/apache/cassandra/db/SystemKeyspaceMigrator40.java new file mode 100644 index 0000000000..ea5ff59b04 --- /dev/null +++ b/src/java/org/apache/cassandra/db/SystemKeyspaceMigrator40.java @@ -0,0 +1,184 @@ +/* + * 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.cassandra.db; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.schema.SchemaConstants; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.db.marshal.BytesType; +import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.db.marshal.UUIDType; + +/** + * Migrate 3.0 versions of some tables to 4.0. In this case it's just extra columns and some keys + * that are changed. + * + * Can't just add the additional columns because they are primary key columns and C* doesn't support changing + * key columns even if it's just clustering columns. + */ +public class SystemKeyspaceMigrator40 +{ + static final String legacyPeersName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.LEGACY_PEERS); + static final String peersName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.PEERS_V2); + static final String legacyPeerEventsName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.LEGACY_PEER_EVENTS); + static final String peerEventsName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.PEER_EVENTS_V2); + static final String legacyTransferredRangesName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.LEGACY_TRANSFERRED_RANGES); + static final String transferredRangesName = String.format("%s.%s", SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.TRANSFERRED_RANGES_V2); + + private static final Logger logger = LoggerFactory.getLogger(SystemKeyspaceMigrator40.class); + + private SystemKeyspaceMigrator40() {} + + public static void migrate() + { + migratePeers(); + migratePeerEvents(); + migrateTransferredRanges(); + } + + private static void migratePeers() + { + ColumnFamilyStore newPeers = Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(SystemKeyspace.PEERS_V2); + + if (!newPeers.isEmpty()) + return; + + logger.info("{} table was empty, migrating legacy {}, if this fails you should fix the issue and then truncate {} to have it try again.", + peersName, legacyPeersName, peersName); + + String query = String.format("SELECT * FROM %s", + legacyPeersName); + + String insert = String.format("INSERT INTO %s ( " + + "peer, " + + "peer_port, " + + "data_center, " + + "host_id, " + + "preferred_ip, " + + "preferred_port, " + + "rack, " + + "release_version, " + + "native_address, " + + "native_port, " + + "schema_version, " + + "tokens) " + + " values ( ?, ?, ? , ? , ?, ?, ?, ?, ?, ?, ?, ?)", + peersName); + + UntypedResultSet rows = QueryProcessor.executeInternalWithPaging(query, 1000); + int transferred = 0; + logger.info("Migrating rows from legacy {} to {}", legacyPeersName, peersName); + for (UntypedResultSet.Row row : rows) + { + logger.debug("Transferring row {}", transferred); + QueryProcessor.executeInternal(insert, + row.has("peer") ? row.getInetAddress("peer") : null, + DatabaseDescriptor.getStoragePort(), + row.has("data_center") ? row.getString("data_center") : null, + row.has("host_id") ? row.getUUID("host_id") : null, + row.has("preferred_ip") ? row.getInetAddress("preferred_ip") : null, + DatabaseDescriptor.getStoragePort(), + row.has("rack") ? row.getString("rack") : null, + row.has("release_version") ? row.getString("release_version") : null, + row.has("rpc_address") ? row.getInetAddress("rpc_address") : null, + DatabaseDescriptor.getNativeTransportPort(), + row.has("schema_version") ? row.getUUID("schema_version") : null, + row.has("tokens") ? row.getSet("tokens", UTF8Type.instance) : null); + transferred++; + } + logger.info("Migrated {} rows from legacy {} to {}", transferred, legacyPeersName, peersName); + } + + private static void migratePeerEvents() + { + ColumnFamilyStore newPeerEvents = Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(SystemKeyspace.PEER_EVENTS_V2); + + if (!newPeerEvents.isEmpty()) + return; + + logger.info("{} table was empty, migrating legacy {} to {}", peerEventsName, legacyPeerEventsName, peerEventsName); + + String query = String.format("SELECT * FROM %s", + legacyPeerEventsName); + + String insert = String.format("INSERT INTO %s ( " + + "peer, " + + "peer_port, " + + "hints_dropped) " + + " values ( ?, ?, ? )", + peerEventsName); + + UntypedResultSet rows = QueryProcessor.executeInternalWithPaging(query, 1000); + int transferred = 0; + for (UntypedResultSet.Row row : rows) + { + logger.debug("Transferring row {}", transferred); + QueryProcessor.executeInternal(insert, + row.has("peer") ? row.getInetAddress("peer") : null, + DatabaseDescriptor.getStoragePort(), + row.has("hints_dropped") ? row.getMap("hints_dropped", UUIDType.instance, Int32Type.instance) : null); + transferred++; + } + logger.info("Migrated {} rows from legacy {} to {}", transferred, legacyPeerEventsName, peerEventsName); + } + + static void migrateTransferredRanges() + { + ColumnFamilyStore newTransferredRanges = Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(SystemKeyspace.TRANSFERRED_RANGES_V2); + + if (!newTransferredRanges.isEmpty()) + return; + + logger.info("{} table was empty, migrating legacy {} to {}", transferredRangesName, legacyTransferredRangesName, transferredRangesName); + + String query = String.format("SELECT * FROM %s", + legacyTransferredRangesName); + + String insert = String.format("INSERT INTO %s (" + + "operation, " + + "peer, " + + "peer_port, " + + "keyspace_name, " + + "ranges) " + + " values ( ?, ?, ? , ?, ?)", + transferredRangesName); + + UntypedResultSet rows = QueryProcessor.executeInternalWithPaging(query, 1000); + int transferred = 0; + for (UntypedResultSet.Row row : rows) + { + logger.debug("Transferring row {}", transferred); + QueryProcessor.executeInternal(insert, + row.has("operation") ? row.getString("operation") : null, + row.has("peer") ? row.getInetAddress("peer") : null, + DatabaseDescriptor.getStoragePort(), + row.has("keyspace_name") ? row.getString("keyspace_name") : null, + row.has("ranges") ? row.getSet("ranges", BytesType.instance) : null); + transferred++; + } + + logger.info("Migrated {} rows from legacy {} to {}", transferred, legacyTransferredRangesName, transferredRangesName); + } + +} diff --git a/src/java/org/apache/cassandra/db/view/ViewUtils.java b/src/java/org/apache/cassandra/db/view/ViewUtils.java index 4dc17662c3..df16943263 100644 --- a/src/java/org/apache/cassandra/db/view/ViewUtils.java +++ b/src/java/org/apache/cassandra/db/view/ViewUtils.java @@ -18,7 +18,6 @@ package org.apache.cassandra.db.view; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -27,6 +26,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.dht.Token; import org.apache.cassandra.locator.AbstractReplicationStrategy; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.NetworkTopologyStrategy; import org.apache.cassandra.utils.FBUtilities; @@ -58,14 +58,14 @@ public final class ViewUtils * * @return Optional.empty() if this method is called using a base token which does not belong to this replica */ - public static Optional getViewNaturalEndpoint(String keyspaceName, Token baseToken, Token viewToken) + public static Optional getViewNaturalEndpoint(String keyspaceName, Token baseToken, Token viewToken) { AbstractReplicationStrategy replicationStrategy = Keyspace.open(keyspaceName).getReplicationStrategy(); - String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); - List baseEndpoints = new ArrayList<>(); - List viewEndpoints = new ArrayList<>(); - for (InetAddress baseEndpoint : replicationStrategy.getNaturalEndpoints(baseToken)) + String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); + List baseEndpoints = new ArrayList<>(); + List viewEndpoints = new ArrayList<>(); + for (InetAddressAndPort baseEndpoint : replicationStrategy.getNaturalEndpoints(baseToken)) { // An endpoint is local if we're not using Net if (!(replicationStrategy instanceof NetworkTopologyStrategy) || @@ -73,10 +73,10 @@ public final class ViewUtils baseEndpoints.add(baseEndpoint); } - for (InetAddress viewEndpoint : replicationStrategy.getNaturalEndpoints(viewToken)) + for (InetAddressAndPort viewEndpoint : replicationStrategy.getNaturalEndpoints(viewToken)) { // If we are a base endpoint which is also a view replica, we use ourselves as our view replica - if (viewEndpoint.equals(FBUtilities.getBroadcastAddress())) + if (viewEndpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return Optional.of(viewEndpoint); // We have to remove any endpoint which is shared between the base and the view, as it will select itself @@ -92,7 +92,7 @@ public final class ViewUtils // Since the same replication strategy is used, the same placement should be used and we should get the same // number of replicas for all of the tokens in the ring. assert baseEndpoints.size() == viewEndpoints.size() : "Replication strategy should have the same number of endpoints for the base and the view"; - int baseIdx = baseEndpoints.indexOf(FBUtilities.getBroadcastAddress()); + int baseIdx = baseEndpoints.indexOf(FBUtilities.getBroadcastAddressAndPort()); if (baseIdx < 0) //This node is not a base replica of this key, so we return empty diff --git a/src/java/org/apache/cassandra/dht/BootStrapper.java b/src/java/org/apache/cassandra/dht/BootStrapper.java index a25f867d5e..432586b13b 100644 --- a/src/java/org/apache/cassandra/dht/BootStrapper.java +++ b/src/java/org/apache/cassandra/dht/BootStrapper.java @@ -18,7 +18,6 @@ package org.apache.cassandra.dht; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -38,6 +37,7 @@ import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.locator.AbstractReplicationStrategy; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.streaming.*; @@ -51,12 +51,12 @@ public class BootStrapper extends ProgressEventNotifierSupport private static final Logger logger = LoggerFactory.getLogger(BootStrapper.class); /* endpoint that needs to be bootstrapped */ - protected final InetAddress address; + protected final InetAddressAndPort address; /* token of the node being bootstrapped. */ protected final Collection tokens; protected final TokenMetadata tokenMetadata; - public BootStrapper(InetAddress address, Collection tokens, TokenMetadata tmd) + public BootStrapper(InetAddressAndPort address, Collection tokens, TokenMetadata tmd) { assert address != null; assert tokens != null && !tokens.isEmpty(); @@ -159,7 +159,7 @@ public class BootStrapper extends ProgressEventNotifierSupport * otherwise, if allocationKeyspace is specified use the token allocation algorithm to generate suitable tokens * else choose num_tokens tokens at random */ - public static Collection getBootstrapTokens(final TokenMetadata metadata, InetAddress address, int schemaWaitDelay) throws ConfigurationException + public static Collection getBootstrapTokens(final TokenMetadata metadata, InetAddressAndPort address, int schemaWaitDelay) throws ConfigurationException { String allocationKeyspace = DatabaseDescriptor.getAllocateTokensForKeyspace(); Collection initialTokens = DatabaseDescriptor.getInitialTokens(); @@ -199,13 +199,13 @@ public class BootStrapper extends ProgressEventNotifierSupport } static Collection allocateTokens(final TokenMetadata metadata, - InetAddress address, + InetAddressAndPort address, String allocationKeyspace, int numTokens, int schemaWaitDelay) { StorageService.instance.waitForSchema(schemaWaitDelay); - if (!FBUtilities.getBroadcastAddress().equals(InetAddress.getLoopbackAddress())) + if (!FBUtilities.getBroadcastAddressAndPort().equals(InetAddressAndPort.getLoopbackAddress())) Gossiper.waitToSettle(); Keyspace ks = Keyspace.open(allocationKeyspace); diff --git a/src/java/org/apache/cassandra/dht/RangeFetchMapCalculator.java b/src/java/org/apache/cassandra/dht/RangeFetchMapCalculator.java index d407212989..b90bc96392 100644 --- a/src/java/org/apache/cassandra/dht/RangeFetchMapCalculator.java +++ b/src/java/org/apache/cassandra/dht/RangeFetchMapCalculator.java @@ -32,6 +32,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.FBUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,7 +73,7 @@ public class RangeFetchMapCalculator { private static final Logger logger = LoggerFactory.getLogger(RangeFetchMapCalculator.class); private static final long TRIVIAL_RANGE_LIMIT = 1000; - private final Multimap, InetAddress> rangesWithSources; + private final Multimap, InetAddressAndPort> rangesWithSources; private final Collection sourceFilters; private final String keyspace; //We need two Vertices to act as source and destination in the algorithm @@ -80,7 +81,7 @@ public class RangeFetchMapCalculator private final Vertex destinationVertex = OuterVertex.getDestinationVertex(); private final Set> trivialRanges; - public RangeFetchMapCalculator(Multimap, InetAddress> rangesWithSources, + public RangeFetchMapCalculator(Multimap, InetAddressAndPort> rangesWithSources, Collection sourceFilters, String keyspace) { @@ -108,16 +109,16 @@ public class RangeFetchMapCalculator return false; } - public Multimap> getRangeFetchMap() + public Multimap> getRangeFetchMap() { - Multimap> fetchMap = HashMultimap.create(); + Multimap> fetchMap = HashMultimap.create(); fetchMap.putAll(getRangeFetchMapForNonTrivialRanges()); fetchMap.putAll(getRangeFetchMapForTrivialRanges(fetchMap)); return fetchMap; } @VisibleForTesting - Multimap> getRangeFetchMapForNonTrivialRanges() + Multimap> getRangeFetchMapForNonTrivialRanges() { //Get the graph with edges between ranges and their source endpoints MutableCapacityGraph graph = getGraph(); @@ -148,19 +149,19 @@ public class RangeFetchMapCalculator } @VisibleForTesting - Multimap> getRangeFetchMapForTrivialRanges(Multimap> optimisedMap) + Multimap> getRangeFetchMapForTrivialRanges(Multimap> optimisedMap) { - Multimap> fetchMap = HashMultimap.create(); + Multimap> fetchMap = HashMultimap.create(); for (Range trivialRange : trivialRanges) { boolean added = false; boolean localDCCheck = true; while (!added) { - List srcs = new ArrayList<>(rangesWithSources.get(trivialRange)); + List srcs = new ArrayList<>(rangesWithSources.get(trivialRange)); // sort with the endpoint having the least number of streams first: srcs.sort(Comparator.comparingInt(o -> optimisedMap.get(o).size())); - for (InetAddress src : srcs) + for (InetAddressAndPort src : srcs) { if (passFilters(src, localDCCheck)) { @@ -202,9 +203,9 @@ public class RangeFetchMapCalculator * @param result Flow algorithm result * @return Multi Map of Machine to Ranges */ - private Multimap> getRangeFetchMapFromGraphResult(MutableCapacityGraph graph, MaximumFlowAlgorithmResult> result) + private Multimap> getRangeFetchMapFromGraphResult(MutableCapacityGraph graph, MaximumFlowAlgorithmResult> result) { - final Multimap> rangeFetchMapMap = HashMultimap.create(); + final Multimap> rangeFetchMapMap = HashMultimap.create(); if(result == null) return rangeFetchMapMap; final Function, Integer> flowFunction = result.calcFlowFunction(); @@ -346,13 +347,13 @@ public class RangeFetchMapCalculator private boolean addEndpoints(MutableCapacityGraph capacityGraph, RangeVertex rangeVertex, boolean localDCCheck) { boolean sourceFound = false; - for (InetAddress endpoint : rangesWithSources.get(rangeVertex.getRange())) + for (InetAddressAndPort endpoint : rangesWithSources.get(rangeVertex.getRange())) { if (passFilters(endpoint, localDCCheck)) { sourceFound = true; // if we pass filters, it means that we don't filter away localhost and we can count it as a source: - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) continue; // but don't add localhost to the graph to avoid streaming locally final Vertex endpointVertex = new EndpointVertex(endpoint); capacityGraph.insertVertex(rangeVertex); @@ -363,7 +364,7 @@ public class RangeFetchMapCalculator return sourceFound; } - private boolean isInLocalDC(InetAddress endpoint) + private boolean isInLocalDC(InetAddressAndPort endpoint) { return DatabaseDescriptor.getLocalDataCenter().equals(DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint)); } @@ -374,7 +375,7 @@ public class RangeFetchMapCalculator * @param localDCCheck Allow endpoints with local DC * @return True if filters pass this endpoint */ - private boolean passFilters(final InetAddress endpoint, boolean localDCCheck) + private boolean passFilters(final InetAddressAndPort endpoint, boolean localDCCheck) { for (RangeStreamer.ISourceFilter filter : sourceFilters) { @@ -410,15 +411,15 @@ public class RangeFetchMapCalculator */ private static class EndpointVertex extends Vertex { - private final InetAddress endpoint; + private final InetAddressAndPort endpoint; - public EndpointVertex(InetAddress endpoint) + public EndpointVertex(InetAddressAndPort endpoint) { assert endpoint != null; this.endpoint = endpoint; } - public InetAddress getEndpoint() + public InetAddressAndPort getEndpoint() { return endpoint; } diff --git a/src/java/org/apache/cassandra/dht/RangeStreamer.java b/src/java/org/apache/cassandra/dht/RangeStreamer.java index eabb21261c..439ebc6307 100644 --- a/src/java/org/apache/cassandra/dht/RangeStreamer.java +++ b/src/java/org/apache/cassandra/dht/RangeStreamer.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.dht; -import java.net.InetAddress; import java.util.*; import com.google.common.annotations.VisibleForTesting; @@ -26,8 +25,9 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.LocalStrategy; -import org.apache.cassandra.service.ActiveRepairService; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,10 +59,10 @@ public class RangeStreamer /* current token ring */ private final TokenMetadata metadata; /* address of this node */ - private final InetAddress address; + private final InetAddressAndPort address; /* streaming description */ private final String description; - private final Multimap>>> toFetch = HashMultimap.create(); + private final Multimap>>> toFetch = HashMultimap.create(); private final Set sourceFilters = new HashSet<>(); private final StreamPlan streamPlan; private final boolean useStrictConsistency; @@ -74,7 +74,7 @@ public class RangeStreamer */ public static interface ISourceFilter { - public boolean shouldInclude(InetAddress endpoint); + public boolean shouldInclude(InetAddressAndPort endpoint); } /** @@ -90,7 +90,7 @@ public class RangeStreamer this.fd = fd; } - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { return fd.isAlive(endpoint); } @@ -110,7 +110,7 @@ public class RangeStreamer this.snitch = snitch; } - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { return snitch.getDatacenter(endpoint).equals(sourceDc); } @@ -121,9 +121,9 @@ public class RangeStreamer */ public static class ExcludeLocalNodeFilter implements ISourceFilter { - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { - return !FBUtilities.getBroadcastAddress().equals(endpoint); + return !FBUtilities.getBroadcastAddressAndPort().equals(endpoint); } } @@ -132,14 +132,14 @@ public class RangeStreamer */ public static class WhitelistedSourcesFilter implements ISourceFilter { - private final Set whitelistedSources; + private final Set whitelistedSources; - public WhitelistedSourcesFilter(Set whitelistedSources) + public WhitelistedSourcesFilter(Set whitelistedSources) { this.whitelistedSources = whitelistedSources; } - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { return whitelistedSources.contains(endpoint); } @@ -147,7 +147,7 @@ public class RangeStreamer public RangeStreamer(TokenMetadata metadata, Collection tokens, - InetAddress address, + InetAddressAndPort address, StreamOperation streamOperation, boolean useStrictConsistency, IEndpointSnitch snitch, @@ -186,18 +186,18 @@ public class RangeStreamer } boolean useStrictSource = useStrictSourcesForRanges(keyspaceName); - Multimap, InetAddress> rangesForKeyspace = useStrictSource + Multimap, InetAddressAndPort> rangesForKeyspace = useStrictSource ? getAllRangesWithStrictSourcesFor(keyspaceName, ranges) : getAllRangesWithSourcesFor(keyspaceName, ranges); - for (Map.Entry, InetAddress> entry : rangesForKeyspace.entries()) + for (Map.Entry, InetAddressAndPort> entry : rangesForKeyspace.entries()) logger.info("{}: range {} exists on {} for keyspace {}", description, entry.getKey(), entry.getValue(), keyspaceName); AbstractReplicationStrategy strat = Keyspace.open(keyspaceName).getReplicationStrategy(); - Multimap> rangeFetchMap = useStrictSource || strat == null || strat.getReplicationFactor() == 1 + Multimap> rangeFetchMap = useStrictSource || strat == null || strat.getReplicationFactor() == 1 ? getRangeFetchMap(rangesForKeyspace, sourceFilters, keyspaceName, useStrictConsistency) : getOptimizedRangeFetchMap(rangesForKeyspace, sourceFilters, keyspaceName); - for (Map.Entry>> entry : rangeFetchMap.asMap().entrySet()) + for (Map.Entry>> entry : rangeFetchMap.asMap().entrySet()) { if (logger.isTraceEnabled()) { @@ -226,19 +226,19 @@ public class RangeStreamer * * @throws java.lang.IllegalStateException when there is no source to get data streamed */ - private Multimap, InetAddress> getAllRangesWithSourcesFor(String keyspaceName, Collection> desiredRanges) + private Multimap, InetAddressAndPort> getAllRangesWithSourcesFor(String keyspaceName, Collection> desiredRanges) { AbstractReplicationStrategy strat = Keyspace.open(keyspaceName).getReplicationStrategy(); - Multimap, InetAddress> rangeAddresses = strat.getRangeAddresses(metadata.cloneOnlyTokenMap()); + Multimap, InetAddressAndPort> rangeAddresses = strat.getRangeAddresses(metadata.cloneOnlyTokenMap()); - Multimap, InetAddress> rangeSources = ArrayListMultimap.create(); + Multimap, InetAddressAndPort> rangeSources = ArrayListMultimap.create(); for (Range desiredRange : desiredRanges) { for (Range range : rangeAddresses.keySet()) { if (range.contains(desiredRange)) { - List preferred = snitch.getSortedListByProximity(address, rangeAddresses.get(range)); + List preferred = snitch.getSortedListByProximity(address, rangeAddresses.get(range)); rangeSources.putAll(desiredRange, preferred); break; } @@ -258,30 +258,30 @@ public class RangeStreamer * * @throws java.lang.IllegalStateException when there is no source to get data streamed, or more than 1 source found. */ - private Multimap, InetAddress> getAllRangesWithStrictSourcesFor(String keyspace, Collection> desiredRanges) + private Multimap, InetAddressAndPort> getAllRangesWithStrictSourcesFor(String keyspace, Collection> desiredRanges) { assert tokens != null; AbstractReplicationStrategy strat = Keyspace.open(keyspace).getReplicationStrategy(); // Active ranges TokenMetadata metadataClone = metadata.cloneOnlyTokenMap(); - Multimap, InetAddress> addressRanges = strat.getRangeAddresses(metadataClone); + Multimap, InetAddressAndPort> addressRanges = strat.getRangeAddresses(metadataClone); // Pending ranges metadataClone.updateNormalTokens(tokens, address); - Multimap, InetAddress> pendingRangeAddresses = strat.getRangeAddresses(metadataClone); + Multimap, InetAddressAndPort> pendingRangeAddresses = strat.getRangeAddresses(metadataClone); // Collects the source that will have its range moved to the new node - Multimap, InetAddress> rangeSources = ArrayListMultimap.create(); + Multimap, InetAddressAndPort> rangeSources = ArrayListMultimap.create(); for (Range desiredRange : desiredRanges) { - for (Map.Entry, Collection> preEntry : addressRanges.asMap().entrySet()) + for (Map.Entry, Collection> preEntry : addressRanges.asMap().entrySet()) { if (preEntry.getKey().contains(desiredRange)) { - Set oldEndpoints = Sets.newHashSet(preEntry.getValue()); - Set newEndpoints = Sets.newHashSet(pendingRangeAddresses.get(desiredRange)); + Set oldEndpoints = Sets.newHashSet(preEntry.getValue()); + Set newEndpoints = Sets.newHashSet(pendingRangeAddresses.get(desiredRange)); // Due to CASSANDRA-5953 we can have a higher RF then we have endpoints. // So we need to be careful to only be strict when endpoints == RF @@ -296,14 +296,14 @@ public class RangeStreamer } // Validate - Collection addressList = rangeSources.get(desiredRange); + Collection addressList = rangeSources.get(desiredRange); if (addressList == null || addressList.isEmpty()) throw new IllegalStateException("No sources found for " + desiredRange); if (addressList.size() > 1) throw new IllegalStateException("Multiple endpoints found for " + desiredRange); - InetAddress sourceIp = addressList.iterator().next(); + InetAddressAndPort sourceIp = addressList.iterator().next(); EndpointState sourceState = Gossiper.instance.getEndpointStateForEndpoint(sourceIp); if (Gossiper.instance.isEnabled() && (sourceState == null || !sourceState.isAlive())) throw new RuntimeException("A node required to move the data consistently is down (" + sourceIp + "). " + @@ -320,17 +320,17 @@ public class RangeStreamer * @param keyspace keyspace name * @return Map of source endpoint to collection of ranges */ - private static Multimap> getRangeFetchMap(Multimap, InetAddress> rangesWithSources, - Collection sourceFilters, String keyspace, - boolean useStrictConsistency) + private static Multimap> getRangeFetchMap(Multimap, InetAddressAndPort> rangesWithSources, + Collection sourceFilters, String keyspace, + boolean useStrictConsistency) { - Multimap> rangeFetchMapMap = HashMultimap.create(); + Multimap> rangeFetchMapMap = HashMultimap.create(); for (Range range : rangesWithSources.keySet()) { boolean foundSource = false; outer: - for (InetAddress address : rangesWithSources.get(range)) + for (InetAddressAndPort address : rangesWithSources.get(range)) { for (ISourceFilter filter : sourceFilters) { @@ -338,7 +338,7 @@ public class RangeStreamer continue outer; } - if (address.equals(FBUtilities.getBroadcastAddress())) + if (address.equals(FBUtilities.getBroadcastAddressAndPort())) { // If localhost is a source, we have found one, but we don't add it to the map to avoid streaming locally foundSource = true; @@ -371,11 +371,11 @@ public class RangeStreamer } - private static Multimap> getOptimizedRangeFetchMap(Multimap, InetAddress> rangesWithSources, - Collection sourceFilters, String keyspace) + private static Multimap> getOptimizedRangeFetchMap(Multimap, InetAddressAndPort> rangesWithSources, + Collection sourceFilters, String keyspace) { RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, sourceFilters, keyspace); - Multimap> rangeFetchMapMap = calculator.getRangeFetchMap(); + Multimap> rangeFetchMapMap = calculator.getRangeFetchMap(); logger.info("Output from RangeFetchMapCalculator for keyspace {}", keyspace); validateRangeFetchMap(rangesWithSources, rangeFetchMapMap, keyspace); return rangeFetchMapMap; @@ -387,11 +387,11 @@ public class RangeStreamer * @param rangeFetchMapMap * @param keyspace */ - private static void validateRangeFetchMap(Multimap, InetAddress> rangesWithSources, Multimap> rangeFetchMapMap, String keyspace) + private static void validateRangeFetchMap(Multimap, InetAddressAndPort> rangesWithSources, Multimap> rangeFetchMapMap, String keyspace) { - for (Map.Entry> entry : rangeFetchMapMap.entries()) + for (Map.Entry> entry : rangeFetchMapMap.entries()) { - if(entry.getKey().equals(FBUtilities.getBroadcastAddress())) + if(entry.getKey().equals(FBUtilities.getBroadcastAddressAndPort())) { throw new IllegalStateException("Trying to stream locally. Range: " + entry.getValue() + " in keyspace " + keyspace); @@ -407,26 +407,26 @@ public class RangeStreamer } } - public static Multimap> getWorkMap(Multimap, InetAddress> rangesWithSourceTarget, String keyspace, - IFailureDetector fd, boolean useStrictConsistency) + public static Multimap> getWorkMap(Multimap, InetAddressAndPort> rangesWithSourceTarget, String keyspace, + IFailureDetector fd, boolean useStrictConsistency) { return getRangeFetchMap(rangesWithSourceTarget, Collections.singleton(new FailureDetectorSourceFilter(fd)), keyspace, useStrictConsistency); } // For testing purposes @VisibleForTesting - Multimap>>> toFetch() + Multimap>>> toFetch() { return toFetch; } public StreamResultFuture fetchAsync() { - for (Map.Entry>>> entry : toFetch.entries()) + for (Map.Entry>>> entry : toFetch.entries()) { String keyspace = entry.getKey(); - InetAddress source = entry.getValue().getKey(); - InetAddress preferred = SystemKeyspace.getPreferredIP(source); + InetAddressAndPort source = entry.getValue().getKey(); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(source); Collection> ranges = entry.getValue().getValue(); // filter out already streamed ranges diff --git a/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocation.java b/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocation.java index 7c9d22c7da..61082df534 100644 --- a/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocation.java +++ b/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocation.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.dht.tokenallocator; -import java.net.InetAddress; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -37,6 +36,7 @@ import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.locator.AbstractReplicationStrategy; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.NetworkTopologyStrategy; import org.apache.cassandra.locator.SimpleStrategy; import org.apache.cassandra.locator.TokenMetadata; @@ -48,7 +48,7 @@ public class TokenAllocation public static Collection allocateTokens(final TokenMetadata tokenMetadata, final AbstractReplicationStrategy rs, - final InetAddress endpoint, + final InetAddressAndPort endpoint, int numTokens) { TokenMetadata tokenMetadataCopy = tokenMetadata.cloneOnlyTokenMap(); @@ -81,7 +81,7 @@ public class TokenAllocation { while (tokenMetadata.getEndpoint(t) != null) { - InetAddress other = tokenMetadata.getEndpoint(t); + InetAddressAndPort other = tokenMetadata.getEndpoint(t); if (strategy.inAllocationRing(other)) throw new ConfigurationException(String.format("Allocated token %s already assigned to node %s. Is another node also allocating tokens?", t, other)); t = t.increaseSlightly(); @@ -92,9 +92,9 @@ public class TokenAllocation } // return the ratio of ownership for each endpoint - public static Map evaluateReplicatedOwnership(TokenMetadata tokenMetadata, AbstractReplicationStrategy rs) + public static Map evaluateReplicatedOwnership(TokenMetadata tokenMetadata, AbstractReplicationStrategy rs) { - Map ownership = Maps.newHashMap(); + Map ownership = Maps.newHashMap(); List sortedTokens = tokenMetadata.sortedTokens(); Iterator it = sortedTokens.iterator(); Token current = it.next(); @@ -109,11 +109,11 @@ public class TokenAllocation return ownership; } - static void addOwnership(final TokenMetadata tokenMetadata, final AbstractReplicationStrategy rs, Token current, Token next, Map ownership) + static void addOwnership(final TokenMetadata tokenMetadata, final AbstractReplicationStrategy rs, Token current, Token next, Map ownership) { double size = current.size(next); Token representative = current.getPartitioner().midpoint(current, next); - for (InetAddress n : rs.calculateNaturalEndpoints(representative, tokenMetadata)) + for (InetAddressAndPort n : rs.calculateNaturalEndpoints(representative, tokenMetadata)) { Double v = ownership.get(n); ownership.put(n, v != null ? v + size : size); @@ -126,11 +126,11 @@ public class TokenAllocation } public static SummaryStatistics replicatedOwnershipStats(TokenMetadata tokenMetadata, - AbstractReplicationStrategy rs, InetAddress endpoint) + AbstractReplicationStrategy rs, InetAddressAndPort endpoint) { SummaryStatistics stat = new SummaryStatistics(); StrategyAdapter strategy = getStrategy(tokenMetadata, rs, endpoint); - for (Map.Entry en : evaluateReplicatedOwnership(tokenMetadata, rs).entrySet()) + for (Map.Entry en : evaluateReplicatedOwnership(tokenMetadata, rs).entrySet()) { // Filter only in the same datacentre. if (strategy.inAllocationRing(en.getKey())) @@ -139,10 +139,10 @@ public class TokenAllocation return stat; } - static TokenAllocator create(TokenMetadata tokenMetadata, StrategyAdapter strategy) + static TokenAllocator create(TokenMetadata tokenMetadata, StrategyAdapter strategy) { - NavigableMap sortedTokens = new TreeMap<>(); - for (Map.Entry en : tokenMetadata.getNormalAndBootstrappingTokenToEndpointMap().entrySet()) + NavigableMap sortedTokens = new TreeMap<>(); + for (Map.Entry en : tokenMetadata.getNormalAndBootstrappingTokenToEndpointMap().entrySet()) { if (strategy.inAllocationRing(en.getValue())) sortedTokens.put(en.getKey(), en.getValue()); @@ -150,15 +150,15 @@ public class TokenAllocation return TokenAllocatorFactory.createTokenAllocator(sortedTokens, strategy, tokenMetadata.partitioner); } - interface StrategyAdapter extends ReplicationStrategy + interface StrategyAdapter extends ReplicationStrategy { // return true iff the provided endpoint occurs in the same virtual token-ring we are allocating for // i.e. the set of the nodes that share ownership with the node we are allocating // alternatively: return false if the endpoint's ownership is independent of the node we are allocating tokens for - boolean inAllocationRing(InetAddress other); + boolean inAllocationRing(InetAddressAndPort other); } - static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final AbstractReplicationStrategy rs, final InetAddress endpoint) + static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final AbstractReplicationStrategy rs, final InetAddressAndPort endpoint) { if (rs instanceof NetworkTopologyStrategy) return getStrategy(tokenMetadata, (NetworkTopologyStrategy) rs, rs.snitch, endpoint); @@ -167,7 +167,7 @@ public class TokenAllocation throw new ConfigurationException("Token allocation does not support replication strategy " + rs.getClass().getSimpleName()); } - static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final SimpleStrategy rs, final InetAddress endpoint) + static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final SimpleStrategy rs, final InetAddressAndPort endpoint) { final int replicas = rs.getReplicationFactor(); @@ -180,20 +180,20 @@ public class TokenAllocation } @Override - public Object getGroup(InetAddress unit) + public Object getGroup(InetAddressAndPort unit) { return unit; } @Override - public boolean inAllocationRing(InetAddress other) + public boolean inAllocationRing(InetAddressAndPort other) { return true; } }; } - static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final NetworkTopologyStrategy rs, final IEndpointSnitch snitch, final InetAddress endpoint) + static StrategyAdapter getStrategy(final TokenMetadata tokenMetadata, final NetworkTopologyStrategy rs, final IEndpointSnitch snitch, final InetAddressAndPort endpoint) { final String dc = snitch.getDatacenter(endpoint); final int replicas = rs.getReplicationFactor(dc); @@ -210,13 +210,13 @@ public class TokenAllocation } @Override - public Object getGroup(InetAddress unit) + public Object getGroup(InetAddressAndPort unit) { return unit; } @Override - public boolean inAllocationRing(InetAddress other) + public boolean inAllocationRing(InetAddressAndPort other) { return dc.equals(snitch.getDatacenter(other)); } @@ -237,13 +237,13 @@ public class TokenAllocation } @Override - public Object getGroup(InetAddress unit) + public Object getGroup(InetAddressAndPort unit) { return snitch.getRack(unit); } @Override - public boolean inAllocationRing(InetAddress other) + public boolean inAllocationRing(InetAddressAndPort other) { return dc.equals(snitch.getDatacenter(other)); } @@ -261,13 +261,13 @@ public class TokenAllocation } @Override - public Object getGroup(InetAddress unit) + public Object getGroup(InetAddressAndPort unit) { return unit; } @Override - public boolean inAllocationRing(InetAddress other) + public boolean inAllocationRing(InetAddressAndPort other) { return dc.equals(snitch.getDatacenter(other)); } diff --git a/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocatorFactory.java b/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocatorFactory.java index 58acb564d4..5fdba02714 100644 --- a/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocatorFactory.java +++ b/src/java/org/apache/cassandra/dht/tokenallocator/TokenAllocatorFactory.java @@ -18,7 +18,6 @@ package org.apache.cassandra.dht.tokenallocator; -import java.net.InetAddress; import java.util.NavigableMap; import org.slf4j.Logger; @@ -26,13 +25,14 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; public class TokenAllocatorFactory { private static final Logger logger = LoggerFactory.getLogger(TokenAllocatorFactory.class); - public static TokenAllocator createTokenAllocator(NavigableMap sortedTokens, - ReplicationStrategy strategy, - IPartitioner partitioner) + public static TokenAllocator createTokenAllocator(NavigableMap sortedTokens, + ReplicationStrategy strategy, + IPartitioner partitioner) { if(strategy.replicas() == 1) { diff --git a/src/java/org/apache/cassandra/exceptions/ReadFailureException.java b/src/java/org/apache/cassandra/exceptions/ReadFailureException.java index 82885e30c7..72242fdb88 100644 --- a/src/java/org/apache/cassandra/exceptions/ReadFailureException.java +++ b/src/java/org/apache/cassandra/exceptions/ReadFailureException.java @@ -17,16 +17,16 @@ */ package org.apache.cassandra.exceptions; -import java.net.InetAddress; import java.util.Map; import org.apache.cassandra.db.ConsistencyLevel; +import org.apache.cassandra.locator.InetAddressAndPort; public class ReadFailureException extends RequestFailureException { public final boolean dataPresent; - public ReadFailureException(ConsistencyLevel consistency, int received, int blockFor, boolean dataPresent, Map failureReasonByEndpoint) + public ReadFailureException(ConsistencyLevel consistency, int received, int blockFor, boolean dataPresent, Map failureReasonByEndpoint) { super(ExceptionCode.READ_FAILURE, consistency, received, blockFor, failureReasonByEndpoint); this.dataPresent = dataPresent; diff --git a/src/java/org/apache/cassandra/exceptions/RequestFailureException.java b/src/java/org/apache/cassandra/exceptions/RequestFailureException.java index 1a5289cdfc..2b57a75174 100644 --- a/src/java/org/apache/cassandra/exceptions/RequestFailureException.java +++ b/src/java/org/apache/cassandra/exceptions/RequestFailureException.java @@ -17,20 +17,20 @@ */ package org.apache.cassandra.exceptions; -import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import org.apache.cassandra.db.ConsistencyLevel; +import org.apache.cassandra.locator.InetAddressAndPort; public class RequestFailureException extends RequestExecutionException { public final ConsistencyLevel consistency; public final int received; public final int blockFor; - public final Map failureReasonByEndpoint; + public final Map failureReasonByEndpoint; - protected RequestFailureException(ExceptionCode code, ConsistencyLevel consistency, int received, int blockFor, Map failureReasonByEndpoint) + protected RequestFailureException(ExceptionCode code, ConsistencyLevel consistency, int received, int blockFor, Map failureReasonByEndpoint) { super(code, String.format("Operation failed - received %d responses and %d failures", received, failureReasonByEndpoint.size())); this.consistency = consistency; diff --git a/src/java/org/apache/cassandra/exceptions/WriteFailureException.java b/src/java/org/apache/cassandra/exceptions/WriteFailureException.java index 1a857fe013..a7dc66aed5 100644 --- a/src/java/org/apache/cassandra/exceptions/WriteFailureException.java +++ b/src/java/org/apache/cassandra/exceptions/WriteFailureException.java @@ -17,17 +17,17 @@ */ package org.apache.cassandra.exceptions; -import java.net.InetAddress; import java.util.Map; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.WriteType; +import org.apache.cassandra.locator.InetAddressAndPort; public class WriteFailureException extends RequestFailureException { public final WriteType writeType; - public WriteFailureException(ConsistencyLevel consistency, int received, int blockFor, WriteType writeType, Map failureReasonByEndpoint) + public WriteFailureException(ConsistencyLevel consistency, int received, int blockFor, WriteType writeType, Map failureReasonByEndpoint) { super(ExceptionCode.WRITE_FAILURE, consistency, received, blockFor, failureReasonByEndpoint); this.writeType = writeType; diff --git a/src/java/org/apache/cassandra/gms/ApplicationState.java b/src/java/org/apache/cassandra/gms/ApplicationState.java index ade9208e05..211387d393 100644 --- a/src/java/org/apache/cassandra/gms/ApplicationState.java +++ b/src/java/org/apache/cassandra/gms/ApplicationState.java @@ -19,15 +19,15 @@ package org.apache.cassandra.gms; public enum ApplicationState { - STATUS, + @Deprecated STATUS, //Deprecated and unsued in 4.0, stop publishing in 5.0, reclaim in 6.0 LOAD, SCHEMA, DC, RACK, RELEASE_VERSION, REMOVAL_COORDINATOR, - INTERNAL_IP, - RPC_ADDRESS, + @Deprecated INTERNAL_IP, //Deprecated and unused in 4.0, stop publishing in 5.0, reclaim in 6.0 + @Deprecated RPC_ADDRESS, // ^ Same X_11_PADDING, // padding specifically for 1.1 SEVERITY, NET_VERSION, @@ -35,8 +35,9 @@ public enum ApplicationState TOKENS, RPC_READY, // pad to allow adding new states to existing cluster - X1, - X2, + INTERNAL_ADDRESS_AND_PORT, //Replacement for INTERNAL_IP with up to two ports + NATIVE_ADDRESS_AND_PORT, //Replacement for RPC_ADDRESS + STATUS_WITH_PORT, //Replacement for STATUS X3, X4, X5, diff --git a/src/java/org/apache/cassandra/gms/EndpointState.java b/src/java/org/apache/cassandra/gms/EndpointState.java index 847041fca6..10854471b3 100644 --- a/src/java/org/apache/cassandra/gms/EndpointState.java +++ b/src/java/org/apache/cassandra/gms/EndpointState.java @@ -146,9 +146,15 @@ public class EndpointState public String getStatus() { - VersionedValue status = getApplicationState(ApplicationState.STATUS); + VersionedValue status = getApplicationState(ApplicationState.STATUS_WITH_PORT); if (status == null) + { + status = getApplicationState(ApplicationState.STATUS); + } + if (status == null) + { return ""; + } String[] pieces = status.value.split(VersionedValue.DELIMITER_STR, -1); assert (pieces.length > 0); diff --git a/src/java/org/apache/cassandra/gms/FailureDetector.java b/src/java/org/apache/cassandra/gms/FailureDetector.java index b89508268a..e567b7b3fe 100644 --- a/src/java/org/apache/cassandra/gms/FailureDetector.java +++ b/src/java/org/apache/cassandra/gms/FailureDetector.java @@ -22,7 +22,6 @@ import java.nio.file.StandardOpenOption; import java.nio.file.Path; import java.io.*; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -38,7 +37,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.io.FSWriteError; -import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.Clock; import org.apache.cassandra.utils.FBUtilities; @@ -79,7 +78,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean // change. private final double PHI_FACTOR = 1.0 / Math.log(10.0); // 0.434... - private final ConcurrentHashMap arrivalSamples = new ConcurrentHashMap<>(); + private final ConcurrentHashMap arrivalSamples = new ConcurrentHashMap<>(); private final List fdEvntListeners = new CopyOnWriteArrayList<>(); public FailureDetector() @@ -111,25 +110,45 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean } public String getAllEndpointStates() + { + return getAllEndpointStates(false); + } + + public String getAllEndpointStatesWithPort() + { + return getAllEndpointStates(true); + } + + public String getAllEndpointStates(boolean withPort) { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) + for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) { - sb.append(entry.getKey()).append("\n"); + sb.append(entry.getKey().toString(withPort)).append("\n"); appendEndpointState(sb, entry.getValue()); } return sb.toString(); } public Map getSimpleStates() + { + return getSimpleStates(false); + } + + public Map getSimpleStatesWithPort() + { + return getSimpleStates(true); + } + + private Map getSimpleStates(boolean withPort) { Map nodesStatus = new HashMap(Gossiper.instance.endpointStateMap.size()); - for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) + for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) { if (entry.getValue().isAlive()) - nodesStatus.put(entry.getKey().toString(), "UP"); + nodesStatus.put(entry.getKey().toString(withPort), "UP"); else - nodesStatus.put(entry.getKey().toString(), "DOWN"); + nodesStatus.put(entry.getKey().toString(withPort), "DOWN"); } return nodesStatus; } @@ -137,7 +156,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean public int getDownEndpointCount() { int count = 0; - for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) + for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) { if (!entry.getValue().isAlive()) count++; @@ -148,7 +167,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean public int getUpEndpointCount() { int count = 0; - for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) + for (Map.Entry entry : Gossiper.instance.endpointStateMap.entrySet()) { if (entry.getValue().isAlive()) count++; @@ -158,6 +177,17 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean @Override public TabularData getPhiValues() throws OpenDataException + { + return getPhiValues(false); + } + + @Override + public TabularData getPhiValuesWithPort() throws OpenDataException + { + return getPhiValues(true); + } + + private TabularData getPhiValues(boolean withPort) throws OpenDataException { final CompositeType ct = new CompositeType("Node", "Node", new String[]{"Endpoint", "PHI"}, @@ -165,7 +195,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean new OpenType[]{SimpleType.STRING, SimpleType.DOUBLE}); final TabularDataSupport results = new TabularDataSupport(new TabularType("PhiList", "PhiList", ct, new String[]{"Endpoint"})); - for (final Map.Entry entry : arrivalSamples.entrySet()) + for (final Map.Entry entry : arrivalSamples.entrySet()) { final ArrivalWindow window = entry.getValue(); if (window.mean() > 0) @@ -176,7 +206,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean // returned values are scaled by PHI_FACTOR so that the are on the same scale as PhiConvictThreshold final CompositeData data = new CompositeDataSupport(ct, new String[]{"Endpoint", "PHI"}, - new Object[]{entry.getKey().toString(), phi * PHI_FACTOR}); + new Object[]{entry.getKey().toString(withPort), phi * PHI_FACTOR}); results.put(data); } } @@ -187,7 +217,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean public String getEndpointState(String address) throws UnknownHostException { StringBuilder sb = new StringBuilder(); - EndpointState endpointState = Gossiper.instance.getEndpointStateForEndpoint(InetAddress.getByName(address)); + EndpointState endpointState = Gossiper.instance.getEndpointStateForEndpoint(InetAddressAndPort.getByName(address)); appendEndpointState(sb, endpointState); return sb.toString(); } @@ -243,9 +273,9 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean return DatabaseDescriptor.getPhiConvictThreshold(); } - public boolean isAlive(InetAddress ep) + public boolean isAlive(InetAddressAndPort ep) { - if (ep.equals(FBUtilities.getBroadcastAddress())) + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) return true; EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(ep); @@ -257,7 +287,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean return epState != null && epState.isAlive(); } - public void report(InetAddress ep) + public void report(InetAddressAndPort ep) { long now = Clock.instance.nanoTime(); ArrivalWindow heartbeatWindow = arrivalSamples.get(ep); @@ -279,7 +309,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean logger.trace("Average for {} is {}ns", ep, heartbeatWindow.mean()); } - public void interpret(InetAddress ep) + public void interpret(InetAddressAndPort ep) { ArrivalWindow hbWnd = arrivalSamples.get(ep); if (hbWnd == null) @@ -324,7 +354,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean } } - public void forceConviction(InetAddress ep) + public void forceConviction(InetAddressAndPort ep) { logger.debug("Forcing conviction of {}", ep); for (IFailureDetectionEventListener listener : fdEvntListeners) @@ -333,7 +363,7 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean } } - public void remove(InetAddress ep) + public void remove(InetAddressAndPort ep) { arrivalSamples.remove(ep); } @@ -351,10 +381,10 @@ public class FailureDetector implements IFailureDetector, FailureDetectorMBean public String toString() { StringBuilder sb = new StringBuilder(); - Set eps = arrivalSamples.keySet(); + Set eps = arrivalSamples.keySet(); sb.append("-----------------------------------------------------------------------"); - for (InetAddress ep : eps) + for (InetAddressAndPort ep : eps) { ArrivalWindow hWnd = arrivalSamples.get(ep); sb.append(ep).append(" : "); @@ -447,7 +477,7 @@ class ArrivalWindow } } - synchronized void add(long value, InetAddress ep) + synchronized void add(long value, InetAddressAndPort ep) { assert tLast >= 0; if (tLast > 0L) diff --git a/src/java/org/apache/cassandra/gms/FailureDetectorMBean.java b/src/java/org/apache/cassandra/gms/FailureDetectorMBean.java index 23fae3a660..6be31b0ee0 100644 --- a/src/java/org/apache/cassandra/gms/FailureDetectorMBean.java +++ b/src/java/org/apache/cassandra/gms/FailureDetectorMBean.java @@ -31,15 +31,18 @@ public interface FailureDetectorMBean public double getPhiConvictThreshold(); - public String getAllEndpointStates(); + @Deprecated public String getAllEndpointStates(); + public String getAllEndpointStatesWithPort(); public String getEndpointState(String address) throws UnknownHostException; - public Map getSimpleStates(); + @Deprecated public Map getSimpleStates(); + public Map getSimpleStatesWithPort(); public int getDownEndpointCount(); public int getUpEndpointCount(); - public TabularData getPhiValues() throws OpenDataException; + @Deprecated public TabularData getPhiValues() throws OpenDataException; + public TabularData getPhiValuesWithPort() throws OpenDataException; } diff --git a/src/java/org/apache/cassandra/gms/GossipDigest.java b/src/java/org/apache/cassandra/gms/GossipDigest.java index 9dfd486922..c7e60c4c34 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigest.java +++ b/src/java/org/apache/cassandra/gms/GossipDigest.java @@ -18,12 +18,12 @@ package org.apache.cassandra.gms; import java.io.*; -import java.net.InetAddress; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; /** @@ -34,18 +34,18 @@ public class GossipDigest implements Comparable { public static final IVersionedSerializer serializer = new GossipDigestSerializer(); - final InetAddress endpoint; + final InetAddressAndPort endpoint; final int generation; final int maxVersion; - GossipDigest(InetAddress ep, int gen, int version) + GossipDigest(InetAddressAndPort ep, int gen, int version) { endpoint = ep; generation = gen; maxVersion = version; } - InetAddress getEndpoint() + InetAddressAndPort getEndpoint() { return endpoint; } @@ -83,14 +83,14 @@ class GossipDigestSerializer implements IVersionedSerializer { public void serialize(GossipDigest gDigest, DataOutputPlus out, int version) throws IOException { - CompactEndpointSerializationHelper.serialize(gDigest.endpoint, out); + CompactEndpointSerializationHelper.instance.serialize(gDigest.endpoint, out, version); out.writeInt(gDigest.generation); out.writeInt(gDigest.maxVersion); } public GossipDigest deserialize(DataInputPlus in, int version) throws IOException { - InetAddress endpoint = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort endpoint = CompactEndpointSerializationHelper.instance.deserialize(in, version); int generation = in.readInt(); int maxVersion = in.readInt(); return new GossipDigest(endpoint, generation, maxVersion); @@ -98,7 +98,7 @@ class GossipDigestSerializer implements IVersionedSerializer public long serializedSize(GossipDigest gDigest, int version) { - long size = CompactEndpointSerializationHelper.serializedSize(gDigest.endpoint); + long size = CompactEndpointSerializationHelper.instance.serializedSize(gDigest.endpoint, version); size += TypeSizes.sizeof(gDigest.generation); size += TypeSizes.sizeof(gDigest.maxVersion); return size; diff --git a/src/java/org/apache/cassandra/gms/GossipDigestAck.java b/src/java/org/apache/cassandra/gms/GossipDigestAck.java index cf71ae6cfa..a7d5b92b30 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigestAck.java +++ b/src/java/org/apache/cassandra/gms/GossipDigestAck.java @@ -18,7 +18,6 @@ package org.apache.cassandra.gms; import java.io.IOException; -import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,6 +26,7 @@ import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; /** @@ -38,9 +38,9 @@ public class GossipDigestAck public static final IVersionedSerializer serializer = new GossipDigestAckSerializer(); final List gDigestList; - final Map epStateMap; + final Map epStateMap; - GossipDigestAck(List gDigestList, Map epStateMap) + GossipDigestAck(List gDigestList, Map epStateMap) { this.gDigestList = gDigestList; this.epStateMap = epStateMap; @@ -51,7 +51,7 @@ public class GossipDigestAck return gDigestList; } - Map getEndpointStateMap() + Map getEndpointStateMap() { return epStateMap; } @@ -63,10 +63,10 @@ class GossipDigestAckSerializer implements IVersionedSerializer { GossipDigestSerializationHelper.serialize(gDigestAckMessage.gDigestList, out, version); out.writeInt(gDigestAckMessage.epStateMap.size()); - for (Map.Entry entry : gDigestAckMessage.epStateMap.entrySet()) + for (Map.Entry entry : gDigestAckMessage.epStateMap.entrySet()) { - InetAddress ep = entry.getKey(); - CompactEndpointSerializationHelper.serialize(ep, out); + InetAddressAndPort ep = entry.getKey(); + CompactEndpointSerializationHelper.instance.serialize(ep, out, version); EndpointState.serializer.serialize(entry.getValue(), out, version); } } @@ -75,11 +75,11 @@ class GossipDigestAckSerializer implements IVersionedSerializer { List gDigestList = GossipDigestSerializationHelper.deserialize(in, version); int size = in.readInt(); - Map epStateMap = new HashMap(size); + Map epStateMap = new HashMap(size); for (int i = 0; i < size; ++i) { - InetAddress ep = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort ep = CompactEndpointSerializationHelper.instance.deserialize(in, version); EndpointState epState = EndpointState.serializer.deserialize(in, version); epStateMap.put(ep, epState); } @@ -90,8 +90,8 @@ class GossipDigestAckSerializer implements IVersionedSerializer { int size = GossipDigestSerializationHelper.serializedSize(ack.gDigestList, version); size += TypeSizes.sizeof(ack.epStateMap.size()); - for (Map.Entry entry : ack.epStateMap.entrySet()) - size += CompactEndpointSerializationHelper.serializedSize(entry.getKey()) + for (Map.Entry entry : ack.epStateMap.entrySet()) + size += CompactEndpointSerializationHelper.instance.serializedSize(entry.getKey(), version) + EndpointState.serializer.serializedSize(entry.getValue(), version); return size; } diff --git a/src/java/org/apache/cassandra/gms/GossipDigestAck2.java b/src/java/org/apache/cassandra/gms/GossipDigestAck2.java index 9d779feb62..a6d1d2b196 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigestAck2.java +++ b/src/java/org/apache/cassandra/gms/GossipDigestAck2.java @@ -18,7 +18,6 @@ package org.apache.cassandra.gms; import java.io.*; -import java.net.InetAddress; import java.util.HashMap; import java.util.Map; @@ -26,6 +25,7 @@ import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; /** @@ -36,14 +36,14 @@ public class GossipDigestAck2 { public static final IVersionedSerializer serializer = new GossipDigestAck2Serializer(); - final Map epStateMap; + final Map epStateMap; - GossipDigestAck2(Map epStateMap) + GossipDigestAck2(Map epStateMap) { this.epStateMap = epStateMap; } - Map getEndpointStateMap() + Map getEndpointStateMap() { return epStateMap; } @@ -54,10 +54,10 @@ class GossipDigestAck2Serializer implements IVersionedSerializer entry : ack2.epStateMap.entrySet()) + for (Map.Entry entry : ack2.epStateMap.entrySet()) { - InetAddress ep = entry.getKey(); - CompactEndpointSerializationHelper.serialize(ep, out); + InetAddressAndPort ep = entry.getKey(); + CompactEndpointSerializationHelper.instance.serialize(ep, out, version); EndpointState.serializer.serialize(entry.getValue(), out, version); } } @@ -65,11 +65,11 @@ class GossipDigestAck2Serializer implements IVersionedSerializer epStateMap = new HashMap(size); + Map epStateMap = new HashMap<>(size); for (int i = 0; i < size; ++i) { - InetAddress ep = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort ep = CompactEndpointSerializationHelper.instance.deserialize(in, version); EndpointState epState = EndpointState.serializer.deserialize(in, version); epStateMap.put(ep, epState); } @@ -79,8 +79,8 @@ class GossipDigestAck2Serializer implements IVersionedSerializer entry : ack2.epStateMap.entrySet()) - size += CompactEndpointSerializationHelper.serializedSize(entry.getKey()) + for (Map.Entry entry : ack2.epStateMap.entrySet()) + size += CompactEndpointSerializationHelper.instance.serializedSize(entry.getKey(), version) + EndpointState.serializer.serializedSize(entry.getValue(), version); return size; } diff --git a/src/java/org/apache/cassandra/gms/GossipDigestAck2VerbHandler.java b/src/java/org/apache/cassandra/gms/GossipDigestAck2VerbHandler.java index 240bb40db4..fd5d4876b4 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigestAck2VerbHandler.java +++ b/src/java/org/apache/cassandra/gms/GossipDigestAck2VerbHandler.java @@ -17,12 +17,12 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IVerbHandler; import org.apache.cassandra.net.MessageIn; @@ -34,7 +34,7 @@ public class GossipDigestAck2VerbHandler implements IVerbHandler remoteEpStateMap = message.payload.getEndpointStateMap(); + Map remoteEpStateMap = message.payload.getEndpointStateMap(); /* Notify the Failure Detector */ Gossiper.instance.notifyFailureDetector(remoteEpStateMap); Gossiper.instance.applyStateLocally(remoteEpStateMap); diff --git a/src/java/org/apache/cassandra/gms/GossipDigestAckVerbHandler.java b/src/java/org/apache/cassandra/gms/GossipDigestAckVerbHandler.java index d6d9dfb159..2a12b7c399 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigestAckVerbHandler.java +++ b/src/java/org/apache/cassandra/gms/GossipDigestAckVerbHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,6 +24,7 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IVerbHandler; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessageOut; @@ -36,7 +36,7 @@ public class GossipDigestAckVerbHandler implements IVerbHandler public void doVerb(MessageIn message, int id) { - InetAddress from = message.from; + InetAddressAndPort from = message.from; if (logger.isTraceEnabled()) logger.trace("Received a GossipDigestAckMessage from {}", from); if (!Gossiper.instance.isEnabled() && !Gossiper.instance.isInShadowRound()) @@ -48,7 +48,7 @@ public class GossipDigestAckVerbHandler implements IVerbHandler GossipDigestAck gDigestAckMessage = message.payload; List gDigestList = gDigestAckMessage.getGossipDigestList(); - Map epStateMap = gDigestAckMessage.getEndpointStateMap(); + Map epStateMap = gDigestAckMessage.getEndpointStateMap(); logger.trace("Received ack with {} digests and {} states", gDigestList.size(), epStateMap.size()); if (Gossiper.instance.isInShadowRound()) @@ -79,10 +79,10 @@ public class GossipDigestAckVerbHandler implements IVerbHandler } /* Get the state required to send to this gossipee - construct GossipDigestAck2Message */ - Map deltaEpStateMap = new HashMap(); + Map deltaEpStateMap = new HashMap(); for (GossipDigest gDigest : gDigestList) { - InetAddress addr = gDigest.getEndpoint(); + InetAddressAndPort addr = gDigest.getEndpoint(); EndpointState localEpStatePtr = Gossiper.instance.getStateForVersionBiggerThan(addr, gDigest.getMaxVersion()); if (localEpStatePtr != null) deltaEpStateMap.put(addr, localEpStatePtr); diff --git a/src/java/org/apache/cassandra/gms/GossipDigestSynVerbHandler.java b/src/java/org/apache/cassandra/gms/GossipDigestSynVerbHandler.java index ddfafc9c85..9619f4e107 100644 --- a/src/java/org/apache/cassandra/gms/GossipDigestSynVerbHandler.java +++ b/src/java/org/apache/cassandra/gms/GossipDigestSynVerbHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.util.*; import com.google.common.collect.Maps; @@ -26,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IVerbHandler; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessageOut; @@ -37,7 +37,7 @@ public class GossipDigestSynVerbHandler implements IVerbHandler public void doVerb(MessageIn message, int id) { - InetAddress from = message.from; + InetAddressAndPort from = message.from; if (logger.isTraceEnabled()) logger.trace("Received a GossipDigestSynMessage from {}", from); if (!Gossiper.instance.isEnabled() && !Gossiper.instance.isInShadowRound()) @@ -102,7 +102,7 @@ public class GossipDigestSynVerbHandler implements IVerbHandler doSort(gDigestList); List deltaGossipDigestList = new ArrayList(); - Map deltaEpStateMap = new HashMap(); + Map deltaEpStateMap = new HashMap(); Gossiper.instance.examineGossiper(gDigestList, deltaGossipDigestList, deltaEpStateMap); logger.trace("sending {} digests and {} deltas", deltaGossipDigestList.size(), deltaEpStateMap.size()); MessageOut gDigestAckMessage = new MessageOut(MessagingService.Verb.GOSSIP_DIGEST_ACK, @@ -116,14 +116,14 @@ public class GossipDigestSynVerbHandler implements IVerbHandler /* * First construct a map whose key is the endpoint in the GossipDigest and the value is the * GossipDigest itself. Then build a list of version differences i.e difference between the - * version in the GossipDigest and the version in the local state for a given InetAddress. + * version in the GossipDigest and the version in the local state for a given InetAddressAndPort. * Sort this list. Now loop through the sorted list and retrieve the GossipDigest corresponding * to the endpoint from the map that was initially constructed. */ private void doSort(List gDigestList) { /* Construct a map of endpoint to GossipDigest. */ - Map epToDigestMap = Maps.newHashMapWithExpectedSize(gDigestList.size()); + Map epToDigestMap = Maps.newHashMapWithExpectedSize(gDigestList.size()); for (GossipDigest gDigest : gDigestList) { epToDigestMap.put(gDigest.getEndpoint(), gDigest); @@ -136,7 +136,7 @@ public class GossipDigestSynVerbHandler implements IVerbHandler List diffDigests = new ArrayList(gDigestList.size()); for (GossipDigest gDigest : gDigestList) { - InetAddress ep = gDigest.getEndpoint(); + InetAddressAndPort ep = gDigest.getEndpoint(); EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(ep); int version = (epState != null) ? Gossiper.instance.getMaxEndpointStateVersion(epState) : 0; int diffVersion = Math.abs(version - gDigest.getMaxVersion()); diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java b/src/java/org/apache/cassandra/gms/Gossiper.java index e675d92d59..eb6c500701 100644 --- a/src/java/org/apache/cassandra/gms/Gossiper.java +++ b/src/java/org/apache/cassandra/gms/Gossiper.java @@ -18,12 +18,12 @@ package org.apache.cassandra.gms; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.management.MBeanServer; @@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Uninterruptibles; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.CassandraVersion; import org.apache.cassandra.utils.Pair; import org.slf4j.Logger; @@ -99,43 +100,36 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean static final int MAX_GENERATION_DIFFERENCE = 86400 * 365; private long fatClientTimeout; private final Random random = new Random(); - private final Comparator inetcomparator = new Comparator() - { - public int compare(InetAddress addr1, InetAddress addr2) - { - return addr1.getHostAddress().compareTo(addr2.getHostAddress()); - } - }; /* subscribers for interest in EndpointState change */ private final List subscribers = new CopyOnWriteArrayList(); /* live member set */ - private final Set liveEndpoints = new ConcurrentSkipListSet(inetcomparator); + private final Set liveEndpoints = new ConcurrentSkipListSet<>(); /* unreachable member set */ - private final Map unreachableEndpoints = new ConcurrentHashMap(); + private final Map unreachableEndpoints = new ConcurrentHashMap<>(); /* initial seeds for joining the cluster */ @VisibleForTesting - final Set seeds = new ConcurrentSkipListSet(inetcomparator); + final Set seeds = new ConcurrentSkipListSet<>(); /* map where key is the endpoint and value is the state associated with the endpoint */ - final ConcurrentMap endpointStateMap = new ConcurrentHashMap(); + final ConcurrentMap endpointStateMap = new ConcurrentHashMap<>(); /* map where key is endpoint and value is timestamp when this endpoint was removed from * gossip. We will ignore any gossip regarding these endpoints for QUARANTINE_DELAY time * after removal to prevent nodes from falsely reincarnating during the time when removal * gossip gets propagated to all nodes */ - private final Map justRemovedEndpoints = new ConcurrentHashMap(); + private final Map justRemovedEndpoints = new ConcurrentHashMap<>(); - private final Map expireTimeEndpointMap = new ConcurrentHashMap(); + private final Map expireTimeEndpointMap = new ConcurrentHashMap<>(); private volatile boolean inShadowRound = false; // seeds gathered during shadow round that indicated to be in the shadow round phase as well - private final Set seedsInShadowRound = new ConcurrentSkipListSet<>(inetcomparator); + private final Set seedsInShadowRound = new ConcurrentSkipListSet<>(); // endpoint states as gathered during shadow round - private final Map endpointShadowStateMap = new ConcurrentHashMap<>(); + private final Map endpointShadowStateMap = new ConcurrentHashMap<>(); private volatile long lastProcessedMessageAt = System.currentTimeMillis(); @@ -151,9 +145,9 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean taskLock.lock(); /* Update the local heartbeat counter. */ - endpointStateMap.get(FBUtilities.getBroadcastAddress()).getHeartBeatState().updateHeartBeat(); + endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).getHeartBeatState().updateHeartBeat(); if (logger.isTraceEnabled()) - logger.trace("My heartbeat is now {}", endpointStateMap.get(FBUtilities.getBroadcastAddress()).getHeartBeatState().getHeartBeatVersion()); + logger.trace("My heartbeat is now {}", endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()).getHeartBeatState().getHeartBeatVersion()); final List gDigests = new ArrayList(); Gossiper.instance.makeRandomGossipDigest(gDigests); @@ -231,14 +225,24 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean public boolean seenAnySeed() { - for (Map.Entry entry : endpointStateMap.entrySet()) + for (Map.Entry entry : endpointStateMap.entrySet()) { if (seeds.contains(entry.getKey())) return true; try { VersionedValue internalIp = entry.getValue().getApplicationState(ApplicationState.INTERNAL_IP); - if (internalIp != null && seeds.contains(InetAddress.getByName(internalIp.value))) + VersionedValue internalIpAndPort = entry.getValue().getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT); + InetAddressAndPort endpoint = null; + if (internalIpAndPort != null) + { + endpoint = InetAddressAndPort.getByName(internalIpAndPort.value); + } + else if (internalIp != null) + { + endpoint = InetAddressAndPort.getByName(internalIp.value); + } + if (endpoint != null && seeds.contains(endpoint)) return true; } catch (UnknownHostException e) @@ -272,18 +276,18 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * @return a list of live gossip participants, including fat clients */ - public Set getLiveMembers() + public Set getLiveMembers() { - Set liveMembers = new HashSet<>(liveEndpoints); - if (!liveMembers.contains(FBUtilities.getBroadcastAddress())) - liveMembers.add(FBUtilities.getBroadcastAddress()); + Set liveMembers = new HashSet<>(liveEndpoints); + if (!liveMembers.contains(FBUtilities.getBroadcastAddressAndPort())) + liveMembers.add(FBUtilities.getBroadcastAddressAndPort()); return liveMembers; } /** * @return a list of live ring members. */ - public Set getLiveTokenOwners() + public Set getLiveTokenOwners() { return StorageService.instance.getLiveRingMembers(true); } @@ -291,7 +295,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * @return a list of unreachable gossip participants, including fat clients */ - public Set getUnreachableMembers() + public Set getUnreachableMembers() { return unreachableEndpoints.keySet(); } @@ -299,10 +303,10 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * @return a list of unreachable token owners */ - public Set getUnreachableTokenOwners() + public Set getUnreachableTokenOwners() { - Set tokenOwners = new HashSet<>(); - for (InetAddress endpoint : unreachableEndpoints.keySet()) + Set tokenOwners = new HashSet<>(); + for (InetAddressAndPort endpoint : unreachableEndpoints.keySet()) { if (StorageService.instance.getTokenMetadata().isMember(endpoint)) tokenOwners.add(endpoint); @@ -311,7 +315,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean return tokenOwners; } - public long getEndpointDowntime(InetAddress ep) + public long getEndpointDowntime(InetAddressAndPort ep) { Long downtime = unreachableEndpoints.get(ep); if (downtime != null) @@ -320,14 +324,25 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean return 0L; } - private boolean isShutdown(InetAddress endpoint) + private boolean isShutdown(InetAddressAndPort endpoint) { EndpointState epState = endpointStateMap.get(endpoint); if (epState == null) + { return false; - if (epState.getApplicationState(ApplicationState.STATUS) == null) - return false; - String value = epState.getApplicationState(ApplicationState.STATUS).value; + } + + VersionedValue versionedValue = epState.getApplicationState(ApplicationState.STATUS_WITH_PORT); + if (versionedValue == null) + { + versionedValue = epState.getApplicationState(ApplicationState.STATUS); + if (versionedValue == null) + { + return false; + } + } + + String value = versionedValue.value; String[] pieces = value.split(VersionedValue.DELIMITER_STR, -1); assert (pieces.length > 0); String state = pieces[0]; @@ -340,7 +355,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * * @param endpoint end point that is convicted. */ - public void convict(InetAddress endpoint, double phi) + public void convict(InetAddressAndPort endpoint, double phi) { EndpointState epState = endpointStateMap.get(endpoint); if (epState == null) @@ -366,11 +381,12 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * This method is used to mark a node as shutdown; that is it gracefully exited on its own and told us about it * @param endpoint endpoint that has shut itself down */ - protected void markAsShutdown(InetAddress endpoint) + protected void markAsShutdown(InetAddressAndPort endpoint) { EndpointState epState = endpointStateMap.get(endpoint); if (epState == null) return; + epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.shutdown(true)); epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.shutdown(true)); epState.addApplicationState(ApplicationState.RPC_READY, StorageService.instance.valueFactory.rpcReady(false)); epState.getHeartBeatState().forceHighestPossibleVersionUnsafe(); @@ -397,7 +413,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * * @param endpoint endpoint to be removed from the current membership. */ - private void evictFromMembership(InetAddress endpoint) + private void evictFromMembership(InetAddressAndPort endpoint) { unreachableEndpoints.remove(endpoint); endpointStateMap.remove(endpoint); @@ -411,7 +427,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * Removes the endpoint from Gossip but retains endpoint state */ - public void removeEndpoint(InetAddress endpoint) + public void removeEndpoint(InetAddressAndPort endpoint) { // do subscribers first so anything in the subscriber that depends on gossiper state won't get confused for (IEndpointStateChangeSubscriber subscriber : subscribers) @@ -438,7 +454,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * * @param endpoint */ - private void quarantineEndpoint(InetAddress endpoint) + private void quarantineEndpoint(InetAddressAndPort endpoint) { quarantineEndpoint(endpoint, System.currentTimeMillis()); } @@ -449,7 +465,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param endpoint * @param quarantineExpiration */ - private void quarantineEndpoint(InetAddress endpoint, long quarantineExpiration) + private void quarantineEndpoint(InetAddressAndPort endpoint, long quarantineExpiration) { justRemovedEndpoints.put(endpoint, quarantineExpiration); } @@ -458,7 +474,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * Quarantine endpoint specifically for replacement purposes. * @param endpoint */ - public void replacementQuarantine(InetAddress endpoint) + public void replacementQuarantine(InetAddressAndPort endpoint) { // remember, quarantineEndpoint will effectively already add QUARANTINE_DELAY, so this is 2x logger.debug(""); @@ -471,7 +487,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * * @param endpoint The endpoint that has been replaced */ - public void replacedEndpoint(InetAddress endpoint) + public void replacedEndpoint(InetAddressAndPort endpoint) { removeEndpoint(endpoint); evictFromMembership(endpoint); @@ -491,9 +507,9 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean int maxVersion = 0; // local epstate will be part of endpointStateMap - List endpoints = new ArrayList(endpointStateMap.keySet()); + List endpoints = new ArrayList<>(endpointStateMap.keySet()); Collections.shuffle(endpoints, random); - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { epState = endpointStateMap.get(endpoint); if (epState != null) @@ -524,7 +540,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param hostId - the ID of the host being removed * @param localHostId - my own host ID for replication coordination */ - public void advertiseRemoving(InetAddress endpoint, UUID hostId, UUID localHostId) + public void advertiseRemoving(InetAddressAndPort endpoint, UUID hostId, UUID localHostId) { EndpointState epState = endpointStateMap.get(endpoint); // remember this node's generation @@ -541,6 +557,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean epState.updateTimestamp(); // make sure we don't evict it too soon epState.getHeartBeatState().forceNewerGenerationUnsafe(); Map states = new EnumMap<>(ApplicationState.class); + states.put(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removingNonlocal(hostId)); states.put(ApplicationState.STATUS, StorageService.instance.valueFactory.removingNonlocal(hostId)); states.put(ApplicationState.REMOVAL_COORDINATOR, StorageService.instance.valueFactory.removalCoordinator(localHostId)); epState.addApplicationStates(states); @@ -554,12 +571,13 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param endpoint * @param hostId */ - public void advertiseTokenRemoved(InetAddress endpoint, UUID hostId) + public void advertiseTokenRemoved(InetAddressAndPort endpoint, UUID hostId) { EndpointState epState = endpointStateMap.get(endpoint); epState.updateTimestamp(); // make sure we don't evict it too soon epState.getHeartBeatState().forceNewerGenerationUnsafe(); long expireTime = computeExpireTime(); + epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime)); epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.removedNonlocal(hostId, expireTime)); logger.info("Completing removal of {}", endpoint); addExpireTimeForEndpoint(endpoint, expireTime); @@ -584,7 +602,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean */ public void assassinateEndpoint(String address) throws UnknownHostException { - InetAddress endpoint = InetAddress.getByName(address); + InetAddressAndPort endpoint = InetAddressAndPort.getByName(address); EndpointState epState = endpointStateMap.get(endpoint); Collection tokens = null; logger.warn("Assassinating {} via gossip", endpoint); @@ -624,18 +642,20 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } // do not pass go, do not collect 200 dollars, just gtfo - epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.left(tokens, computeExpireTime())); + long expireTime = computeExpireTime(); + epState.addApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.left(tokens, expireTime)); + epState.addApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.left(tokens, expireTime)); handleMajorStateChange(endpoint, epState); Uninterruptibles.sleepUninterruptibly(intervalInMillis * 4, TimeUnit.MILLISECONDS); logger.warn("Finished assassinating {}", endpoint); } - public boolean isKnownEndpoint(InetAddress endpoint) + public boolean isKnownEndpoint(InetAddressAndPort endpoint) { return endpointStateMap.containsKey(endpoint); } - public int getCurrentGenerationNumber(InetAddress endpoint) + public int getCurrentGenerationNumber(InetAddressAndPort endpoint) { return endpointStateMap.get(endpoint).getHeartBeatState().getGeneration(); } @@ -647,16 +667,16 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param epSet a set of endpoint from which a random endpoint is chosen. * @return true if the chosen endpoint is also a seed. */ - private boolean sendGossip(MessageOut message, Set epSet) + private boolean sendGossip(MessageOut message, Set epSet) { - List liveEndpoints = ImmutableList.copyOf(epSet); + List liveEndpoints = ImmutableList.copyOf(epSet); int size = liveEndpoints.size(); if (size < 1) return false; /* Generate a random number from 0 -> size */ int index = (size == 1) ? 0 : random.nextInt(size); - InetAddress to = liveEndpoints.get(index); + InetAddressAndPort to = liveEndpoints.get(index); if (logger.isTraceEnabled()) logger.trace("Sending a GossipDigestSyn to {} ...", to); if (firstSynSendAt == 0) @@ -695,7 +715,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean int size = seeds.size(); if (size > 0) { - if (size == 1 && seeds.contains(FBUtilities.getBroadcastAddress())) + if (size == 1 && seeds.contains(FBUtilities.getBroadcastAddressAndPort())) { return; } @@ -715,7 +735,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } } - public boolean isGossipOnlyMember(InetAddress endpoint) + public boolean isGossipOnlyMember(InetAddressAndPort endpoint) { EndpointState epState = endpointStateMap.get(endpoint); if (epState == null) @@ -740,8 +760,8 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param epStates - endpoint states in the cluster * @return true if it is safe to start the node, false otherwise */ - public boolean isSafeForStartup(InetAddress endpoint, UUID localHostUUID, boolean isBootstrapping, - Map epStates) + public boolean isSafeForStartup(InetAddressAndPort endpoint, UUID localHostUUID, boolean isBootstrapping, + Map epStates) { EndpointState epState = epStates.get(endpoint); // if there's no previous state, or the node was previously removed from the cluster, we're good @@ -792,10 +812,10 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } } - Set eps = endpointStateMap.keySet(); - for (InetAddress endpoint : eps) + Set eps = endpointStateMap.keySet(); + for (InetAddressAndPort endpoint : eps) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) continue; FailureDetector.instance.interpret(endpoint); @@ -829,7 +849,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean if (!justRemovedEndpoints.isEmpty()) { - for (Entry entry : justRemovedEndpoints.entrySet()) + for (Entry entry : justRemovedEndpoints.entrySet()) { if ((now - entry.getValue()) > QUARANTINE_DELAY) { @@ -841,34 +861,34 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } } - protected long getExpireTimeForEndpoint(InetAddress endpoint) + protected long getExpireTimeForEndpoint(InetAddressAndPort endpoint) { /* default expireTime is aVeryLongTime */ Long storedTime = expireTimeEndpointMap.get(endpoint); return storedTime == null ? computeExpireTime() : storedTime; } - public EndpointState getEndpointStateForEndpoint(InetAddress ep) + public EndpointState getEndpointStateForEndpoint(InetAddressAndPort ep) { return endpointStateMap.get(ep); } - public Set> getEndpointStates() + public Set> getEndpointStates() { return endpointStateMap.entrySet(); } - public UUID getHostId(InetAddress endpoint) + public UUID getHostId(InetAddressAndPort endpoint) { return getHostId(endpoint, endpointStateMap); } - public UUID getHostId(InetAddress endpoint, Map epStates) + public UUID getHostId(InetAddressAndPort endpoint, Map epStates) { return UUID.fromString(epStates.get(endpoint).getApplicationState(ApplicationState.HOST_ID).value); } - EndpointState getStateForVersionBiggerThan(InetAddress forEndpoint, int version) + EndpointState getStateForVersionBiggerThan(InetAddressAndPort forEndpoint, int version) { EndpointState epState = endpointStateMap.get(forEndpoint); EndpointState reqdEndpointState = null; @@ -919,7 +939,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * determine which endpoint started up earlier */ - public int compareEndpointStartup(InetAddress addr1, InetAddress addr2) + public int compareEndpointStartup(InetAddressAndPort addr1, InetAddressAndPort addr2) { EndpointState ep1 = getEndpointStateForEndpoint(addr1); EndpointState ep2 = getEndpointStateForEndpoint(addr2); @@ -927,15 +947,15 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean return ep1.getHeartBeatState().getGeneration() - ep2.getHeartBeatState().getGeneration(); } - void notifyFailureDetector(Map remoteEpStateMap) + void notifyFailureDetector(Map remoteEpStateMap) { - for (Entry entry : remoteEpStateMap.entrySet()) + for (Entry entry : remoteEpStateMap.entrySet()) { notifyFailureDetector(entry.getKey(), entry.getValue()); } } - void notifyFailureDetector(InetAddress endpoint, EndpointState remoteEndpointState) + void notifyFailureDetector(InetAddressAndPort endpoint, EndpointState remoteEndpointState) { EndpointState localEndpointState = endpointStateMap.get(endpoint); /* @@ -976,7 +996,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } - private void markAlive(final InetAddress addr, final EndpointState localState) + private void markAlive(final InetAddressAndPort addr, final EndpointState localState) { localState.markDead(); @@ -999,7 +1019,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } @VisibleForTesting - public void realMarkAlive(final InetAddress addr, final EndpointState localState) + public void realMarkAlive(final InetAddressAndPort addr, final EndpointState localState) { if (logger.isTraceEnabled()) logger.trace("marking as alive {}", addr); @@ -1017,7 +1037,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } @VisibleForTesting - public void markDead(InetAddress addr, EndpointState localState) + public void markDead(InetAddressAndPort addr, EndpointState localState) { if (logger.isTraceEnabled()) logger.trace("marking as down {}", addr); @@ -1037,7 +1057,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * @param ep endpoint * @param epState EndpointState for the endpoint */ - private void handleMajorStateChange(InetAddress ep, EndpointState epState) + private void handleMajorStateChange(InetAddressAndPort ep, EndpointState epState) { EndpointState localEpState = endpointStateMap.get(ep); if (!isDeadState(epState)) @@ -1071,7 +1091,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean markAsShutdown(ep); } - public boolean isAlive(InetAddress endpoint) + public boolean isAlive(InetAddressAndPort endpoint) { EndpointState epState = getEndpointStateForEndpoint(endpoint); if (epState == null) @@ -1099,21 +1119,33 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean private static String getGossipStatus(EndpointState epState) { - if (epState == null || epState.getApplicationState(ApplicationState.STATUS) == null) + if (epState == null) + { return ""; + } - String value = epState.getApplicationState(ApplicationState.STATUS).value; + VersionedValue versionedValue = epState.getApplicationState(ApplicationState.STATUS_WITH_PORT); + if (versionedValue == null) + { + versionedValue = epState.getApplicationState(ApplicationState.STATUS); + if (versionedValue == null) + { + return ""; + } + } + + String value = versionedValue.value; String[] pieces = value.split(VersionedValue.DELIMITER_STR, -1); assert (pieces.length > 0); return pieces[0]; } - void applyStateLocally(Map epStateMap) + void applyStateLocally(Map epStateMap) { - for (Entry entry : epStateMap.entrySet()) + for (Entry entry : epStateMap.entrySet()) { - InetAddress ep = entry.getKey(); - if ( ep.equals(FBUtilities.getBroadcastAddress()) && !isInShadowRound()) + InetAddressAndPort ep = entry.getKey(); + if ( ep.equals(FBUtilities.getBroadcastAddressAndPort()) && !isInShadowRound()) continue; if (justRemovedEndpoints.containsKey(ep)) { @@ -1181,7 +1213,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } } - private void applyNewStates(InetAddress addr, EndpointState localState, EndpointState remoteState) + private void applyNewStates(InetAddressAndPort addr, EndpointState localState, EndpointState remoteState) { // don't assert here, since if the node restarts the version will go back to zero int oldVersion = localState.getHeartBeatState().getHeartBeatVersion(); @@ -1194,12 +1226,27 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean assert remoteState.getHeartBeatState().getGeneration() == localState.getHeartBeatState().getGeneration(); localState.addApplicationStates(remoteStates); - for (Entry remoteEntry : remoteStates) + //Filter out pre-4.0 versions of data for more complete 4.0 versions + Set> filtered = remoteStates.stream().filter(entry -> { + switch (entry.getKey()) + { + case INTERNAL_IP: + return remoteState.getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT) == null; + case STATUS: + return remoteState.getApplicationState(ApplicationState.STATUS_WITH_PORT) == null; + case RPC_ADDRESS: + return remoteState.getApplicationState(ApplicationState.NATIVE_ADDRESS_AND_PORT) == null; + default: + return true; + } + }).collect(Collectors.toSet()); + + for (Entry remoteEntry : filtered) doOnChangeNotifications(addr, remoteEntry.getKey(), remoteEntry.getValue()); } // notify that a local application state is going to change (doesn't get triggered for remote changes) - private void doBeforeChangeNotifications(InetAddress addr, EndpointState epState, ApplicationState apState, VersionedValue newValue) + private void doBeforeChangeNotifications(InetAddressAndPort addr, EndpointState epState, ApplicationState apState, VersionedValue newValue) { for (IEndpointStateChangeSubscriber subscriber : subscribers) { @@ -1208,7 +1255,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } // notify that an application state has changed - private void doOnChangeNotifications(InetAddress addr, ApplicationState state, VersionedValue value) + private void doOnChangeNotifications(InetAddressAndPort addr, ApplicationState state, VersionedValue value) { for (IEndpointStateChangeSubscriber subscriber : subscribers) { @@ -1226,7 +1273,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } /* Send all the data with version greater than maxRemoteVersion */ - private void sendAll(GossipDigest gDigest, Map deltaEpStateMap, int maxRemoteVersion) + private void sendAll(GossipDigest gDigest, Map deltaEpStateMap, int maxRemoteVersion) { EndpointState localEpStatePtr = getStateForVersionBiggerThan(gDigest.getEndpoint(), maxRemoteVersion); if (localEpStatePtr != null) @@ -1237,7 +1284,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean This method is used to figure the state that the Gossiper has but Gossipee doesn't. The delta digests and the delta state are built up. */ - void examineGossiper(List gDigestList, List deltaGossipDigestList, Map deltaEpStateMap) + void examineGossiper(List gDigestList, List deltaGossipDigestList, Map deltaEpStateMap) { if (gDigestList.size() == 0) { @@ -1245,7 +1292,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean If this is happening then the node is attempting shadow gossip, and we should reply with everything we know. */ logger.debug("Shadow request received, adding all states"); - for (Map.Entry entry : endpointStateMap.entrySet()) + for (Map.Entry entry : endpointStateMap.entrySet()) { gDigestList.add(new GossipDigest(entry.getKey(), 0, 0)); } @@ -1320,7 +1367,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean buildSeedsList(); /* initialize the heartbeat state for this localEndpoint */ maybeInitializeLocalState(generationNbr); - EndpointState localState = endpointStateMap.get(FBUtilities.getBroadcastAddress()); + EndpointState localState = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()); localState.addApplicationStates(preloadLocalStates); //notify snitches that Gossiper is about to start @@ -1345,14 +1392,14 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * * * Method is synchronized, as we use an in-progress flag to indicate that shadow round must be cleared - * again by calling {@link Gossiper#maybeFinishShadowRound(InetAddress, boolean, Map)}. This will update + * again by calling {@link Gossiper#maybeFinishShadowRound(InetAddressAndPort, boolean, Map)}. This will update * {@link Gossiper#endpointShadowStateMap} with received values, in order to return an immutable copy to the * caller of {@link Gossiper#doShadowRound()}. Therefor only a single shadow round execution is permitted at * the same time. * * @return endpoint states gathered during shadow round or empty map */ - public synchronized Map doShadowRound() + public synchronized Map doShadowRound() { buildSeedsList(); // it may be that the local address is the only entry in the seed @@ -1381,7 +1428,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean { // CASSANDRA-8072, retry at the beginning and every 5 seconds logger.trace("Sending shadow round GOSSIP DIGEST SYN to seeds {}", seeds); - for (InetAddress seed : seeds) + for (InetAddressAndPort seed : seeds) MessagingService.instance().sendOneWay(message, seed); } @@ -1393,8 +1440,8 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean if (slept > StorageService.RING_DELAY) { // if we don't consider ourself to be a seed, fail out - if (!DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddress())) - throw new RuntimeException("Unable to gossip with any seeds"); + if (!DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddressAndPort())) + throw new RuntimeException("Unable to gossip with any seeds " + DatabaseDescriptor.getSeeds() + " and " + FBUtilities.getBroadcastAddressAndPort()); logger.warn("Unable to gossip with any seeds but continuing since node is in its own seed list"); inShadowRound = false; @@ -1413,9 +1460,9 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean @VisibleForTesting void buildSeedsList() { - for (InetAddress seed : DatabaseDescriptor.getSeeds()) + for (InetAddressAndPort seed : DatabaseDescriptor.getSeeds()) { - if (seed.equals(FBUtilities.getBroadcastAddress())) + if (seed.equals(FBUtilities.getBroadcastAddressAndPort())) continue; seeds.add(seed); } @@ -1427,12 +1474,12 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean HeartBeatState hbState = new HeartBeatState(generationNbr); EndpointState localState = new EndpointState(hbState); localState.markAlive(); - endpointStateMap.putIfAbsent(FBUtilities.getBroadcastAddress(), localState); + endpointStateMap.putIfAbsent(FBUtilities.getBroadcastAddressAndPort(), localState); } public void forceNewerGeneration() { - EndpointState epstate = endpointStateMap.get(FBUtilities.getBroadcastAddress()); + EndpointState epstate = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()); epstate.getHeartBeatState().forceNewerGenerationUnsafe(); } @@ -1440,9 +1487,9 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean /** * Add an endpoint we knew about previously, but whose state is unknown */ - public void addSavedEndpoint(InetAddress ep) + public void addSavedEndpoint(InetAddressAndPort ep) { - if (ep.equals(FBUtilities.getBroadcastAddress())) + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) { logger.debug("Attempt to add self as saved endpoint"); return; @@ -1470,8 +1517,8 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean private void addLocalApplicationStateInternal(ApplicationState state, VersionedValue value) { assert taskLock.isHeldByCurrentThread(); - EndpointState epState = endpointStateMap.get(FBUtilities.getBroadcastAddress()); - InetAddress epAddr = FBUtilities.getBroadcastAddress(); + EndpointState epState = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()); + InetAddressAndPort epAddr = FBUtilities.getBroadcastAddressAndPort(); assert epState != null; // Fire "before change" notifications: doBeforeChangeNotifications(epAddr, epState, state, value); @@ -1508,13 +1555,14 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean public void stop() { - EndpointState mystate = endpointStateMap.get(FBUtilities.getBroadcastAddress()); + EndpointState mystate = endpointStateMap.get(FBUtilities.getBroadcastAddressAndPort()); if (mystate != null && !isSilentShutdownState(mystate) && StorageService.instance.isJoined()) { logger.info("Announcing shutdown"); + addLocalApplicationState(ApplicationState.STATUS_WITH_PORT, StorageService.instance.valueFactory.shutdown(true)); addLocalApplicationState(ApplicationState.STATUS, StorageService.instance.valueFactory.shutdown(true)); MessageOut message = new MessageOut(MessagingService.Verb.GOSSIP_SHUTDOWN); - for (InetAddress ep : liveEndpoints) + for (InetAddressAndPort ep : liveEndpoints) MessagingService.instance().sendOneWay(message, ep); Uninterruptibles.sleepUninterruptibly(Integer.getInteger("cassandra.shutdown_announce_in_ms", 2000), TimeUnit.MILLISECONDS); } @@ -1529,7 +1577,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean return (scheduledGossipTask != null) && (!scheduledGossipTask.isCancelled()); } - protected void maybeFinishShadowRound(InetAddress respondent, boolean isInShadowRound, Map epStateMap) + protected void maybeFinishShadowRound(InetAddressAndPort respondent, boolean isInShadowRound, Map epStateMap) { if (inShadowRound) { @@ -1565,7 +1613,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } @VisibleForTesting - public void initializeNodeUnsafe(InetAddress addr, UUID uuid, int generationNbr) + public void initializeNodeUnsafe(InetAddressAndPort addr, UUID uuid, int generationNbr) { HeartBeatState hbState = new HeartBeatState(generationNbr); EndpointState newState = new EndpointState(hbState); @@ -1581,7 +1629,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } @VisibleForTesting - public void injectApplicationState(InetAddress endpoint, ApplicationState state, VersionedValue value) + public void injectApplicationState(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) { EndpointState localState = endpointStateMap.get(endpoint); localState.addApplicationState(state, value); @@ -1589,15 +1637,15 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean public long getEndpointDowntime(String address) throws UnknownHostException { - return getEndpointDowntime(InetAddress.getByName(address)); + return getEndpointDowntime(InetAddressAndPort.getByName(address)); } public int getCurrentGenerationNumber(String address) throws UnknownHostException { - return getCurrentGenerationNumber(InetAddress.getByName(address)); + return getCurrentGenerationNumber(InetAddressAndPort.getByName(address)); } - public void addExpireTimeForEndpoint(InetAddress endpoint, long expireTime) + public void addExpireTimeForEndpoint(InetAddressAndPort endpoint, long expireTime) { if (logger.isDebugEnabled()) { @@ -1612,14 +1660,14 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } @Nullable - public CassandraVersion getReleaseVersion(InetAddress ep) + public CassandraVersion getReleaseVersion(InetAddressAndPort ep) { EndpointState state = getEndpointStateForEndpoint(ep); return state != null ? state.getReleaseVersion() : null; } @Nullable - public UUID getSchemaVersion(InetAddress ep) + public UUID getSchemaVersion(InetAddressAndPort ep) { EndpointState state = getEndpointStateForEndpoint(ep); return state != null ? state.getSchemaVersion() : null; diff --git a/src/java/org/apache/cassandra/gms/IEndpointStateChangeSubscriber.java b/src/java/org/apache/cassandra/gms/IEndpointStateChangeSubscriber.java index 1bfd678cbe..dc81650340 100644 --- a/src/java/org/apache/cassandra/gms/IEndpointStateChangeSubscriber.java +++ b/src/java/org/apache/cassandra/gms/IEndpointStateChangeSubscriber.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * This is called by an instance of the IEndpointStateChangePublisher to notify @@ -36,17 +36,17 @@ public interface IEndpointStateChangeSubscriber * @param endpoint endpoint for which the state change occurred. * @param epState state that actually changed for the above endpoint. */ - public void onJoin(InetAddress endpoint, EndpointState epState); + public void onJoin(InetAddressAndPort endpoint, EndpointState epState); - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue); + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue); - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value); + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value); - public void onAlive(InetAddress endpoint, EndpointState state); + public void onAlive(InetAddressAndPort endpoint, EndpointState state); - public void onDead(InetAddress endpoint, EndpointState state); + public void onDead(InetAddressAndPort endpoint, EndpointState state); - public void onRemove(InetAddress endpoint); + public void onRemove(InetAddressAndPort endpoint); /** * Called whenever a node is restarted. @@ -54,5 +54,5 @@ public interface IEndpointStateChangeSubscriber * previously marked down. It will have only if {@code state.isAlive() == false} * as {@code state} is from before the restarted node is marked up. */ - public void onRestart(InetAddress endpoint, EndpointState state); + public void onRestart(InetAddressAndPort endpoint, EndpointState state); } diff --git a/src/java/org/apache/cassandra/gms/IFailureDetectionEventListener.java b/src/java/org/apache/cassandra/gms/IFailureDetectionEventListener.java index 8b274b6b62..4e0c6632f9 100644 --- a/src/java/org/apache/cassandra/gms/IFailureDetectionEventListener.java +++ b/src/java/org/apache/cassandra/gms/IFailureDetectionEventListener.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Implemented by the Gossiper to convict an endpoint @@ -33,5 +33,5 @@ public interface IFailureDetectionEventListener * @param ep endpoint to be convicted * @param phi the value of phi with with ep was convicted */ - public void convict(InetAddress ep, double phi); + public void convict(InetAddressAndPort ep, double phi); } diff --git a/src/java/org/apache/cassandra/gms/IFailureDetector.java b/src/java/org/apache/cassandra/gms/IFailureDetector.java index a860c7cc3a..62fc97dbba 100644 --- a/src/java/org/apache/cassandra/gms/IFailureDetector.java +++ b/src/java/org/apache/cassandra/gms/IFailureDetector.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.gms; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * An interface that provides an application with the ability @@ -35,7 +35,7 @@ public interface IFailureDetector * @param ep endpoint in question. * @return true if UP and false if DOWN. */ - public boolean isAlive(InetAddress ep); + public boolean isAlive(InetAddressAndPort ep); /** * This method is invoked by any entity wanting to interrogate the status of an endpoint. @@ -44,7 +44,7 @@ public interface IFailureDetector * * param ep endpoint for which we interpret the inter arrival times. */ - public void interpret(InetAddress ep); + public void interpret(InetAddressAndPort ep); /** * This method is invoked by the receiver of the heartbeat. In our case it would be @@ -53,17 +53,17 @@ public interface IFailureDetector * * param ep endpoint being reported. */ - public void report(InetAddress ep); + public void report(InetAddressAndPort ep); /** * remove endpoint from failure detector */ - public void remove(InetAddress ep); + public void remove(InetAddressAndPort ep); /** * force conviction of endpoint in the failure detector */ - public void forceConviction(InetAddress ep); + public void forceConviction(InetAddressAndPort ep); /** * Register interest for Failure Detector events. diff --git a/src/java/org/apache/cassandra/gms/VersionedValue.java b/src/java/org/apache/cassandra/gms/VersionedValue.java index d9c8d0bc50..691f544d5b 100644 --- a/src/java/org/apache/cassandra/gms/VersionedValue.java +++ b/src/java/org/apache/cassandra/gms/VersionedValue.java @@ -32,6 +32,7 @@ import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.utils.FBUtilities; import org.apache.commons.lang3.StringUtils; @@ -133,11 +134,17 @@ public class VersionedValue implements Comparable return new VersionedValue(value.value); } + @Deprecated public VersionedValue bootReplacing(InetAddress oldNode) { return new VersionedValue(versionString(VersionedValue.STATUS_BOOTSTRAPPING_REPLACE, oldNode.getHostAddress())); } + public VersionedValue bootReplacingWithPort(InetAddressAndPort oldNode) + { + return new VersionedValue(versionString(VersionedValue.STATUS_BOOTSTRAPPING_REPLACE, oldNode.toString())); + } + public VersionedValue bootstrapping(Collection tokens) { return new VersionedValue(versionString(VersionedValue.STATUS_BOOTSTRAPPING, @@ -248,6 +255,11 @@ public class VersionedValue implements Comparable return new VersionedValue(endpoint.getHostAddress()); } + public VersionedValue nativeaddressAndPort(InetAddressAndPort address) + { + return new VersionedValue(address.toString()); + } + public VersionedValue releaseVersion() { return new VersionedValue(FBUtilities.getReleaseVersionString()); @@ -263,6 +275,11 @@ public class VersionedValue implements Comparable return new VersionedValue(private_ip); } + public VersionedValue internalAddressAndPort(InetAddressAndPort address) + { + return new VersionedValue(address.toString()); + } + public VersionedValue severity(double value) { return new VersionedValue(String.valueOf(value)); diff --git a/src/java/org/apache/cassandra/hadoop/ConfigHelper.java b/src/java/org/apache/cassandra/hadoop/ConfigHelper.java index 8c0ed1e094..f01197db95 100644 --- a/src/java/org/apache/cassandra/hadoop/ConfigHelper.java +++ b/src/java/org/apache/cassandra/hadoop/ConfigHelper.java @@ -52,11 +52,13 @@ public class ConfigHelper private static final int DEFAULT_RANGE_BATCH_SIZE = 4096; private static final String INPUT_INITIAL_ADDRESS = "cassandra.input.address"; private static final String OUTPUT_INITIAL_ADDRESS = "cassandra.output.address"; + private static final String OUTPUT_INITIAL_PORT = "cassandra.output.port"; private static final String READ_CONSISTENCY_LEVEL = "cassandra.consistencylevel.read"; private static final String WRITE_CONSISTENCY_LEVEL = "cassandra.consistencylevel.write"; private static final String OUTPUT_COMPRESSION_CLASS = "cassandra.output.compression.class"; private static final String OUTPUT_COMPRESSION_CHUNK_LENGTH = "cassandra.output.compression.length"; private static final String OUTPUT_LOCAL_DC_ONLY = "cassandra.output.local.dc.only"; + private static final String DEFAULT_CASSANDRA_NATIVE_PORT = "7000"; private static final Logger logger = LoggerFactory.getLogger(ConfigHelper.class); @@ -349,6 +351,16 @@ public class ConfigHelper return conf.get(OUTPUT_INITIAL_ADDRESS); } + public static void setOutputInitialPort(Configuration conf, Integer port) + { + conf.set(OUTPUT_INITIAL_PORT, port.toString()); + } + + public static Integer getOutputInitialPort(Configuration conf) + { + return Integer.valueOf(conf.get(OUTPUT_INITIAL_PORT, DEFAULT_CASSANDRA_NATIVE_PORT)); + } + public static void setOutputInitialAddress(Configuration conf, String address) { conf.set(OUTPUT_INITIAL_ADDRESS, address); diff --git a/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java b/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java index 0f44e0ceca..204d9ee31f 100644 --- a/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java +++ b/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java @@ -21,11 +21,13 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.*; +import com.google.common.net.HostAndPort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +41,7 @@ import org.apache.cassandra.hadoop.HadoopCompat; import org.apache.cassandra.io.sstable.CQLSSTableWriter; import org.apache.cassandra.io.sstable.SSTableLoader; import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadataRef; import org.apache.cassandra.streaming.StreamState; import org.apache.cassandra.utils.NativeSSTableLoaderClient; @@ -80,7 +83,7 @@ public class CqlBulkRecordWriter extends RecordWriter> protected SSTableLoader loader; protected Progressable progress; protected TaskAttemptContext context; - protected final Set ignores = new HashSet<>(); + protected final Set ignores = new HashSet<>(); private String keyspace; private String table; @@ -139,7 +142,7 @@ public class CqlBulkRecordWriter extends RecordWriter> try { for (String hostToIgnore : CqlBulkOutputFormat.getIgnoreHosts(conf)) - ignores.add(InetAddress.getByName(hostToIgnore)); + ignores.add(InetAddressAndPort.getByName(hostToIgnore)); } catch (UnknownHostException e) { @@ -285,20 +288,23 @@ public class CqlBulkRecordWriter extends RecordWriter> { super(resolveHostAddresses(conf), CqlConfigHelper.getOutputNativePort(conf), + ConfigHelper.getOutputInitialPort(conf), ConfigHelper.getOutputKeyspaceUserName(conf), ConfigHelper.getOutputKeyspacePassword(conf), - CqlConfigHelper.getSSLOptions(conf).orNull()); + CqlConfigHelper.getSSLOptions(conf).orNull(), + CqlConfigHelper.getAllowServerPortDiscovery(conf)); } - private static Collection resolveHostAddresses(Configuration conf) + private static Collection resolveHostAddresses(Configuration conf) { - Set addresses = new HashSet<>(); - + Set addresses = new HashSet<>(); + int port = CqlConfigHelper.getOutputNativePort(conf); for (String host : ConfigHelper.getOutputInitialAddress(conf).split(",")) { try { - addresses.add(InetAddress.getByName(host)); + HostAndPort hap = HostAndPort.fromString(host); + addresses.add(new InetSocketAddress(InetAddress.getByName(hap.getHost()), hap.getPortOrDefault(port))); } catch (UnknownHostException e) { diff --git a/src/java/org/apache/cassandra/hadoop/cql3/CqlConfigHelper.java b/src/java/org/apache/cassandra/hadoop/cql3/CqlConfigHelper.java index f9a6f3ae0f..3a47a728b2 100644 --- a/src/java/org/apache/cassandra/hadoop/cql3/CqlConfigHelper.java +++ b/src/java/org/apache/cassandra/hadoop/cql3/CqlConfigHelper.java @@ -88,6 +88,7 @@ public class CqlConfigHelper private static final String OUTPUT_CQL = "cassandra.output.cql"; private static final String OUTPUT_NATIVE_PORT = "cassandra.output.native.port"; + private static final String ALLOW_SERVER_PORT_DISCOVERY = "cassandra.allowserverportdiscovery"; /** * Set the CQL columns for the input of this job. @@ -651,4 +652,15 @@ public class CqlConfigHelper new SecureRandom()); return ctx; } + + public static void setAllowServerPortDiscovery(Configuration conf, boolean allowServerPortDiscovery) + { + conf.set(ALLOW_SERVER_PORT_DISCOVERY, Boolean.toString(allowServerPortDiscovery)); + } + + public static boolean getAllowServerPortDiscovery(Configuration conf) + { + return Boolean.parseBoolean(conf.get(ALLOW_SERVER_PORT_DISCOVERY, "false")); + } + } diff --git a/src/java/org/apache/cassandra/hints/HintVerbHandler.java b/src/java/org/apache/cassandra/hints/HintVerbHandler.java index 2b92a42ead..cec6f0b2b4 100644 --- a/src/java/org/apache/cassandra/hints/HintVerbHandler.java +++ b/src/java/org/apache/cassandra/hints/HintVerbHandler.java @@ -18,13 +18,13 @@ */ package org.apache.cassandra.hints; -import java.net.InetAddress; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.db.partitions.PartitionUpdate; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IVerbHandler; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessagingService; @@ -47,7 +47,7 @@ public final class HintVerbHandler implements IVerbHandler { UUID hostId = message.payload.hostId; Hint hint = message.payload.hint; - InetAddress address = StorageService.instance.getEndpointForHostId(hostId); + InetAddressAndPort address = StorageService.instance.getEndpointForHostId(hostId); // If we see an unknown table id, it means the table, or one of the tables in the mutation, had been dropped. // In that case there is nothing we can really do, or should do, other than log it go on. @@ -96,7 +96,7 @@ public final class HintVerbHandler implements IVerbHandler } } - private static void reply(int id, InetAddress to) + private static void reply(int id, InetAddressAndPort to) { MessagingService.instance().sendReply(HintResponse.message, id, to); } diff --git a/src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java b/src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java index 58a3e6fdfd..cbbb2125d5 100644 --- a/src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java +++ b/src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java @@ -18,7 +18,6 @@ package org.apache.cassandra.hints; import java.io.File; -import java.net.InetAddress; import java.util.Map; import java.util.UUID; import java.util.concurrent.*; @@ -36,6 +35,7 @@ import org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutor; import org.apache.cassandra.concurrent.NamedThreadFactory; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.io.FSReadError; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.StorageService; /** @@ -50,10 +50,10 @@ final class HintsDispatchExecutor private final File hintsDirectory; private final ExecutorService executor; private final AtomicBoolean isPaused; - private final Predicate isAlive; + private final Predicate isAlive; private final Map scheduledDispatches; - HintsDispatchExecutor(File hintsDirectory, int maxThreads, AtomicBoolean isPaused, Predicate isAlive) + HintsDispatchExecutor(File hintsDirectory, int maxThreads, AtomicBoolean isPaused, Predicate isAlive) { this.hintsDirectory = hintsDirectory; this.isPaused = isPaused; @@ -154,7 +154,7 @@ final class HintsDispatchExecutor public void run() { UUID hostId = hostIdSupplier.get(); - InetAddress address = StorageService.instance.getEndpointForHostId(hostId); + InetAddressAndPort address = StorageService.instance.getEndpointForHostId(hostId); logger.info("Transferring all hints to {}: {}", address, hostId); if (transfer(hostId)) return; @@ -257,7 +257,7 @@ final class HintsDispatchExecutor { logger.trace("Dispatching hints file {}", descriptor.fileName()); - InetAddress address = StorageService.instance.getEndpointForHostId(hostId); + InetAddressAndPort address = StorageService.instance.getEndpointForHostId(hostId); if (address != null) return deliver(descriptor, address); @@ -266,7 +266,7 @@ final class HintsDispatchExecutor return true; } - private boolean deliver(HintsDescriptor descriptor, InetAddress address) + private boolean deliver(HintsDescriptor descriptor, InetAddressAndPort address) { File file = new File(hintsDirectory, descriptor.fileName()); InputPosition offset = store.getDispatchOffset(descriptor); diff --git a/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java b/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java index 34d1eb26ef..ca38c0c319 100644 --- a/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java +++ b/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java @@ -23,7 +23,7 @@ import org.apache.cassandra.gms.ApplicationState; import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.schema.Schema; -import static org.apache.cassandra.utils.FBUtilities.getBroadcastAddress; +import static org.apache.cassandra.utils.FBUtilities.getBroadcastAddressAndPort; /** * A simple dispatch trigger that's being run every 10 seconds. diff --git a/src/java/org/apache/cassandra/hints/HintsDispatcher.java b/src/java/org/apache/cassandra/hints/HintsDispatcher.java index 323eeb126e..d0d9aace2a 100644 --- a/src/java/org/apache/cassandra/hints/HintsDispatcher.java +++ b/src/java/org/apache/cassandra/hints/HintsDispatcher.java @@ -18,7 +18,6 @@ package org.apache.cassandra.hints; import java.io.File; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.TimeUnit; @@ -31,6 +30,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.db.monitoring.ApproximateTime; import org.apache.cassandra.exceptions.RequestFailureReason; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.HintsServiceMetrics; import org.apache.cassandra.net.IAsyncCallbackWithFailure; import org.apache.cassandra.net.MessageIn; @@ -51,13 +51,13 @@ final class HintsDispatcher implements AutoCloseable private final HintsReader reader; private final UUID hostId; - private final InetAddress address; + private final InetAddressAndPort address; private final int messagingVersion; private final BooleanSupplier abortRequested; private InputPosition currentPagePosition; - private HintsDispatcher(HintsReader reader, UUID hostId, InetAddress address, int messagingVersion, BooleanSupplier abortRequested) + private HintsDispatcher(HintsReader reader, UUID hostId, InetAddressAndPort address, int messagingVersion, BooleanSupplier abortRequested) { currentPagePosition = null; @@ -68,7 +68,7 @@ final class HintsDispatcher implements AutoCloseable this.abortRequested = abortRequested; } - static HintsDispatcher create(File file, RateLimiter rateLimiter, InetAddress address, UUID hostId, BooleanSupplier abortRequested) + static HintsDispatcher create(File file, RateLimiter rateLimiter, InetAddressAndPort address, UUID hostId, BooleanSupplier abortRequested) { int messagingVersion = MessagingService.instance().getVersion(address); return new HintsDispatcher(HintsReader.open(file, rateLimiter), hostId, address, messagingVersion, abortRequested); @@ -228,7 +228,7 @@ final class HintsDispatcher implements AutoCloseable return timedOut ? Outcome.TIMEOUT : outcome; } - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { outcome = Outcome.FAILURE; condition.signalAll(); diff --git a/src/java/org/apache/cassandra/hints/HintsService.java b/src/java/org/apache/cassandra/hints/HintsService.java index 3d82c02c4e..5c331d0647 100644 --- a/src/java/org/apache/cassandra/hints/HintsService.java +++ b/src/java/org/apache/cassandra/hints/HintsService.java @@ -19,7 +19,6 @@ package org.apache.cassandra.hints; import java.io.File; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.UUID; @@ -40,6 +39,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.ParameterizedClass; import org.apache.cassandra.gms.FailureDetector; import org.apache.cassandra.gms.IFailureDetector; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.HintedHandoffMetrics; import org.apache.cassandra.metrics.StorageMetrics; import org.apache.cassandra.dht.Token; @@ -267,10 +267,10 @@ public final class HintsService implements HintsServiceMBean */ public void deleteAllHintsForEndpoint(String address) { - InetAddress target; + InetAddressAndPort target; try { - target = InetAddress.getByName(address); + target = InetAddressAndPort.getByName(address); } catch (UnknownHostException e) { @@ -284,7 +284,7 @@ public final class HintsService implements HintsServiceMBean * * @param target inet address of the target node */ - public void deleteAllHintsForEndpoint(InetAddress target) + public void deleteAllHintsForEndpoint(InetAddressAndPort target) { UUID hostId = StorageService.instance.getHostIdForEndpoint(target); if (hostId == null) diff --git a/src/java/org/apache/cassandra/hints/HintsStore.java b/src/java/org/apache/cassandra/hints/HintsStore.java index 3572172a05..bbf57f5d35 100644 --- a/src/java/org/apache/cassandra/hints/HintsStore.java +++ b/src/java/org/apache/cassandra/hints/HintsStore.java @@ -19,7 +19,6 @@ package org.apache.cassandra.hints; import java.io.File; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; @@ -31,6 +30,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.gms.FailureDetector; import org.apache.cassandra.io.FSWriteError; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.SyncUtil; @@ -77,14 +77,14 @@ final class HintsStore return new HintsStore(hostId, hintsDirectory, writerParams, descriptors); } - InetAddress address() + InetAddressAndPort address() { return StorageService.instance.getEndpointForHostId(hostId); } boolean isLive() { - InetAddress address = address(); + InetAddressAndPort address = address(); return address != null && FailureDetector.instance.isAlive(address); } diff --git a/src/java/org/apache/cassandra/io/DummyByteVersionedSerializer.java b/src/java/org/apache/cassandra/io/DummyByteVersionedSerializer.java new file mode 100644 index 0000000000..d82ff7d7da --- /dev/null +++ b/src/java/org/apache/cassandra/io/DummyByteVersionedSerializer.java @@ -0,0 +1,55 @@ +/* + * 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.cassandra.io; + +import java.io.IOException; + +import com.google.common.base.Preconditions; + +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.net.MessagingService; + +/** + * Serializes a dummy byte that can't be set. Will always write 0 and return 0 in a correctly formed message. + */ +public class DummyByteVersionedSerializer implements IVersionedSerializer +{ + public static final DummyByteVersionedSerializer instance = new DummyByteVersionedSerializer(); + + private DummyByteVersionedSerializer() {} + + public void serialize(byte[] bytes, DataOutputPlus out, int version) throws IOException + { + Preconditions.checkArgument(bytes == MessagingService.ONE_BYTE); + out.write(0); + } + + public byte[] deserialize(DataInputPlus in, int version) throws IOException + { + assert(0 == in.readByte()); + return MessagingService.ONE_BYTE; + } + + public long serializedSize(byte[] bytes, int version) + { + //Payload + return 1; + } +} diff --git a/src/java/org/apache/cassandra/io/ShortVersionedSerializer.java b/src/java/org/apache/cassandra/io/ShortVersionedSerializer.java new file mode 100644 index 0000000000..8731f4c94f --- /dev/null +++ b/src/java/org/apache/cassandra/io/ShortVersionedSerializer.java @@ -0,0 +1,47 @@ +/* + * 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.cassandra.io; + +import java.io.IOException; + +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; + +public class ShortVersionedSerializer implements IVersionedSerializer +{ + + public static final ShortVersionedSerializer instance = new ShortVersionedSerializer(); + + private ShortVersionedSerializer() {} + + public void serialize(Short aShort, DataOutputPlus out, int version) throws IOException + { + out.writeShort(aShort); + } + + public Short deserialize(DataInputPlus in, int version) throws IOException + { + return in.readShort(); + } + + public long serializedSize(Short aShort, int version) + { + return 2; + } +} diff --git a/src/java/org/apache/cassandra/io/sstable/SSTableLoader.java b/src/java/org/apache/cassandra/io/sstable/SSTableLoader.java index 9fb3059cce..7d77ad5aae 100644 --- a/src/java/org/apache/cassandra/io/sstable/SSTableLoader.java +++ b/src/java/org/apache/cassandra/io/sstable/SSTableLoader.java @@ -19,12 +19,12 @@ package org.apache.cassandra.io.sstable; import java.io.File; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.io.FSError; import org.apache.cassandra.schema.TableMetadataRef; import org.apache.cassandra.db.Directories; @@ -32,7 +32,6 @@ import org.apache.cassandra.db.lifecycle.LifecycleTransaction; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.sstable.format.SSTableReader; -import org.apache.cassandra.service.ActiveRepairService; import org.apache.cassandra.streaming.*; import org.apache.cassandra.utils.OutputHandler; import org.apache.cassandra.utils.Pair; @@ -50,10 +49,10 @@ public class SSTableLoader implements StreamEventHandler private final Client client; private final int connectionsPerHost; private final OutputHandler outputHandler; - private final Set failedHosts = new HashSet<>(); + private final Set failedHosts = new HashSet<>(); private final List sstables = new ArrayList<>(); - private final Multimap streamingDetails = HashMultimap.create(); + private final Multimap streamingDetails = HashMultimap.create(); public SSTableLoader(File directory, Client client, OutputHandler outputHandler) { @@ -70,7 +69,7 @@ public class SSTableLoader implements StreamEventHandler } @SuppressWarnings("resource") - protected Collection openSSTables(final Map>> ranges) + protected Collection openSSTables(final Map>> ranges) { outputHandler.output("Opening sstables and calculating sections to stream"); @@ -124,9 +123,9 @@ public class SSTableLoader implements StreamEventHandler // calculate the sstable sections to stream as well as the estimated number of // keys per host - for (Map.Entry>> entry : ranges.entrySet()) + for (Map.Entry>> entry : ranges.entrySet()) { - InetAddress endpoint = entry.getKey(); + InetAddressAndPort endpoint = entry.getKey(); Collection> tokenRanges = entry.getValue(); List> sstableSections = sstable.getPositionsForRanges(tokenRanges); @@ -153,17 +152,17 @@ public class SSTableLoader implements StreamEventHandler public StreamResultFuture stream() { - return stream(Collections.emptySet()); + return stream(Collections.emptySet()); } - public StreamResultFuture stream(Set toIgnore, StreamEventHandler... listeners) + public StreamResultFuture stream(Set toIgnore, StreamEventHandler... listeners) { client.init(keyspace); outputHandler.output("Established connection to initial hosts"); StreamPlan plan = new StreamPlan(StreamOperation.BULK_LOAD, connectionsPerHost, false, false, null, PreviewKind.NONE).connectionFactory(client.getConnectionFactory()); - Map>> endpointToRanges = client.getEndpointToRangesMap(); + Map>> endpointToRanges = client.getEndpointToRangesMap(); openSSTables(endpointToRanges); if (sstables.isEmpty()) { @@ -173,9 +172,9 @@ public class SSTableLoader implements StreamEventHandler outputHandler.output(String.format("Streaming relevant part of %s to %s", names(sstables), endpointToRanges.keySet())); - for (Map.Entry>> entry : endpointToRanges.entrySet()) + for (Map.Entry>> entry : endpointToRanges.entrySet()) { - InetAddress remote = entry.getKey(); + InetAddressAndPort remote = entry.getKey(); if (toIgnore.contains(remote)) continue; @@ -232,14 +231,14 @@ public class SSTableLoader implements StreamEventHandler return builder.toString(); } - public Set getFailedHosts() + public Set getFailedHosts() { return failedHosts; } public static abstract class Client { - private final Map>> endpointToRanges = new HashMap<>(); + private final Map>> endpointToRanges = new HashMap<>(); /** * Initialize the client. @@ -281,12 +280,12 @@ public class SSTableLoader implements StreamEventHandler throw new RuntimeException(); } - public Map>> getEndpointToRangesMap() + public Map>> getEndpointToRangesMap() { return endpointToRanges; } - protected void addRangeForEndpoint(Range range, InetAddress endpoint) + protected void addRangeForEndpoint(Range range, InetAddressAndPort endpoint) { Collection> ranges = endpointToRanges.get(endpoint); if (ranges == null) diff --git a/src/java/org/apache/cassandra/locator/AbstractEndpointSnitch.java b/src/java/org/apache/cassandra/locator/AbstractEndpointSnitch.java index 546d15e7ac..2ee8eea6b1 100644 --- a/src/java/org/apache/cassandra/locator/AbstractEndpointSnitch.java +++ b/src/java/org/apache/cassandra/locator/AbstractEndpointSnitch.java @@ -17,14 +17,13 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.*; import org.apache.cassandra.config.DatabaseDescriptor; public abstract class AbstractEndpointSnitch implements IEndpointSnitch { - public abstract int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2); + public abstract int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2); /** * Sorts the Collection of node addresses by proximity to the given address @@ -32,9 +31,9 @@ public abstract class AbstractEndpointSnitch implements IEndpointSnitch * @param unsortedAddress the nodes to sort * @return a new sorted List */ - public List getSortedListByProximity(InetAddress address, Collection unsortedAddress) + public List getSortedListByProximity(InetAddressAndPort address, Collection unsortedAddress) { - List preferred = new ArrayList(unsortedAddress); + List preferred = new ArrayList<>(unsortedAddress); sortByProximity(address, preferred); return preferred; } @@ -44,11 +43,11 @@ public abstract class AbstractEndpointSnitch implements IEndpointSnitch * @param address the address to sort the proximity by * @param addresses the nodes to sort */ - public void sortByProximity(final InetAddress address, List addresses) + public void sortByProximity(final InetAddressAndPort address, List addresses) { - Collections.sort(addresses, new Comparator() + Collections.sort(addresses, new Comparator() { - public int compare(InetAddress a1, InetAddress a2) + public int compare(InetAddressAndPort a1, InetAddressAndPort a2) { return compareEndpoints(address, a1, a2); } @@ -60,7 +59,7 @@ public abstract class AbstractEndpointSnitch implements IEndpointSnitch // noop by default } - public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) + public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) { // Querying remote DC is likely to be an order of magnitude slower than // querying locally, so 2 queries to local nodes is likely to still be @@ -71,10 +70,10 @@ public abstract class AbstractEndpointSnitch implements IEndpointSnitch : true; } - private boolean hasRemoteNode(List l) + private boolean hasRemoteNode(List l) { String localDc = DatabaseDescriptor.getLocalDataCenter(); - for (InetAddress ep : l) + for (InetAddressAndPort ep : l) { if (!localDc.equals(getDatacenter(ep))) return true; diff --git a/src/java/org/apache/cassandra/locator/AbstractNetworkTopologySnitch.java b/src/java/org/apache/cassandra/locator/AbstractNetworkTopologySnitch.java index b5606d6dc4..e91f6ac207 100644 --- a/src/java/org/apache/cassandra/locator/AbstractNetworkTopologySnitch.java +++ b/src/java/org/apache/cassandra/locator/AbstractNetworkTopologySnitch.java @@ -17,8 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; - /** * An endpoint snitch tells Cassandra information about network topology that it can use to route * requests more efficiently. @@ -30,16 +28,16 @@ public abstract class AbstractNetworkTopologySnitch extends AbstractEndpointSnit * @param endpoint a specified endpoint * @return string of rack */ - abstract public String getRack(InetAddress endpoint); + abstract public String getRack(InetAddressAndPort endpoint); /** * Return the data center for which an endpoint resides in * @param endpoint a specified endpoint * @return string of data center */ - abstract public String getDatacenter(InetAddress endpoint); + abstract public String getDatacenter(InetAddressAndPort endpoint); - public int compareEndpoints(InetAddress address, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort address, InetAddressAndPort a1, InetAddressAndPort a2) { if (address.equals(a1) && !address.equals(a2)) return -1; diff --git a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java index c3498d99d1..3e9b5bb26a 100644 --- a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java +++ b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java @@ -19,7 +19,6 @@ package org.apache.cassandra.locator; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.net.InetAddress; import java.util.*; import com.google.common.annotations.VisibleForTesting; @@ -74,9 +73,9 @@ public abstract class AbstractReplicationStrategy // lazy-initialize keyspace itself since we don't create them until after the replication strategies } - private final Map> cachedEndpoints = new NonBlockingHashMap>(); + private final Map> cachedEndpoints = new NonBlockingHashMap>(); - public ArrayList getCachedEndpoints(Token t) + public ArrayList getCachedEndpoints(Token t) { long lastVersion = tokenMetadata.getRingVersion(); @@ -103,21 +102,21 @@ public abstract class AbstractReplicationStrategy * @param searchPosition the position the natural endpoints are requested for * @return a copy of the natural endpoints for the given token */ - public ArrayList getNaturalEndpoints(RingPosition searchPosition) + public ArrayList getNaturalEndpoints(RingPosition searchPosition) { Token searchToken = searchPosition.getToken(); Token keyToken = TokenMetadata.firstToken(tokenMetadata.sortedTokens(), searchToken); - ArrayList endpoints = getCachedEndpoints(keyToken); + ArrayList endpoints = getCachedEndpoints(keyToken); if (endpoints == null) { TokenMetadata tm = tokenMetadata.cachedOnlyTokenMap(); // if our cache got invalidated, it's possible there is a new token to account for too keyToken = TokenMetadata.firstToken(tm.sortedTokens(), searchToken); - endpoints = new ArrayList(calculateNaturalEndpoints(searchToken, tm)); + endpoints = new ArrayList(calculateNaturalEndpoints(searchToken, tm)); cachedEndpoints.put(keyToken, endpoints); } - return new ArrayList(endpoints); + return new ArrayList(endpoints); } /** @@ -128,10 +127,10 @@ public abstract class AbstractReplicationStrategy * @param searchToken the token the natural endpoints are requested for * @return a copy of the natural endpoints for the given token */ - public abstract List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata); + public abstract List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata); - public AbstractWriteResponseHandler getWriteResponseHandler(Collection naturalEndpoints, - Collection pendingEndpoints, + public AbstractWriteResponseHandler getWriteResponseHandler(Collection naturalEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistency_level, Runnable callback, WriteType writeType, @@ -140,8 +139,8 @@ public abstract class AbstractReplicationStrategy return getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, callback, writeType, queryStartNanoTime, DatabaseDescriptor.getIdealConsistencyLevel()); } - public AbstractWriteResponseHandler getWriteResponseHandler(Collection naturalEndpoints, - Collection pendingEndpoints, + public AbstractWriteResponseHandler getWriteResponseHandler(Collection naturalEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistency_level, Runnable callback, WriteType writeType, @@ -211,14 +210,14 @@ public abstract class AbstractReplicationStrategy * (fixing this would probably require merging tokenmetadata into replicationstrategy, * so we could cache/invalidate cleanly.) */ - public Multimap> getAddressRanges(TokenMetadata metadata) + public Multimap> getAddressRanges(TokenMetadata metadata) { - Multimap> map = HashMultimap.create(); + Multimap> map = HashMultimap.create(); for (Token token : metadata.sortedTokens()) { Range range = metadata.getPrimaryRangeFor(token); - for (InetAddress ep : calculateNaturalEndpoints(token, metadata)) + for (InetAddressAndPort ep : calculateNaturalEndpoints(token, metadata)) { map.put(ep, range); } @@ -227,14 +226,14 @@ public abstract class AbstractReplicationStrategy return map; } - public Multimap, InetAddress> getRangeAddresses(TokenMetadata metadata) + public Multimap, InetAddressAndPort> getRangeAddresses(TokenMetadata metadata) { - Multimap, InetAddress> map = HashMultimap.create(); + Multimap, InetAddressAndPort> map = HashMultimap.create(); for (Token token : metadata.sortedTokens()) { Range range = metadata.getPrimaryRangeFor(token); - for (InetAddress ep : calculateNaturalEndpoints(token, metadata)) + for (InetAddressAndPort ep : calculateNaturalEndpoints(token, metadata)) { map.put(range, ep); } @@ -243,17 +242,17 @@ public abstract class AbstractReplicationStrategy return map; } - public Multimap> getAddressRanges() + public Multimap> getAddressRanges() { return getAddressRanges(tokenMetadata.cloneOnlyTokenMap()); } - public Collection> getPendingAddressRanges(TokenMetadata metadata, Token pendingToken, InetAddress pendingAddress) + public Collection> getPendingAddressRanges(TokenMetadata metadata, Token pendingToken, InetAddressAndPort pendingAddress) { return getPendingAddressRanges(metadata, Arrays.asList(pendingToken), pendingAddress); } - public Collection> getPendingAddressRanges(TokenMetadata metadata, Collection pendingTokens, InetAddress pendingAddress) + public Collection> getPendingAddressRanges(TokenMetadata metadata, Collection pendingTokens, InetAddressAndPort pendingAddress) { TokenMetadata temp = metadata.cloneOnlyTokenMap(); temp.updateNormalTokens(pendingTokens, pendingAddress); diff --git a/src/java/org/apache/cassandra/locator/CloudstackSnitch.java b/src/java/org/apache/cassandra/locator/CloudstackSnitch.java index ec2e87ec17..be6d3c432e 100644 --- a/src/java/org/apache/cassandra/locator/CloudstackSnitch.java +++ b/src/java/org/apache/cassandra/locator/CloudstackSnitch.java @@ -24,7 +24,6 @@ import java.io.FileReader; import java.io.IOException; import java.io.File; import java.net.HttpURLConnection; -import java.net.InetAddress; import java.net.URL; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -56,7 +55,7 @@ public class CloudstackSnitch extends AbstractNetworkTopologySnitch protected static final Logger logger = LoggerFactory.getLogger(CloudstackSnitch.class); protected static final String ZONE_NAME_QUERY_URI = "/latest/meta-data/availability-zone"; - private Map> savedEndpoints; + private Map> savedEndpoints; private static final String DEFAULT_DC = "UNKNOWN-DC"; private static final String DEFAULT_RACK = "UNKNOWN-RACK"; @@ -83,9 +82,9 @@ public class CloudstackSnitch extends AbstractNetworkTopologySnitch csZoneRack = zone_parts[2]; } - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return csZoneRack; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.RACK) == null) @@ -99,9 +98,9 @@ public class CloudstackSnitch extends AbstractNetworkTopologySnitch return state.getApplicationState(ApplicationState.RACK).value; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return csZoneDc; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.DC) == null) diff --git a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java index 42fc26c862..b9c9ba0902 100644 --- a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java +++ b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitch.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import com.codahale.metrics.ExponentiallyDecayingReservoir; import javax.management.MBeanServer; @@ -63,8 +64,8 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa private String mbeanName; private boolean registered = false; - private volatile HashMap scores = new HashMap<>(); - private final ConcurrentHashMap samples = new ConcurrentHashMap<>(); + private volatile HashMap scores = new HashMap<>(); + private final ConcurrentHashMap samples = new ConcurrentHashMap<>(); public final IEndpointSnitch subsnitch; @@ -174,27 +175,27 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa subsnitch.gossiperStarting(); } - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return subsnitch.getRack(endpoint); } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return subsnitch.getDatacenter(endpoint); } - public List getSortedListByProximity(final InetAddress address, Collection addresses) + public List getSortedListByProximity(final InetAddressAndPort address, Collection addresses) { - List list = new ArrayList(addresses); + List list = new ArrayList<>(addresses); sortByProximity(address, list); return list; } @Override - public void sortByProximity(final InetAddress address, List addresses) + public void sortByProximity(final InetAddressAndPort address, List addresses) { - assert address.equals(FBUtilities.getBroadcastAddress()); // we only know about ourself + assert address.equals(FBUtilities.getBroadcastAddressAndPort()); // we only know about ourself if (dynamicBadnessThreshold == 0) { sortByProximityWithScore(address, addresses); @@ -205,32 +206,32 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa } } - private void sortByProximityWithScore(final InetAddress address, List addresses) + private void sortByProximityWithScore(final InetAddressAndPort address, List addresses) { // Scores can change concurrently from a call to this method. But Collections.sort() expects // its comparator to be "stable", that is 2 endpoint should compare the same way for the duration // of the sort() call. As we copy the scores map on write, it is thus enough to alias the current // version of it during this call. - final HashMap scores = this.scores; - Collections.sort(addresses, new Comparator() + final HashMap scores = this.scores; + Collections.sort(addresses, new Comparator() { - public int compare(InetAddress a1, InetAddress a2) + public int compare(InetAddressAndPort a1, InetAddressAndPort a2) { return compareEndpoints(address, a1, a2, scores); } }); } - private void sortByProximityWithBadness(final InetAddress address, List addresses) + private void sortByProximityWithBadness(final InetAddressAndPort address, List addresses) { if (addresses.size() < 2) return; subsnitch.sortByProximity(address, addresses); - HashMap scores = this.scores; // Make sure the score don't change in the middle of the loop below + HashMap scores = this.scores; // Make sure the score don't change in the middle of the loop below // (which wouldn't really matter here but its cleaner that way). ArrayList subsnitchOrderedScores = new ArrayList<>(addresses.size()); - for (InetAddress inet : addresses) + for (InetAddressAndPort inet : addresses) { Double score = scores.get(inet); if (score == null) @@ -256,7 +257,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa } // Compare endpoints given an immutable snapshot of the scores - private int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2, Map scores) + private int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2, Map scores) { Double scored1 = scores.get(a1); Double scored2 = scores.get(a2); @@ -279,7 +280,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa return 1; } - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { // That function is fundamentally unsafe because the scores can change at any time and so the result of that // method is not stable for identical arguments. This is why we don't rely on super.sortByProximity() in @@ -287,7 +288,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa throw new UnsupportedOperationException("You shouldn't wrap the DynamicEndpointSnitch (within itself or otherwise)"); } - public void receiveTiming(InetAddress host, long latency) // this is cheap + public void receiveTiming(InetAddressAndPort host, long latency) // this is cheap { ExponentiallyDecayingReservoir sample = samples.get(host); if (sample == null) @@ -315,23 +316,23 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa } double maxLatency = 1; - Map snapshots = new HashMap<>(samples.size()); - for (Map.Entry entry : samples.entrySet()) + Map snapshots = new HashMap<>(samples.size()); + for (Map.Entry entry : samples.entrySet()) { snapshots.put(entry.getKey(), entry.getValue().getSnapshot()); } // We're going to weight the latency for each host against the worst one we see, to // arrive at sort of a 'badness percentage' for them. First, find the worst for each: - HashMap newScores = new HashMap<>(); - for (Map.Entry entry : snapshots.entrySet()) + HashMap newScores = new HashMap<>(); + for (Map.Entry entry : snapshots.entrySet()) { double mean = entry.getValue().getMedian(); if (mean > maxLatency) maxLatency = mean; } // now make another pass to do the weighting based on the maximums we found before - for (Map.Entry entry : snapshots.entrySet()) + for (Map.Entry entry : snapshots.entrySet()) { double score = entry.getValue().getMedian() / maxLatency; // finally, add the severity without any weighting, since hosts scale this relative to their own load and the size of the task causing the severity. @@ -350,6 +351,11 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa } public Map getScores() + { + return scores.entrySet().stream().collect(Collectors.toMap(address -> address.getKey().address, Map.Entry::getValue)); + } + + public Map getScoresWithPort() { return scores; } @@ -374,7 +380,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa public List dumpTimings(String hostname) throws UnknownHostException { - InetAddress host = InetAddress.getByName(hostname); + InetAddressAndPort host = InetAddressAndPort.getByName(hostname); ArrayList timings = new ArrayList(); ExponentiallyDecayingReservoir sample = samples.get(host); if (sample != null) @@ -390,7 +396,7 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa Gossiper.instance.addLocalApplicationState(ApplicationState.SEVERITY, StorageService.instance.valueFactory.severity(severity)); } - private double getSeverity(InetAddress endpoint) + private double getSeverity(InetAddressAndPort endpoint) { EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null) @@ -405,10 +411,10 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa public double getSeverity() { - return getSeverity(FBUtilities.getBroadcastAddress()); + return getSeverity(FBUtilities.getBroadcastAddressAndPort()); } - public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) + public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) { if (!subsnitch.isWorthMergingForRangeQuery(merged, l1, l2)) return false; @@ -428,10 +434,10 @@ public class DynamicEndpointSnitch extends AbstractEndpointSnitch implements ILa } // Return the max score for the endpoint in the provided list, or -1.0 if no node have a score. - private double maxScore(List endpoints) + private double maxScore(List endpoints) { double maxScore = -1.0; - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { Double score = scores.get(endpoint); if (score == null) diff --git a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitchMBean.java b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitchMBean.java index bfafa756a7..61f0d9779d 100644 --- a/src/java/org/apache/cassandra/locator/DynamicEndpointSnitchMBean.java +++ b/src/java/org/apache/cassandra/locator/DynamicEndpointSnitchMBean.java @@ -24,6 +24,8 @@ import java.util.List; public interface DynamicEndpointSnitchMBean { + public Map getScoresWithPort(); + @Deprecated public Map getScores(); public int getUpdateInterval(); public int getResetInterval(); diff --git a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java index b32ca84e96..2a6c7e9c02 100644 --- a/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java +++ b/src/java/org/apache/cassandra/locator/Ec2MultiRegionSnitch.java @@ -19,6 +19,7 @@ package org.apache.cassandra.locator; import java.io.IOException; import java.net.InetAddress; +import java.net.UnknownHostException; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.config.DatabaseDescriptor; @@ -62,6 +63,16 @@ public class Ec2MultiRegionSnitch extends Ec2Snitch public void gossiperStarting() { super.gossiperStarting(); + InetAddressAndPort address; + try + { + address = InetAddressAndPort.getByName(localPrivateAddress); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + Gossiper.instance.addLocalApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT, StorageService.instance.valueFactory.internalAddressAndPort(address)); Gossiper.instance.addLocalApplicationState(ApplicationState.INTERNAL_IP, StorageService.instance.valueFactory.internalIP(localPrivateAddress)); Gossiper.instance.register(new ReconnectableSnitchHelper(this, ec2region, true)); } diff --git a/src/java/org/apache/cassandra/locator/Ec2Snitch.java b/src/java/org/apache/cassandra/locator/Ec2Snitch.java index 59eb27bb2b..c7324c8ac3 100644 --- a/src/java/org/apache/cassandra/locator/Ec2Snitch.java +++ b/src/java/org/apache/cassandra/locator/Ec2Snitch.java @@ -21,7 +21,6 @@ import java.io.DataInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.InetAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -46,7 +45,7 @@ public class Ec2Snitch extends AbstractNetworkTopologySnitch protected static final String ZONE_NAME_QUERY_URL = "http://169.254.169.254/latest/meta-data/placement/availability-zone"; private static final String DEFAULT_DC = "UNKNOWN-DC"; private static final String DEFAULT_RACK = "UNKNOWN-RACK"; - private Map> savedEndpoints; + private Map> savedEndpoints; protected String ec2zone; protected String ec2region; @@ -92,9 +91,9 @@ public class Ec2Snitch extends AbstractNetworkTopologySnitch } } - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return ec2zone; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.RACK) == null) @@ -108,9 +107,9 @@ public class Ec2Snitch extends AbstractNetworkTopologySnitch return state.getApplicationState(ApplicationState.RACK).value; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return ec2region; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.DC) == null) diff --git a/src/java/org/apache/cassandra/locator/EndpointSnitchInfo.java b/src/java/org/apache/cassandra/locator/EndpointSnitchInfo.java index bbfabb6718..c06d765568 100644 --- a/src/java/org/apache/cassandra/locator/EndpointSnitchInfo.java +++ b/src/java/org/apache/cassandra/locator/EndpointSnitchInfo.java @@ -19,7 +19,6 @@ package org.apache.cassandra.locator; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.net.UnknownHostException; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -44,22 +43,22 @@ public class EndpointSnitchInfo implements EndpointSnitchInfoMBean public String getDatacenter(String host) throws UnknownHostException { - return DatabaseDescriptor.getEndpointSnitch().getDatacenter(InetAddress.getByName(host)); + return DatabaseDescriptor.getEndpointSnitch().getDatacenter(InetAddressAndPort.getByName(host)); } public String getRack(String host) throws UnknownHostException { - return DatabaseDescriptor.getEndpointSnitch().getRack(InetAddress.getByName(host)); + return DatabaseDescriptor.getEndpointSnitch().getRack(InetAddressAndPort.getByName(host)); } public String getDatacenter() { - return DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + return DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); } public String getRack() { - return DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddress()); + return DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddressAndPort()); } public String getSnitchName() diff --git a/src/java/org/apache/cassandra/locator/GoogleCloudSnitch.java b/src/java/org/apache/cassandra/locator/GoogleCloudSnitch.java index b4d3b19931..1e1c500791 100644 --- a/src/java/org/apache/cassandra/locator/GoogleCloudSnitch.java +++ b/src/java/org/apache/cassandra/locator/GoogleCloudSnitch.java @@ -21,7 +21,6 @@ import java.io.DataInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.InetAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -46,7 +45,7 @@ public class GoogleCloudSnitch extends AbstractNetworkTopologySnitch protected static final String ZONE_NAME_QUERY_URL = "http://metadata.google.internal/computeMetadata/v1/instance/zone"; private static final String DEFAULT_DC = "UNKNOWN-DC"; private static final String DEFAULT_RACK = "UNKNOWN-RACK"; - private Map> savedEndpoints; + private Map> savedEndpoints; protected String gceZone; protected String gceRegion; @@ -94,9 +93,9 @@ public class GoogleCloudSnitch extends AbstractNetworkTopologySnitch } } - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return gceZone; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.RACK) == null) @@ -110,9 +109,9 @@ public class GoogleCloudSnitch extends AbstractNetworkTopologySnitch return state.getApplicationState(ApplicationState.RACK).value; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return gceRegion; EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); if (state == null || state.getApplicationState(ApplicationState.DC) == null) diff --git a/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java b/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java index e2449ae346..75b56851f6 100644 --- a/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java +++ b/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java @@ -18,7 +18,6 @@ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.concurrent.atomic.AtomicReference; import java.util.Map; @@ -45,7 +44,7 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// private final boolean preferLocal; private final AtomicReference snitchHelperReference; - private Map> savedEndpoints; + private Map> savedEndpoints; private static final String DEFAULT_DC = "UNKNOWN_DC"; private static final String DEFAULT_RACK = "UNKNOWN_RACK"; @@ -84,9 +83,9 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// * @param endpoint the endpoint to process * @return string of data center */ - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return myDC; EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint); @@ -112,9 +111,9 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// * @param endpoint the endpoint to process * @return string of rack */ - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) return myRack; EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint); @@ -138,8 +137,10 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// { super.gossiperStarting(); + Gossiper.instance.addLocalApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT, + StorageService.instance.valueFactory.internalAddressAndPort(FBUtilities.getLocalAddressAndPort())); Gossiper.instance.addLocalApplicationState(ApplicationState.INTERNAL_IP, - StorageService.instance.valueFactory.internalIP(FBUtilities.getLocalAddress().getHostAddress())); + StorageService.instance.valueFactory.internalIP(FBUtilities.getJustLocalAddress().getHostAddress())); loadGossiperState(); } diff --git a/src/java/org/apache/cassandra/locator/IEndpointSnitch.java b/src/java/org/apache/cassandra/locator/IEndpointSnitch.java index 71b441c15f..00a15436e8 100644 --- a/src/java/org/apache/cassandra/locator/IEndpointSnitch.java +++ b/src/java/org/apache/cassandra/locator/IEndpointSnitch.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.Collection; import java.util.List; @@ -32,27 +31,27 @@ public interface IEndpointSnitch /** * returns a String representing the rack this endpoint belongs to */ - public String getRack(InetAddress endpoint); + public String getRack(InetAddressAndPort endpoint); /** * returns a String representing the datacenter this endpoint belongs to */ - public String getDatacenter(InetAddress endpoint); + public String getDatacenter(InetAddressAndPort endpoint); /** * returns a new List sorted by proximity to the given endpoint */ - public List getSortedListByProximity(InetAddress address, Collection unsortedAddress); + public List getSortedListByProximity(InetAddressAndPort address, Collection unsortedAddress); /** * This method will sort the List by proximity to the given address. */ - public void sortByProximity(InetAddress address, List addresses); + public void sortByProximity(InetAddressAndPort address, List addresses); /** * compares two endpoints in relation to the target endpoint, returning as Comparator.compare would */ - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2); + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2); /** * called after Gossiper instance exists immediately before it starts gossiping @@ -63,5 +62,5 @@ public interface IEndpointSnitch * Returns whether for a range query doing a query against merged is likely * to be faster than 2 sequential queries, one against l1 followed by one against l2. */ - public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2); + public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2); } diff --git a/src/java/org/apache/cassandra/locator/ILatencySubscriber.java b/src/java/org/apache/cassandra/locator/ILatencySubscriber.java index d2ae6db796..f6c1c7f20f 100644 --- a/src/java/org/apache/cassandra/locator/ILatencySubscriber.java +++ b/src/java/org/apache/cassandra/locator/ILatencySubscriber.java @@ -17,9 +17,7 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; - public interface ILatencySubscriber { - public void receiveTiming(InetAddress address, long latency); + public void receiveTiming(InetAddressAndPort address, long latency); } diff --git a/src/java/org/apache/cassandra/locator/InetAddressAndPort.java b/src/java/org/apache/cassandra/locator/InetAddressAndPort.java new file mode 100644 index 0000000000..38a1a49ec8 --- /dev/null +++ b/src/java/org/apache/cassandra/locator/InetAddressAndPort.java @@ -0,0 +1,203 @@ +/* + * 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.cassandra.locator; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.google.common.base.Preconditions; +import com.google.common.net.HostAndPort; + +import org.apache.cassandra.utils.FastByteOperations; + +/** + * A class to replace the usage of InetAddress to identify hosts in the cluster. + * Opting for a full replacement class so that in the future if we change the nature + * of the identifier the refactor will be easier in that we don't have to change the type + * just the methods. + * + * Because an IP might contain multiple C* instances the identification must be done + * using the IP + port. InetSocketAddress is undesirable for a couple of reasons. It's not comparable, + * it's toString() method doesn't correctly bracket IPv6, it doesn't handle optional default values, + * and a couple of other minor behaviors that are slightly less troublesome like handling the + * need to sometimes return a port and sometimes not. + * + */ +public final class InetAddressAndPort implements Comparable, Serializable +{ + private static final long serialVersionUID = 0; + + //Store these here to avoid requiring DatabaseDescriptor to be loaded. DatabaseDescriptor will set + //these when it loads the config. A lot of unit tests won't end up loading DatabaseDescriptor. + //Tools that might use this class also might not load database descriptor. Those tools are expected + //to always override the defaults. + static volatile int defaultPort = 7000; + + public final InetAddress address; + public final byte[] addressBytes; + public final int port; + + private InetAddressAndPort(InetAddress address, byte[] addressBytes, int port) + { + Preconditions.checkNotNull(address); + Preconditions.checkNotNull(addressBytes); + validatePortRange(port); + this.address = address; + this.port = port; + this.addressBytes = addressBytes; + } + + private static void validatePortRange(int port) + { + if (port < 0 | port > 65535) + { + throw new IllegalArgumentException("Port " + port + " is not a valid port number in the range 0-65535"); + } + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InetAddressAndPort that = (InetAddressAndPort) o; + + if (port != that.port) return false; + return address.equals(that.address); + } + + @Override + public int hashCode() + { + int result = address.hashCode(); + result = 31 * result + port; + return result; + } + + @Override + public int compareTo(InetAddressAndPort o) + { + int retval = FastByteOperations.compareUnsigned(addressBytes, 0, addressBytes.length, o.addressBytes, 0, o.addressBytes.length); + if (retval != 0) + { + return retval; + } + + return Integer.compare(port, o.port); + } + + public String getHostAddress(boolean withPort) + { + if (withPort) + { + return toString(); + } + else + { + return address.getHostAddress(); + } + } + + @Override + public String toString() + { + return toString(true); + } + + public String toString(boolean withPort) + { + if (withPort) + { + return HostAndPort.fromParts(address.getHostAddress(), port).toString(); + } + else + { + return address.toString(); + } + } + + public static InetAddressAndPort getByName(String name) throws UnknownHostException + { + return getByNameOverrideDefaults(name, null); + } + + /** + * + * @param name Hostname + optional ports string + * @param port Port to connect on, overridden by values in hostname string, defaults to DatabaseDescriptor default if not specified anywhere. + * @return + * @throws UnknownHostException + */ + public static InetAddressAndPort getByNameOverrideDefaults(String name, Integer port) throws UnknownHostException + { + HostAndPort hap = HostAndPort.fromString(name); + if (hap.hasPort()) + { + port = hap.getPort(); + } + return getByAddressOverrideDefaults(InetAddress.getByName(hap.getHost()), port); + } + + public static InetAddressAndPort getByAddress(byte[] address) throws UnknownHostException + { + return getByAddressOverrideDefaults(InetAddress.getByAddress(address), address, null); + } + + public static InetAddressAndPort getByAddress(InetAddress address) + { + return getByAddressOverrideDefaults(address, null); + } + + public static InetAddressAndPort getByAddressOverrideDefaults(InetAddress address, Integer port) + { + if (port == null) + { + port = defaultPort; + } + + return new InetAddressAndPort(address, address.getAddress(), port); + } + + public static InetAddressAndPort getByAddressOverrideDefaults(InetAddress address, byte[] addressBytes, Integer port) + { + if (port == null) + { + port = defaultPort; + } + + return new InetAddressAndPort(address, addressBytes, port); + } + + public static InetAddressAndPort getLoopbackAddress() + { + return InetAddressAndPort.getByAddress(InetAddress.getLoopbackAddress()); + } + + public static InetAddressAndPort getLocalHost() throws UnknownHostException + { + return InetAddressAndPort.getByAddress(InetAddress.getLocalHost()); + } + + public static void initializeDefaultPort(int port) + { + defaultPort = port; + } +} diff --git a/src/java/org/apache/cassandra/locator/LocalStrategy.java b/src/java/org/apache/cassandra/locator/LocalStrategy.java index ae58203d29..a76fe966ac 100644 --- a/src/java/org/apache/cassandra/locator/LocalStrategy.java +++ b/src/java/org/apache/cassandra/locator/LocalStrategy.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.Collection; @@ -42,16 +41,16 @@ public class LocalStrategy extends AbstractReplicationStrategy * LocalStrategy may be used before tokens are set up. */ @Override - public ArrayList getNaturalEndpoints(RingPosition searchPosition) + public ArrayList getNaturalEndpoints(RingPosition searchPosition) { - ArrayList l = new ArrayList(1); - l.add(FBUtilities.getBroadcastAddress()); + ArrayList l = new ArrayList(1); + l.add(FBUtilities.getBroadcastAddressAndPort()); return l; } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { - return Collections.singletonList(FBUtilities.getBroadcastAddress()); + return Collections.singletonList(FBUtilities.getBroadcastAddressAndPort()); } public int getReplicationFactor() diff --git a/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java b/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java index 442e6cf2bd..673c018fab 100644 --- a/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java +++ b/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.*; import java.util.Map.Entry; @@ -72,7 +71,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy } datacenters = Collections.unmodifiableMap(newDatacenters); - logger.trace("Configured datacenter replicas are {}", FBUtilities.toString(datacenters)); + logger.info("Configured datacenter replicas are {}", FBUtilities.toString(datacenters)); } /** @@ -81,7 +80,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy private static final class DatacenterEndpoints { /** List accepted endpoints get pushed into. */ - Set endpoints; + Set endpoints; /** * Racks encountered so far. Replicas are put into separate racks while possible. * For efficiency the set is shared between the instances, using the location pair (dc, rack) to make sure @@ -93,7 +92,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy int rfLeft; int acceptableRackRepeats; - DatacenterEndpoints(int rf, int rackCount, int nodeCount, Set endpoints, Set> racks) + DatacenterEndpoints(int rf, int rackCount, int nodeCount, Set endpoints, Set> racks) { this.endpoints = endpoints; this.racks = racks; @@ -108,7 +107,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy * Attempts to add an endpoint to the replicas for this datacenter, adding to the endpoints set if successful. * Returns true if the endpoint was added, and this datacenter does not require further replicas. */ - boolean addEndpointAndCheckIfDone(InetAddress ep, Pair location) + boolean addEndpointAndCheckIfDone(InetAddressAndPort ep, Pair location) { if (done()) return false; @@ -143,17 +142,17 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy /** * calculate endpoints in one pass through the tokens by tracking our progress in each DC. */ - public List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata) + public List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata) { // we want to preserve insertion order so that the first added endpoint becomes primary - Set replicas = new LinkedHashSet<>(); + Set replicas = new LinkedHashSet<>(); Set> seenRacks = new HashSet<>(); Topology topology = tokenMetadata.getTopology(); // all endpoints in each DC, so we can check when we have exhausted all the members of a DC - Multimap allEndpoints = topology.getDatacenterEndpoints(); + Multimap allEndpoints = topology.getDatacenterEndpoints(); // all racks in a DC so we can check when we have exhausted all racks in a DC - Map> racks = topology.getDatacenterRacks(); + Map> racks = topology.getDatacenterRacks(); assert !allEndpoints.isEmpty() && !racks.isEmpty() : "not aware of any cluster members"; int dcsToFill = 0; @@ -178,7 +177,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy while (dcsToFill > 0 && tokenIter.hasNext()) { Token next = tokenIter.next(); - InetAddress ep = tokenMetadata.getEndpoint(next); + InetAddressAndPort ep = tokenMetadata.getEndpoint(next); Pair location = topology.getLocation(ep); DatacenterEndpoints dcEndpoints = dcs.get(location.left); if (dcEndpoints != null && dcEndpoints.addEndpointAndCheckIfDone(ep, location)) @@ -227,9 +226,9 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy final IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); // Add data center of localhost. - validDataCenters.add(snitch.getDatacenter(FBUtilities.getBroadcastAddress())); + validDataCenters.add(snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort())); // Fetch and add DCs of all peers. - for (final InetAddress peer : StorageService.instance.getTokenMetadata().getAllEndpoints()) + for (final InetAddressAndPort peer : StorageService.instance.getTokenMetadata().getAllEndpoints()) { validDataCenters.add(snitch.getDatacenter(peer)); } diff --git a/src/java/org/apache/cassandra/locator/OldNetworkTopologyStrategy.java b/src/java/org/apache/cassandra/locator/OldNetworkTopologyStrategy.java index b9bd76751e..93e629ed6e 100644 --- a/src/java/org/apache/cassandra/locator/OldNetworkTopologyStrategy.java +++ b/src/java/org/apache/cassandra/locator/OldNetworkTopologyStrategy.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.Collection; @@ -42,10 +41,10 @@ public class OldNetworkTopologyStrategy extends AbstractReplicationStrategy super(keyspaceName, tokenMetadata, snitch, configOptions); } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { int replicas = getReplicationFactor(); - List endpoints = new ArrayList(replicas); + List endpoints = new ArrayList<>(replicas); ArrayList tokens = metadata.sortedTokens(); if (tokens.isEmpty()) diff --git a/src/java/org/apache/cassandra/locator/PendingRangeMaps.java b/src/java/org/apache/cassandra/locator/PendingRangeMaps.java index cfeccc4fc2..92307a3980 100644 --- a/src/java/org/apache/cassandra/locator/PendingRangeMaps.java +++ b/src/java/org/apache/cassandra/locator/PendingRangeMaps.java @@ -26,10 +26,9 @@ import org.apache.cassandra.dht.Token; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.InetAddress; import java.util.*; -public class PendingRangeMaps implements Iterable, List>> +public class PendingRangeMaps implements Iterable, List>> { private static final Logger logger = LoggerFactory.getLogger(PendingRangeMaps.class); @@ -39,7 +38,7 @@ public class PendingRangeMaps implements Iterable, List, List> ascendingMap; + final NavigableMap, List> ascendingMap; /** * sorting end ascending, if ends are same, sorting begin descending, so that token (end, end) will * come before (begin, end] with the same end, and (begin, end) will be selected in the tailMap. @@ -58,7 +57,7 @@ public class PendingRangeMaps implements Iterable, List, List> descendingMap; + final NavigableMap, List> descendingMap; /** * sorting begin descending, if begins are same, sorting end descending, so that token (begin, begin) will * come after (begin, end] with the same begin, and (begin, end) won't be selected in the tailMap. @@ -78,7 +77,7 @@ public class PendingRangeMaps implements Iterable, List, List> ascendingMapForWrapAround; + final NavigableMap, List> ascendingMapForWrapAround; /** * for wrap around range (begin, end], which begin > end. * Sorting end ascending, if ends are same, sorting begin ascending, @@ -98,7 +97,7 @@ public class PendingRangeMaps implements Iterable, List, List> descendingMapForWrapAround; + final NavigableMap, List> descendingMapForWrapAround; /** * for wrap around ranges, which begin > end. * Sorting end ascending, so that token (begin, begin) will come after (begin, end] with the same begin, @@ -118,28 +117,28 @@ public class PendingRangeMaps implements Iterable, List, List>(ascendingComparator); - this.descendingMap = new TreeMap, List>(descendingComparator); - this.ascendingMapForWrapAround = new TreeMap, List>(ascendingComparatorForWrapAround); - this.descendingMapForWrapAround = new TreeMap, List>(descendingComparatorForWrapAround); + this.ascendingMap = new TreeMap, List>(ascendingComparator); + this.descendingMap = new TreeMap, List>(descendingComparator); + this.ascendingMapForWrapAround = new TreeMap, List>(ascendingComparatorForWrapAround); + this.descendingMapForWrapAround = new TreeMap, List>(descendingComparatorForWrapAround); } static final void addToMap(Range range, - InetAddress address, - NavigableMap, List> ascendingMap, - NavigableMap, List> descendingMap) + InetAddressAndPort address, + NavigableMap, List> ascendingMap, + NavigableMap, List> descendingMap) { - List addresses = ascendingMap.get(range); + List addresses = ascendingMap.get(range); if (addresses == null) { - addresses = new ArrayList(1); + addresses = new ArrayList<>(1); ascendingMap.put(range, addresses); descendingMap.put(range, addresses); } addresses.add(address); } - public void addPendingRange(Range range, InetAddress address) + public void addPendingRange(Range range, InetAddressAndPort address) { if (Range.isWrapAround(range.left, range.right)) { @@ -151,14 +150,14 @@ public class PendingRangeMaps implements Iterable, List endpointsToAdd, - NavigableMap, List> smallerMap, - NavigableMap, List> biggerMap) + static final void addIntersections(Set endpointsToAdd, + NavigableMap, List> smallerMap, + NavigableMap, List> biggerMap) { // find the intersection of two sets for (Range range : smallerMap.keySet()) { - List addresses = biggerMap.get(range); + List addresses = biggerMap.get(range); if (addresses != null) { endpointsToAdd.addAll(addresses); @@ -166,15 +165,15 @@ public class PendingRangeMaps implements Iterable, List pendingEndpointsFor(Token token) + public Collection pendingEndpointsFor(Token token) { - Set endpoints = new HashSet<>(); + Set endpoints = new HashSet<>(); Range searchRange = new Range(token, token); // search for non-wrap-around maps - NavigableMap, List> ascendingTailMap = ascendingMap.tailMap(searchRange, true); - NavigableMap, List> descendingTailMap = descendingMap.tailMap(searchRange, false); + NavigableMap, List> ascendingTailMap = ascendingMap.tailMap(searchRange, true); + NavigableMap, List> descendingTailMap = descendingMap.tailMap(searchRange, false); // add intersections of two maps if (ascendingTailMap.size() < descendingTailMap.size()) @@ -191,11 +190,11 @@ public class PendingRangeMaps implements Iterable, List, List> entry : ascendingTailMap.entrySet()) + for (Map.Entry, List> entry : ascendingTailMap.entrySet()) { endpoints.addAll(entry.getValue()); } - for (Map.Entry, List> entry : descendingTailMap.entrySet()) + for (Map.Entry, List> entry : descendingTailMap.entrySet()) { endpoints.addAll(entry.getValue()); } @@ -207,11 +206,11 @@ public class PendingRangeMaps implements Iterable, List, List> entry : this) + for (Map.Entry, List> entry : this) { Range range = entry.getKey(); - for (InetAddress address : entry.getValue()) + for (InetAddressAndPort address : entry.getValue()) { sb.append(address).append(':').append(range); sb.append(System.getProperty("line.separator")); @@ -222,7 +221,7 @@ public class PendingRangeMaps implements Iterable, List, List>> iterator() + public Iterator, List>> iterator() { return Iterators.concat(ascendingMap.entrySet().iterator(), ascendingMapForWrapAround.entrySet().iterator()); } diff --git a/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java b/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java index 2908976bbd..3a9b161356 100644 --- a/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java +++ b/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java @@ -18,7 +18,6 @@ package org.apache.cassandra.locator; import java.io.InputStream; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashMap; @@ -55,7 +54,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch public static final String SNITCH_PROPERTIES_FILENAME = "cassandra-topology.properties"; private static final int DEFAULT_REFRESH_PERIOD_IN_SECONDS = 5; - private static volatile Map endpointMap; + private static volatile Map endpointMap; private static volatile String[] defaultDCRack; private volatile boolean gossipStarted; @@ -93,7 +92,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch * @param endpoint endpoint to process * @return a array of string with the first index being the data center and the second being the rack */ - public static String[] getEndpointInfo(InetAddress endpoint) + public static String[] getEndpointInfo(InetAddressAndPort endpoint) { String[] rawEndpointInfo = getRawEndpointInfo(endpoint); if (rawEndpointInfo == null) @@ -101,7 +100,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch return rawEndpointInfo; } - private static String[] getRawEndpointInfo(InetAddress endpoint) + private static String[] getRawEndpointInfo(InetAddressAndPort endpoint) { String[] value = endpointMap.get(endpoint); if (value == null) @@ -118,7 +117,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch * @param endpoint the endpoint to process * @return string of data center */ - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { String[] info = getEndpointInfo(endpoint); assert info != null : "No location defined for endpoint " + endpoint; @@ -131,7 +130,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch * @param endpoint the endpoint to process * @return string of rack */ - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { String[] info = getEndpointInfo(endpoint); assert info != null : "No location defined for endpoint " + endpoint; @@ -140,7 +139,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch public void reloadConfiguration(boolean isUpdate) throws ConfigurationException { - HashMap reloadedMap = new HashMap<>(); + HashMap reloadedMap = new HashMap<>(); String[] reloadedDefaultDCRack = null; Properties properties = new Properties(); @@ -168,11 +167,11 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch } else { - InetAddress host; + InetAddressAndPort host; String hostString = StringUtils.remove(key, '/'); try { - host = InetAddress.getByName(hostString); + host = InetAddressAndPort.getByName(hostString); } catch (UnknownHostException e) { @@ -186,7 +185,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch reloadedMap.put(host, token); } } - InetAddress broadcastAddress = FBUtilities.getBroadcastAddress(); + InetAddressAndPort broadcastAddress = FBUtilities.getBroadcastAddressAndPort(); String[] localInfo = reloadedMap.get(broadcastAddress); if (reloadedDefaultDCRack == null && localInfo == null) throw new ConfigurationException(String.format("Snitch definitions at %s do not define a location for " + @@ -194,7 +193,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch SNITCH_PROPERTIES_FILENAME, broadcastAddress)); // internode messaging code converts our broadcast address to local, // make sure we can be found at that as well. - InetAddress localAddress = FBUtilities.getLocalAddress(); + InetAddressAndPort localAddress = FBUtilities.getLocalAddressAndPort(); if (!localAddress.equals(broadcastAddress) && !reloadedMap.containsKey(localAddress)) reloadedMap.put(localAddress, localInfo); @@ -204,7 +203,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : reloadedMap.entrySet()) + for (Map.Entry entry : reloadedMap.entrySet()) sb.append(entry.getKey()).append(':').append(Arrays.toString(entry.getValue())).append(", "); logger.trace("Loaded network topology from property file: {}", StringUtils.removeEnd(sb.toString(), ", ")); } @@ -231,17 +230,17 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch * @param reloadedDefaultDCRack - the default dc:rack or null if no default * @return true if we can continue updating (no live host had dc or rack updated) */ - private static boolean livenessCheck(HashMap reloadedMap, String[] reloadedDefaultDCRack) + private static boolean livenessCheck(HashMap reloadedMap, String[] reloadedDefaultDCRack) { // If the default has changed we must check all live hosts but hopefully we will find a live // host quickly and interrupt the loop. Otherwise we only check the live hosts that were either // in the old set or in the new set - Set hosts = Arrays.equals(defaultDCRack, reloadedDefaultDCRack) + Set hosts = Arrays.equals(defaultDCRack, reloadedDefaultDCRack) ? Sets.intersection(StorageService.instance.getLiveRingMembers(), // same default Sets.union(endpointMap.keySet(), reloadedMap.keySet())) : StorageService.instance.getLiveRingMembers(); // default updated - for (InetAddress host : hosts) + for (InetAddressAndPort host : hosts) { String[] origValue = endpointMap.containsKey(host) ? endpointMap.get(host) : defaultDCRack; String[] updateValue = reloadedMap.containsKey(host) ? reloadedMap.get(host) : reloadedDefaultDCRack; diff --git a/src/java/org/apache/cassandra/locator/RackInferringSnitch.java b/src/java/org/apache/cassandra/locator/RackInferringSnitch.java index a6ea1abd53..6ae10ccac6 100644 --- a/src/java/org/apache/cassandra/locator/RackInferringSnitch.java +++ b/src/java/org/apache/cassandra/locator/RackInferringSnitch.java @@ -17,21 +17,19 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; - /** * A simple endpoint snitch implementation that assumes datacenter and rack information is encoded * in the 2nd and 3rd octets of the ip address, respectively. */ public class RackInferringSnitch extends AbstractNetworkTopologySnitch { - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { - return Integer.toString(endpoint.getAddress()[2] & 0xFF, 10); + return Integer.toString(endpoint.address.getAddress()[2] & 0xFF, 10); } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - return Integer.toString(endpoint.getAddress()[1] & 0xFF, 10); + return Integer.toString(endpoint.address.getAddress()[1] & 0xFF, 10); } } diff --git a/src/java/org/apache/cassandra/locator/ReconnectableSnitchHelper.java b/src/java/org/apache/cassandra/locator/ReconnectableSnitchHelper.java index 0b344c985d..547901086d 100644 --- a/src/java/org/apache/cassandra/locator/ReconnectableSnitchHelper.java +++ b/src/java/org/apache/cassandra/locator/ReconnectableSnitchHelper.java @@ -18,7 +18,6 @@ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.net.UnknownHostException; import com.google.common.annotations.VisibleForTesting; @@ -49,11 +48,11 @@ public class ReconnectableSnitchHelper implements IEndpointStateChangeSubscriber this.preferLocal = preferLocal; } - private void reconnect(InetAddress publicAddress, VersionedValue localAddressValue) + private void reconnect(InetAddressAndPort publicAddress, VersionedValue localAddressValue) { try { - reconnect(publicAddress, InetAddress.getByName(localAddressValue.value), snitch, localDc); + reconnect(publicAddress, InetAddressAndPort.getByName(localAddressValue.value), snitch, localDc); } catch (UnknownHostException e) { @@ -62,9 +61,9 @@ public class ReconnectableSnitchHelper implements IEndpointStateChangeSubscriber } @VisibleForTesting - static void reconnect(InetAddress publicAddress, InetAddress localAddress, IEndpointSnitch snitch, String localDc) + static void reconnect(InetAddressAndPort publicAddress, InetAddressAndPort localAddress, IEndpointSnitch snitch, String localDc) { - if (!DatabaseDescriptor.getInternodeAuthenticator().authenticate(publicAddress, MessagingService.instance().portFor(publicAddress))) + if (!DatabaseDescriptor.getInternodeAuthenticator().authenticate(publicAddress.address, MessagingService.instance().portFor(publicAddress))) { logger.debug("InternodeAuthenticator said don't reconnect to {} on {}", publicAddress, localAddress); return; @@ -78,40 +77,65 @@ public class ReconnectableSnitchHelper implements IEndpointStateChangeSubscriber } } - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) { // no-op } - public void onJoin(InetAddress endpoint, EndpointState epState) + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) { - if (preferLocal && !Gossiper.instance.isDeadState(epState) && epState.getApplicationState(ApplicationState.INTERNAL_IP) != null) - reconnect(endpoint, epState.getApplicationState(ApplicationState.INTERNAL_IP)); + if (preferLocal && !Gossiper.instance.isDeadState(epState)) + { + VersionedValue address = epState.getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT); + if (address == null) + { + address = epState.getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT); + } + if (address != null) + { + reconnect(endpoint, address); + } + } } - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) + //Skeptical this will always do the right thing all the time port wise. It will converge on the right thing + //eventually once INTERNAL_ADDRESS_AND_PORT is populated + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) { - if (preferLocal && state == ApplicationState.INTERNAL_IP && !Gossiper.instance.isDeadState(Gossiper.instance.getEndpointStateForEndpoint(endpoint))) - reconnect(endpoint, value); + if (preferLocal && !Gossiper.instance.isDeadState(Gossiper.instance.getEndpointStateForEndpoint(endpoint))) + { + if (state == ApplicationState.INTERNAL_ADDRESS_AND_PORT) + { + reconnect(endpoint, value); + } + else if (state == ApplicationState.INTERNAL_IP && + null == Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT)) + { + //Only use INTERNAL_IP if INTERNAL_ADDRESS_AND_PORT is unavailable + reconnect(endpoint, value); + } + } } - public void onAlive(InetAddress endpoint, EndpointState state) + public void onAlive(InetAddressAndPort endpoint, EndpointState state) { - if (preferLocal && state.getApplicationState(ApplicationState.INTERNAL_IP) != null) - reconnect(endpoint, state.getApplicationState(ApplicationState.INTERNAL_IP)); + VersionedValue internalIP = state.getApplicationState(ApplicationState.INTERNAL_IP); + VersionedValue internalIPAndPorts = state.getApplicationState(ApplicationState.INTERNAL_ADDRESS_AND_PORT); + if (preferLocal && internalIP != null) + reconnect(endpoint, internalIPAndPorts != null ? internalIPAndPorts : internalIP); } - public void onDead(InetAddress endpoint, EndpointState state) + public void onDead(InetAddressAndPort endpoint, EndpointState state) { // do nothing. } - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { // do nothing. } - public void onRestart(InetAddress endpoint, EndpointState state) + public void onRestart(InetAddressAndPort endpoint, EndpointState state) { // do nothing. } diff --git a/src/java/org/apache/cassandra/locator/SeedProvider.java b/src/java/org/apache/cassandra/locator/SeedProvider.java index a013fbbb31..7efa9e050a 100644 --- a/src/java/org/apache/cassandra/locator/SeedProvider.java +++ b/src/java/org/apache/cassandra/locator/SeedProvider.java @@ -17,10 +17,9 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.List; public interface SeedProvider { - List getSeeds(); + List getSeeds(); } diff --git a/src/java/org/apache/cassandra/locator/SimpleSeedProvider.java b/src/java/org/apache/cassandra/locator/SimpleSeedProvider.java index 665261d47a..47401a04a0 100644 --- a/src/java/org/apache/cassandra/locator/SimpleSeedProvider.java +++ b/src/java/org/apache/cassandra/locator/SimpleSeedProvider.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; @@ -26,6 +25,7 @@ import java.util.Map; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +35,7 @@ public class SimpleSeedProvider implements SeedProvider public SimpleSeedProvider(Map args) {} - public List getSeeds() + public List getSeeds() { Config conf; try @@ -47,12 +47,12 @@ public class SimpleSeedProvider implements SeedProvider throw new AssertionError(e); } String[] hosts = conf.seed_provider.parameters.get("seeds").split(",", -1); - List seeds = new ArrayList(hosts.length); + List seeds = new ArrayList<>(hosts.length); for (String host : hosts) { try { - seeds.add(InetAddress.getByName(host.trim())); + seeds.add(InetAddressAndPort.getByName(host.trim())); } catch (UnknownHostException ex) { diff --git a/src/java/org/apache/cassandra/locator/SimpleSnitch.java b/src/java/org/apache/cassandra/locator/SimpleSnitch.java index 27648c80a2..e31fc6b153 100644 --- a/src/java/org/apache/cassandra/locator/SimpleSnitch.java +++ b/src/java/org/apache/cassandra/locator/SimpleSnitch.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.List; /** @@ -27,23 +26,23 @@ import java.util.List; */ public class SimpleSnitch extends AbstractEndpointSnitch { - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return "rack1"; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return "datacenter1"; } @Override - public void sortByProximity(final InetAddress address, List addresses) + public void sortByProximity(final InetAddressAndPort address, List addresses) { // Optimization to avoid walking the list } - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { // Making all endpoints equal ensures we won't change the original ordering (since // Collections.sort is guaranteed to be stable) diff --git a/src/java/org/apache/cassandra/locator/SimpleStrategy.java b/src/java/org/apache/cassandra/locator/SimpleStrategy.java index 9a5062b9c0..545ad28e0c 100644 --- a/src/java/org/apache/cassandra/locator/SimpleStrategy.java +++ b/src/java/org/apache/cassandra/locator/SimpleStrategy.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.Collection; @@ -42,11 +41,11 @@ public class SimpleStrategy extends AbstractReplicationStrategy super(keyspaceName, tokenMetadata, snitch, configOptions); } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { int replicas = getReplicationFactor(); ArrayList tokens = metadata.sortedTokens(); - List endpoints = new ArrayList(replicas); + List endpoints = new ArrayList(replicas); if (tokens.isEmpty()) return endpoints; @@ -55,7 +54,7 @@ public class SimpleStrategy extends AbstractReplicationStrategy Iterator iter = TokenMetadata.ringIterator(tokens, token, false); while (endpoints.size() < replicas && iter.hasNext()) { - InetAddress ep = metadata.getEndpoint(iter.next()); + InetAddressAndPort ep = metadata.getEndpoint(iter.next()); if (!endpoints.contains(ep)) endpoints.add(ep); } diff --git a/src/java/org/apache/cassandra/locator/TokenMetadata.java b/src/java/org/apache/cassandra/locator/TokenMetadata.java index 00f95364ec..e2c4628642 100644 --- a/src/java/org/apache/cassandra/locator/TokenMetadata.java +++ b/src/java/org/apache/cassandra/locator/TokenMetadata.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -52,12 +51,12 @@ public class TokenMetadata * Each Token is associated with exactly one Address, but each Address may have * multiple tokens. Hence, the BiMultiValMap collection. */ - private final BiMultiValMap tokenToEndpointMap; + private final BiMultiValMap tokenToEndpointMap; /** Maintains endpoint to host ID map of every node in the cluster */ - private final BiMap endpointToHostIdMap; + private final BiMap endpointToHostIdMap; - // Prior to CASSANDRA-603, we just had Map pendingRanges, + // Prior to CASSANDRA-603, we just had Map pendingRanges, // which was added to when a node began bootstrap and removed from when it finished. // // This is inadequate when multiple changes are allowed simultaneously. For example, @@ -70,8 +69,8 @@ public class TokenMetadata // // So, we made two changes: // - // First, we changed pendingRanges to a Multimap (now - // Map>, because replication strategy + // First, we changed pendingRanges to a Multimap (now + // Map>, because replication strategy // and options are per-KeySpace). // // Second, we added the bootstrapTokens and leavingEndpoints collections, so we can @@ -81,17 +80,17 @@ public class TokenMetadata // Finally, note that recording the tokens of joining nodes in bootstrapTokens also // means we can detect and reject the addition of multiple nodes at the same token // before one becomes part of the ring. - private final BiMultiValMap bootstrapTokens = new BiMultiValMap<>(); + private final BiMultiValMap bootstrapTokens = new BiMultiValMap<>(); - private final BiMap replacementToOriginal = HashBiMap.create(); + private final BiMap replacementToOriginal = HashBiMap.create(); // (don't need to record Token here since it's still part of tokenToEndpointMap until it's done leaving) - private final Set leavingEndpoints = new HashSet<>(); + private final Set leavingEndpoints = new HashSet<>(); // this is a cache of the calculation from {tokenToEndpointMap, bootstrapTokens, leavingEndpoints} private final ConcurrentMap pendingRanges = new ConcurrentHashMap(); // nodes which are migrating to the new tokens in the ring - private final Set> movingEndpoints = new HashSet<>(); + private final Set> movingEndpoints = new HashSet<>(); /* Use this lock for manipulating the token map */ private final ReadWriteLock lock = new ReentrantReadWriteLock(true); @@ -100,26 +99,18 @@ public class TokenMetadata private final Topology topology; public final IPartitioner partitioner; - private static final Comparator inetaddressCmp = new Comparator() - { - public int compare(InetAddress o1, InetAddress o2) - { - return ByteBuffer.wrap(o1.getAddress()).compareTo(ByteBuffer.wrap(o2.getAddress())); - } - }; - // signals replication strategies that nodes have joined or left the ring and they need to recompute ownership private volatile long ringVersion = 0; public TokenMetadata() { - this(SortedBiMultiValMap.create(null, inetaddressCmp), - HashBiMap.create(), + this(SortedBiMultiValMap.create(), + HashBiMap.create(), new Topology(), DatabaseDescriptor.getPartitioner()); } - private TokenMetadata(BiMultiValMap tokenToEndpointMap, BiMap endpointsMap, Topology topology, IPartitioner partitioner) + private TokenMetadata(BiMultiValMap tokenToEndpointMap, BiMap endpointsMap, Topology topology, IPartitioner partitioner) { this.tokenToEndpointMap = tokenToEndpointMap; this.topology = topology; @@ -143,7 +134,7 @@ public class TokenMetadata } /** @return the number of nodes bootstrapping into source's primary range */ - public int pendingRangeChanges(InetAddress source) + public int pendingRangeChanges(InetAddressAndPort source) { int n = 0; Collection> sourceRanges = getPrimaryRangesFor(getTokens(source)); @@ -165,14 +156,14 @@ public class TokenMetadata /** * Update token map with a single token/endpoint pair in normal state. */ - public void updateNormalToken(Token token, InetAddress endpoint) + public void updateNormalToken(Token token, InetAddressAndPort endpoint) { updateNormalTokens(Collections.singleton(token), endpoint); } - public void updateNormalTokens(Collection tokens, InetAddress endpoint) + public void updateNormalTokens(Collection tokens, InetAddressAndPort endpoint) { - Multimap endpointTokens = HashMultimap.create(); + Multimap endpointTokens = HashMultimap.create(); for (Token token : tokens) endpointTokens.put(endpoint, token); updateNormalTokens(endpointTokens); @@ -184,7 +175,7 @@ public class TokenMetadata * Prefer this whenever there are multiple pairs to update, as each update (whether a single or multiple) * is expensive (CASSANDRA-3831). */ - public void updateNormalTokens(Multimap endpointTokens) + public void updateNormalTokens(Multimap endpointTokens) { if (endpointTokens.isEmpty()) return; @@ -193,7 +184,7 @@ public class TokenMetadata try { boolean shouldSortTokens = false; - for (InetAddress endpoint : endpointTokens.keySet()) + for (InetAddressAndPort endpoint : endpointTokens.keySet()) { Collection tokens = endpointTokens.get(endpoint); @@ -208,7 +199,7 @@ public class TokenMetadata for (Token token : tokens) { - InetAddress prev = tokenToEndpointMap.put(token, endpoint); + InetAddressAndPort prev = tokenToEndpointMap.put(token, endpoint); if (!endpoint.equals(prev)) { if (prev != null) @@ -231,7 +222,7 @@ public class TokenMetadata * Store an end-point to host ID mapping. Each ID must be unique, and * cannot be changed after the fact. */ - public void updateHostId(UUID hostId, InetAddress endpoint) + public void updateHostId(UUID hostId, InetAddressAndPort endpoint) { assert hostId != null; assert endpoint != null; @@ -239,7 +230,7 @@ public class TokenMetadata lock.writeLock().lock(); try { - InetAddress storedEp = endpointToHostIdMap.inverse().get(hostId); + InetAddressAndPort storedEp = endpointToHostIdMap.inverse().get(hostId); if (storedEp != null) { if (!storedEp.equals(endpoint) && (FailureDetector.instance.isAlive(storedEp))) @@ -265,7 +256,7 @@ public class TokenMetadata } /** Return the unique host ID for an end-point. */ - public UUID getHostId(InetAddress endpoint) + public UUID getHostId(InetAddressAndPort endpoint) { lock.readLock().lock(); try @@ -279,7 +270,7 @@ public class TokenMetadata } /** Return the end-point for a unique host ID */ - public InetAddress getEndpointForHostId(UUID hostId) + public InetAddressAndPort getEndpointForHostId(UUID hostId) { lock.readLock().lock(); try @@ -293,12 +284,12 @@ public class TokenMetadata } /** @return a copy of the endpoint-to-id map for read-only operations */ - public Map getEndpointToHostIdMapForReading() + public Map getEndpointToHostIdMapForReading() { lock.readLock().lock(); try { - Map readMap = new HashMap<>(); + Map readMap = new HashMap<>(); readMap.putAll(endpointToHostIdMap); return readMap; } @@ -309,17 +300,17 @@ public class TokenMetadata } @Deprecated - public void addBootstrapToken(Token token, InetAddress endpoint) + public void addBootstrapToken(Token token, InetAddressAndPort endpoint) { addBootstrapTokens(Collections.singleton(token), endpoint); } - public void addBootstrapTokens(Collection tokens, InetAddress endpoint) + public void addBootstrapTokens(Collection tokens, InetAddressAndPort endpoint) { addBootstrapTokens(tokens, endpoint, null); } - private void addBootstrapTokens(Collection tokens, InetAddress endpoint, InetAddress original) + private void addBootstrapTokens(Collection tokens, InetAddressAndPort endpoint, InetAddressAndPort original) { assert tokens != null && !tokens.isEmpty(); assert endpoint != null; @@ -328,7 +319,7 @@ public class TokenMetadata try { - InetAddress oldEndpoint; + InetAddressAndPort oldEndpoint; for (Token token : tokens) { @@ -352,7 +343,7 @@ public class TokenMetadata } } - public void addReplaceTokens(Collection replacingTokens, InetAddress newNode, InetAddress oldNode) + public void addReplaceTokens(Collection replacingTokens, InetAddressAndPort newNode, InetAddressAndPort oldNode) { assert replacingTokens != null && !replacingTokens.isEmpty(); assert newNode != null && oldNode != null; @@ -379,12 +370,12 @@ public class TokenMetadata } } - public Optional getReplacementNode(InetAddress endpoint) + public Optional getReplacementNode(InetAddressAndPort endpoint) { return Optional.ofNullable(replacementToOriginal.inverse().get(endpoint)); } - public Optional getReplacingNode(InetAddress endpoint) + public Optional getReplacingNode(InetAddressAndPort endpoint) { return Optional.ofNullable((replacementToOriginal.get(endpoint))); } @@ -405,7 +396,7 @@ public class TokenMetadata } } - public void addLeavingEndpoint(InetAddress endpoint) + public void addLeavingEndpoint(InetAddressAndPort endpoint) { assert endpoint != null; @@ -425,7 +416,7 @@ public class TokenMetadata * @param token token which is node moving to * @param endpoint address of the moving node */ - public void addMovingEndpoint(Token token, InetAddress endpoint) + public void addMovingEndpoint(Token token, InetAddressAndPort endpoint) { assert endpoint != null; @@ -441,7 +432,7 @@ public class TokenMetadata } } - public void removeEndpoint(InetAddress endpoint) + public void removeEndpoint(InetAddressAndPort endpoint) { assert endpoint != null; @@ -469,7 +460,7 @@ public class TokenMetadata /** * This is called when the snitch properties for this endpoint are updated, see CASSANDRA-10238. */ - public void updateTopology(InetAddress endpoint) + public void updateTopology(InetAddressAndPort endpoint) { assert endpoint != null; @@ -509,14 +500,14 @@ public class TokenMetadata * Remove pair of token/address from moving endpoints * @param endpoint address of the moving node */ - public void removeFromMoving(InetAddress endpoint) + public void removeFromMoving(InetAddressAndPort endpoint) { assert endpoint != null; lock.writeLock().lock(); try { - for (Pair pair : movingEndpoints) + for (Pair pair : movingEndpoints) { if (pair.right.equals(endpoint)) { @@ -533,7 +524,7 @@ public class TokenMetadata } } - public Collection getTokens(InetAddress endpoint) + public Collection getTokens(InetAddressAndPort endpoint) { assert endpoint != null; assert isMember(endpoint); // don't want to return nulls @@ -550,12 +541,12 @@ public class TokenMetadata } @Deprecated - public Token getToken(InetAddress endpoint) + public Token getToken(InetAddressAndPort endpoint) { return getTokens(endpoint).iterator().next(); } - public boolean isMember(InetAddress endpoint) + public boolean isMember(InetAddressAndPort endpoint) { assert endpoint != null; @@ -570,7 +561,7 @@ public class TokenMetadata } } - public boolean isLeaving(InetAddress endpoint) + public boolean isLeaving(InetAddressAndPort endpoint) { assert endpoint != null; @@ -585,7 +576,7 @@ public class TokenMetadata } } - public boolean isMoving(InetAddress endpoint) + public boolean isMoving(InetAddressAndPort endpoint) { assert endpoint != null; @@ -593,7 +584,7 @@ public class TokenMetadata try { - for (Pair pair : movingEndpoints) + for (Pair pair : movingEndpoints) { if (pair.right.equals(endpoint)) return true; @@ -618,7 +609,7 @@ public class TokenMetadata lock.readLock().lock(); try { - return new TokenMetadata(SortedBiMultiValMap.create(tokenToEndpointMap, null, inetaddressCmp), + return new TokenMetadata(SortedBiMultiValMap.create(tokenToEndpointMap), HashBiMap.create(endpointToHostIdMap), new Topology(topology), partitioner); @@ -673,9 +664,9 @@ public class TokenMetadata } } - private static TokenMetadata removeEndpoints(TokenMetadata allLeftMetadata, Set leavingEndpoints) + private static TokenMetadata removeEndpoints(TokenMetadata allLeftMetadata, Set leavingEndpoints) { - for (InetAddress endpoint : leavingEndpoints) + for (InetAddressAndPort endpoint : leavingEndpoints) allLeftMetadata.removeEndpoint(endpoint); return allLeftMetadata; @@ -695,11 +686,11 @@ public class TokenMetadata { TokenMetadata metadata = cloneOnlyTokenMap(); - for (InetAddress endpoint : leavingEndpoints) + for (InetAddressAndPort endpoint : leavingEndpoints) metadata.removeEndpoint(endpoint); - for (Pair pair : movingEndpoints) + for (Pair pair : movingEndpoints) metadata.updateNormalToken(pair.left, pair.right); return metadata; @@ -710,7 +701,7 @@ public class TokenMetadata } } - public InetAddress getEndpoint(Token token) + public InetAddressAndPort getEndpoint(Token token) { lock.readLock().lock(); try @@ -742,17 +733,17 @@ public class TokenMetadata return sortedTokens; } - public Multimap, InetAddress> getPendingRangesMM(String keyspaceName) + public Multimap, InetAddressAndPort> getPendingRangesMM(String keyspaceName) { - Multimap, InetAddress> map = HashMultimap.create(); + Multimap, InetAddressAndPort> map = HashMultimap.create(); PendingRangeMaps pendingRangeMaps = this.pendingRanges.get(keyspaceName); if (pendingRangeMaps != null) { - for (Map.Entry, List> entry : pendingRangeMaps) + for (Map.Entry, List> entry : pendingRangeMaps) { Range range = entry.getKey(); - for (InetAddress address : entry.getValue()) + for (InetAddressAndPort address : entry.getValue()) { map.put(range, address); } @@ -768,10 +759,10 @@ public class TokenMetadata return this.pendingRanges.get(keyspaceName); } - public List> getPendingRanges(String keyspaceName, InetAddress endpoint) + public List> getPendingRanges(String keyspaceName, InetAddressAndPort endpoint) { List> ranges = new ArrayList<>(); - for (Map.Entry, InetAddress> entry : getPendingRangesMM(keyspaceName).entries()) + for (Map.Entry, InetAddressAndPort> entry : getPendingRangesMM(keyspaceName).entries()) { if (entry.getValue().equals(endpoint)) { @@ -824,9 +815,9 @@ public class TokenMetadata long startedAt = System.currentTimeMillis(); // create clone of current state - BiMultiValMap bootstrapTokensClone = new BiMultiValMap<>(); - Set leavingEndpointsClone = new HashSet<>(); - Set> movingEndpointsClone = new HashSet<>(); + BiMultiValMap bootstrapTokensClone = new BiMultiValMap<>(); + Set leavingEndpointsClone = new HashSet<>(); + Set> movingEndpointsClone = new HashSet<>(); TokenMetadata metadata; lock.readLock().lock(); @@ -859,29 +850,29 @@ public class TokenMetadata */ private static PendingRangeMaps calculatePendingRanges(AbstractReplicationStrategy strategy, TokenMetadata metadata, - BiMultiValMap bootstrapTokens, - Set leavingEndpoints, - Set> movingEndpoints) + BiMultiValMap bootstrapTokens, + Set leavingEndpoints, + Set> movingEndpoints) { PendingRangeMaps newPendingRanges = new PendingRangeMaps(); - Multimap> addressRanges = strategy.getAddressRanges(metadata); + Multimap> addressRanges = strategy.getAddressRanges(metadata); // Copy of metadata reflecting the situation after all leave operations are finished. TokenMetadata allLeftMetadata = removeEndpoints(metadata.cloneOnlyTokenMap(), leavingEndpoints); // get all ranges that will be affected by leaving nodes Set> affectedRanges = new HashSet>(); - for (InetAddress endpoint : leavingEndpoints) + for (InetAddressAndPort endpoint : leavingEndpoints) affectedRanges.addAll(addressRanges.get(endpoint)); // for each of those ranges, find what new nodes will be responsible for the range when // all leaving nodes are gone. for (Range range : affectedRanges) { - Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, metadata)); - Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, allLeftMetadata)); - for (InetAddress address : Sets.difference(newEndpoints, currentEndpoints)) + Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, metadata)); + Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, allLeftMetadata)); + for (InetAddressAndPort address : Sets.difference(newEndpoints, currentEndpoints)) { newPendingRanges.addPendingRange(range, address); } @@ -892,8 +883,8 @@ public class TokenMetadata // For each of the bootstrapping nodes, simply add and remove them one by one to // allLeftMetadata and check in between what their ranges would be. - Multimap bootstrapAddresses = bootstrapTokens.inverse(); - for (InetAddress endpoint : bootstrapAddresses.keySet()) + Multimap bootstrapAddresses = bootstrapTokens.inverse(); + for (InetAddressAndPort endpoint : bootstrapAddresses.keySet()) { Collection tokens = bootstrapAddresses.get(endpoint); @@ -910,11 +901,11 @@ public class TokenMetadata // For each of the moving nodes, we do the same thing we did for bootstrapping: // simply add and remove them one by one to allLeftMetadata and check in between what their ranges would be. - for (Pair moving : movingEndpoints) + for (Pair moving : movingEndpoints) { //Calculate all the ranges which will could be affected. This will include the ranges before and after the move. Set> moveAffectedRanges = new HashSet<>(); - InetAddress endpoint = moving.right; // address of the moving node + InetAddressAndPort endpoint = moving.right; // address of the moving node //Add ranges before the move for (Range range : strategy.getAddressRanges(allLeftMetadata).get(endpoint)) { @@ -930,10 +921,10 @@ public class TokenMetadata for(Range range : moveAffectedRanges) { - Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, metadata)); - Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, allLeftMetadata)); - Set difference = Sets.difference(newEndpoints, currentEndpoints); - for(final InetAddress address : difference) + Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, metadata)); + Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(range.right, allLeftMetadata)); + Set difference = Sets.difference(newEndpoints, currentEndpoints); + for(final InetAddressAndPort address : difference) { Collection> newRanges = strategy.getAddressRanges(allLeftMetadata).get(address); Collection> oldRanges = strategy.getAddressRanges(metadata).get(address); @@ -973,12 +964,12 @@ public class TokenMetadata } /** @return a copy of the bootstrapping tokens map */ - public BiMultiValMap getBootstrapTokens() + public BiMultiValMap getBootstrapTokens() { lock.readLock().lock(); try { - return new BiMultiValMap(bootstrapTokens); + return new BiMultiValMap<>(bootstrapTokens); } finally { @@ -986,7 +977,7 @@ public class TokenMetadata } } - public Set getAllEndpoints() + public Set getAllEndpoints() { lock.readLock().lock(); try @@ -1010,7 +1001,7 @@ public class TokenMetadata } /** caller should not modify leavingEndpoints */ - public Set getLeavingEndpoints() + public Set getLeavingEndpoints() { lock.readLock().lock(); try @@ -1037,7 +1028,7 @@ public class TokenMetadata * Endpoints which are migrating to the new tokens * @return set of addresses of moving endpoints */ - public Set> getMovingEndpoints() + public Set> getMovingEndpoints() { lock.readLock().lock(); try @@ -1148,14 +1139,14 @@ public class TokenMetadata lock.readLock().lock(); try { - Multimap endpointToTokenMap = tokenToEndpointMap.inverse(); - Set eps = endpointToTokenMap.keySet(); + Multimap endpointToTokenMap = tokenToEndpointMap.inverse(); + Set eps = endpointToTokenMap.keySet(); if (!eps.isEmpty()) { sb.append("Normal Tokens:"); sb.append(System.getProperty("line.separator")); - for (InetAddress ep : eps) + for (InetAddressAndPort ep : eps) { sb.append(ep); sb.append(':'); @@ -1168,7 +1159,7 @@ public class TokenMetadata { sb.append("Bootstrapping Tokens:" ); sb.append(System.getProperty("line.separator")); - for (Map.Entry entry : bootstrapTokens.entrySet()) + for (Map.Entry entry : bootstrapTokens.entrySet()) { sb.append(entry.getValue()).append(':').append(entry.getKey()); sb.append(System.getProperty("line.separator")); @@ -1179,7 +1170,7 @@ public class TokenMetadata { sb.append("Leaving Endpoints:"); sb.append(System.getProperty("line.separator")); - for (InetAddress ep : leavingEndpoints) + for (InetAddressAndPort ep : leavingEndpoints) { sb.append(ep); sb.append(System.getProperty("line.separator")); @@ -1213,7 +1204,7 @@ public class TokenMetadata return sb.toString(); } - public Collection pendingEndpointsFor(Token token, String keyspaceName) + public Collection pendingEndpointsFor(Token token, String keyspaceName) { PendingRangeMaps pendingRangeMaps = this.pendingRanges.get(keyspaceName); if (pendingRangeMaps == null) @@ -1225,19 +1216,19 @@ public class TokenMetadata /** * @deprecated retained for benefit of old tests */ - public Collection getWriteEndpoints(Token token, String keyspaceName, Collection naturalEndpoints) + public Collection getWriteEndpoints(Token token, String keyspaceName, Collection naturalEndpoints) { return ImmutableList.copyOf(Iterables.concat(naturalEndpoints, pendingEndpointsFor(token, keyspaceName))); } /** @return an endpoint to token multimap representation of tokenToEndpointMap (a copy) */ - public Multimap getEndpointToTokenMapForReading() + public Multimap getEndpointToTokenMapForReading() { lock.readLock().lock(); try { - Multimap cloned = HashMultimap.create(); - for (Map.Entry entry : tokenToEndpointMap.entrySet()) + Multimap cloned = HashMultimap.create(); + for (Map.Entry entry : tokenToEndpointMap.entrySet()) cloned.put(entry.getValue(), entry.getKey()); return cloned; } @@ -1251,12 +1242,12 @@ public class TokenMetadata * @return a (stable copy, won't be modified) Token to Endpoint map for all the normal and bootstrapping nodes * in the cluster. */ - public Map getNormalAndBootstrappingTokenToEndpointMap() + public Map getNormalAndBootstrappingTokenToEndpointMap() { lock.readLock().lock(); try { - Map map = new HashMap<>(tokenToEndpointMap.size() + bootstrapTokens.size()); + Map map = new HashMap<>(tokenToEndpointMap.size() + bootstrapTokens.size()); map.putAll(tokenToEndpointMap); map.putAll(bootstrapTokens); return map; @@ -1302,11 +1293,11 @@ public class TokenMetadata public static class Topology { /** multi-map of DC to endpoints in that DC */ - private final Multimap dcEndpoints; + private final Multimap dcEndpoints; /** map of DC to multi-map of rack to endpoints in that rack */ - private final Map> dcRacks; + private final Map> dcRacks; /** reverse-lookup map for endpoint to current known dc/rack assignment */ - private final Map> currentLocations; + private final Map> currentLocations; Topology() { @@ -1337,7 +1328,7 @@ public class TokenMetadata /** * Stores current DC/rack assignment for ep */ - void addEndpoint(InetAddress ep) + void addEndpoint(InetAddressAndPort ep) { IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); String dc = snitch.getDatacenter(ep); @@ -1353,12 +1344,12 @@ public class TokenMetadata doAddEndpoint(ep, dc, rack); } - private void doAddEndpoint(InetAddress ep, String dc, String rack) + private void doAddEndpoint(InetAddressAndPort ep, String dc, String rack) { dcEndpoints.put(dc, ep); if (!dcRacks.containsKey(dc)) - dcRacks.put(dc, HashMultimap.create()); + dcRacks.put(dc, HashMultimap.create()); dcRacks.get(dc).put(rack, ep); currentLocations.put(ep, Pair.create(dc, rack)); @@ -1367,7 +1358,7 @@ public class TokenMetadata /** * Removes current DC/rack assignment for ep */ - void removeEndpoint(InetAddress ep) + void removeEndpoint(InetAddressAndPort ep) { if (!currentLocations.containsKey(ep)) return; @@ -1375,13 +1366,13 @@ public class TokenMetadata doRemoveEndpoint(ep, currentLocations.remove(ep)); } - private void doRemoveEndpoint(InetAddress ep, Pair current) + private void doRemoveEndpoint(InetAddressAndPort ep, Pair current) { dcRacks.get(current.left).remove(current.right, ep); dcEndpoints.remove(current.left, ep); } - void updateEndpoint(InetAddress ep) + void updateEndpoint(InetAddressAndPort ep) { IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); if (snitch == null || !currentLocations.containsKey(ep)) @@ -1396,11 +1387,11 @@ public class TokenMetadata if (snitch == null) return; - for (InetAddress ep : currentLocations.keySet()) + for (InetAddressAndPort ep : currentLocations.keySet()) updateEndpoint(ep, snitch); } - private void updateEndpoint(InetAddress ep, IEndpointSnitch snitch) + private void updateEndpoint(InetAddressAndPort ep, IEndpointSnitch snitch) { Pair current = currentLocations.get(ep); String dc = snitch.getDatacenter(ep); @@ -1415,7 +1406,7 @@ public class TokenMetadata /** * @return multi-map of DC to endpoints in that DC */ - public Multimap getDatacenterEndpoints() + public Multimap getDatacenterEndpoints() { return dcEndpoints; } @@ -1423,7 +1414,7 @@ public class TokenMetadata /** * @return map of DC to multi-map of rack to endpoints in that rack */ - public Map> getDatacenterRacks() + public Map> getDatacenterRacks() { return dcRacks; } @@ -1431,7 +1422,7 @@ public class TokenMetadata /** * @return The DC and rack of the given endpoint. */ - public Pair getLocation(InetAddress addr) + public Pair getLocation(InetAddressAndPort addr) { return currentLocations.get(addr); } diff --git a/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java b/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java index 7815784e10..3655a404d0 100644 --- a/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ConnectionMetrics.java @@ -17,14 +17,14 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; - import com.codahale.metrics.Gauge; import com.codahale.metrics.Meter; import org.apache.cassandra.net.async.OutboundMessagingPool; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * Metrics for internode connections. */ @@ -65,10 +65,10 @@ public class ConnectionMetrics * * @param ip IP address to use for metrics label */ - public ConnectionMetrics(InetAddress ip, final OutboundMessagingPool messagingPool) + public ConnectionMetrics(InetAddressAndPort ip, final OutboundMessagingPool messagingPool) { // ipv6 addresses will contain colons, which are invalid in a JMX ObjectName - address = ip.getHostAddress().replace(':', '.'); + address = ip.toString().replace(':', '.'); factory = new DefaultNameFactory("Connection", address); diff --git a/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java b/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java index 052830ac54..56888da103 100644 --- a/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java +++ b/src/java/org/apache/cassandra/metrics/HintedHandoffMetrics.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; import java.util.Map.Entry; import com.google.common.util.concurrent.MoreExecutors; @@ -27,6 +26,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import org.apache.cassandra.db.SystemKeyspace; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.UUIDGen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,28 +43,28 @@ public class HintedHandoffMetrics private static final MetricNameFactory factory = new DefaultNameFactory("HintedHandOffManager"); /** Total number of hints which are not stored, This is not a cache. */ - private final LoadingCache notStored = Caffeine.newBuilder() - .executor(MoreExecutors.directExecutor()) - .build(DifferencingCounter::new); + private final LoadingCache notStored = Caffeine.newBuilder() + .executor(MoreExecutors.directExecutor()) + .build(DifferencingCounter::new); /** Total number of hints that have been created, This is not a cache. */ - private final LoadingCache createdHintCounts = Caffeine.newBuilder() - .executor(MoreExecutors.directExecutor()) - .build(address -> Metrics.counter(factory.createMetricName("Hints_created-" + address.getHostAddress().replace(':', '.')))); + private final LoadingCache createdHintCounts = Caffeine.newBuilder() + .executor(MoreExecutors.directExecutor()) + .build(address -> Metrics.counter(factory.createMetricName("Hints_created-" + address.toString().replace(':', '.')))); - public void incrCreatedHints(InetAddress address) + public void incrCreatedHints(InetAddressAndPort address) { createdHintCounts.get(address).inc(); } - public void incrPastWindow(InetAddress address) + public void incrPastWindow(InetAddressAndPort address) { notStored.get(address).mark(); } public void log() { - for (Entry entry : notStored.asMap().entrySet()) + for (Entry entry : notStored.asMap().entrySet()) { long difference = entry.getValue().difference(); if (difference == 0) @@ -79,9 +79,10 @@ public class HintedHandoffMetrics private final Counter meter; private long reported = 0; - public DifferencingCounter(InetAddress address) + public DifferencingCounter(InetAddressAndPort address) { - this.meter = Metrics.counter(factory.createMetricName("Hints_not_stored-" + address.getHostAddress().replace(':', '.'))); + //This changes the name of the metric, people can update their monitoring when upgrading? + this.meter = Metrics.counter(factory.createMetricName("Hints_not_stored-" + address.toString().replace(':', '.'))); } public long difference() diff --git a/src/java/org/apache/cassandra/metrics/HintsServiceMetrics.java b/src/java/org/apache/cassandra/metrics/HintsServiceMetrics.java index d6a75f7e20..424f5025f9 100644 --- a/src/java/org/apache/cassandra/metrics/HintsServiceMetrics.java +++ b/src/java/org/apache/cassandra/metrics/HintsServiceMetrics.java @@ -17,8 +17,6 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; - import com.google.common.util.concurrent.MoreExecutors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +25,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.Meter; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import org.apache.cassandra.locator.InetAddressAndPort; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; @@ -47,11 +46,11 @@ public final class HintsServiceMetrics private static final Histogram globalDelayHistogram = Metrics.histogram(factory.createMetricName("Hint_delays"), false); /** Histograms per-endpoint of hint delivery delays, This is not a cache. */ - private static final LoadingCache delayByEndpoint = Caffeine.newBuilder() - .executor(MoreExecutors.directExecutor()) - .build(address -> Metrics.histogram(factory.createMetricName("Hint_delays-"+address.getHostAddress().replace(':', '.')), false)); + private static final LoadingCache delayByEndpoint = Caffeine.newBuilder() + .executor(MoreExecutors.directExecutor()) + .build(address -> Metrics.histogram(factory.createMetricName("Hint_delays-"+address.toString().replace(':', '.')), false)); - public static void updateDelayMetrics(InetAddress endpoint, long delay) + public static void updateDelayMetrics(InetAddressAndPort endpoint, long delay) { if (delay <= 0) { diff --git a/src/java/org/apache/cassandra/metrics/MessagingMetrics.java b/src/java/org/apache/cassandra/metrics/MessagingMetrics.java index 5f640b9510..2f096f6c37 100644 --- a/src/java/org/apache/cassandra/metrics/MessagingMetrics.java +++ b/src/java/org/apache/cassandra/metrics/MessagingMetrics.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -26,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codahale.metrics.Timer; +import org.apache.cassandra.locator.InetAddressAndPort; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; @@ -47,7 +47,7 @@ public class MessagingMetrics queueWaitLatency = new ConcurrentHashMap<>(); } - public void addTimeTaken(InetAddress from, long timeTaken) + public void addTimeTaken(InetAddressAndPort from, long timeTaken) { String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(from); Timer timer = dcLatency.get(dc); diff --git a/src/java/org/apache/cassandra/metrics/StreamingMetrics.java b/src/java/org/apache/cassandra/metrics/StreamingMetrics.java index 72e9b23724..d220ca5704 100644 --- a/src/java/org/apache/cassandra/metrics/StreamingMetrics.java +++ b/src/java/org/apache/cassandra/metrics/StreamingMetrics.java @@ -17,11 +17,12 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; import java.util.concurrent.ConcurrentMap; import com.codahale.metrics.Counter; +import org.apache.cassandra.locator.InetAddressAndPort; + import org.cliffc.high_scale_lib.NonBlockingHashMap; import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; @@ -33,7 +34,7 @@ public class StreamingMetrics { public static final String TYPE_NAME = "Streaming"; - private static final ConcurrentMap instances = new NonBlockingHashMap(); + private static final ConcurrentMap instances = new NonBlockingHashMap<>(); public static final Counter activeStreamsOutbound = Metrics.counter(DefaultNameFactory.createMetricName(TYPE_NAME, "ActiveOutboundStreams", null)); public static final Counter totalIncomingBytes = Metrics.counter(DefaultNameFactory.createMetricName(TYPE_NAME, "TotalIncomingBytes", null)); @@ -41,7 +42,7 @@ public class StreamingMetrics public final Counter incomingBytes; public final Counter outgoingBytes; - public static StreamingMetrics get(InetAddress ip) + public static StreamingMetrics get(InetAddressAndPort ip) { StreamingMetrics metrics = instances.get(ip); if (metrics == null) @@ -52,9 +53,9 @@ public class StreamingMetrics return metrics; } - public StreamingMetrics(final InetAddress peer) + public StreamingMetrics(final InetAddressAndPort peer) { - MetricNameFactory factory = new DefaultNameFactory("Streaming", peer.getHostAddress().replace(':', '.')); + MetricNameFactory factory = new DefaultNameFactory("Streaming", peer.toString().replace(':', '.')); incomingBytes = Metrics.counter(factory.createMetricName("IncomingBytes")); outgoingBytes= Metrics.counter(factory.createMetricName("OutgoingBytes")); } diff --git a/src/java/org/apache/cassandra/net/BackPressureState.java b/src/java/org/apache/cassandra/net/BackPressureState.java index 34fd0ddef5..886c075468 100644 --- a/src/java/org/apache/cassandra/net/BackPressureState.java +++ b/src/java/org/apache/cassandra/net/BackPressureState.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Interface meant to track the back-pressure state per replica host. @@ -47,5 +47,5 @@ public interface BackPressureState /** * Returns the host this state refers to. */ - InetAddress getHost(); + InetAddressAndPort getHost(); } diff --git a/src/java/org/apache/cassandra/net/BackPressureStrategy.java b/src/java/org/apache/cassandra/net/BackPressureStrategy.java index 78f748bb13..6b49495ae9 100644 --- a/src/java/org/apache/cassandra/net/BackPressureStrategy.java +++ b/src/java/org/apache/cassandra/net/BackPressureStrategy.java @@ -17,10 +17,11 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * Back-pressure algorithm interface. *

@@ -39,5 +40,5 @@ public interface BackPressureStrategy /** * Creates a new {@link BackPressureState} initialized as needed by the specific implementation. */ - S newState(InetAddress host); + S newState(InetAddressAndPort host); } diff --git a/src/java/org/apache/cassandra/net/CallbackInfo.java b/src/java/org/apache/cassandra/net/CallbackInfo.java index ea000ae53f..f2ed8a10fa 100644 --- a/src/java/org/apache/cassandra/net/CallbackInfo.java +++ b/src/java/org/apache/cassandra/net/CallbackInfo.java @@ -17,9 +17,8 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; - import org.apache.cassandra.io.IVersionedSerializer; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Encapsulates the callback information. @@ -28,7 +27,7 @@ import org.apache.cassandra.io.IVersionedSerializer; */ public class CallbackInfo { - protected final InetAddress target; + protected final InetAddressAndPort target; protected final IAsyncCallback callback; protected final IVersionedSerializer serializer; private final boolean failureCallback; @@ -41,7 +40,7 @@ public class CallbackInfo * @param serializer serializer to deserialize response message * @param failureCallback True when we have a callback to handle failures */ - public CallbackInfo(InetAddress target, IAsyncCallback callback, IVersionedSerializer serializer, boolean failureCallback) + public CallbackInfo(InetAddressAndPort target, IAsyncCallback callback, IVersionedSerializer serializer, boolean failureCallback) { this.target = target; this.callback = callback; diff --git a/src/java/org/apache/cassandra/net/CompactEndpointSerializationHelper.java b/src/java/org/apache/cassandra/net/CompactEndpointSerializationHelper.java index 83bbbf3fe3..b58ca47254 100644 --- a/src/java/org/apache/cassandra/net/CompactEndpointSerializationHelper.java +++ b/src/java/org/apache/cassandra/net/CompactEndpointSerializationHelper.java @@ -21,28 +21,108 @@ import java.io.*; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.ByteBuffer; -public class CompactEndpointSerializationHelper +import org.apache.cassandra.io.IVersionedSerializer; +import org.apache.cassandra.io.util.DataInputBuffer; +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.streaming.messages.StreamMessage; + +/* + * As of version 4.0 the endpoint description includes a port number as an unsigned short + */ +public class CompactEndpointSerializationHelper implements IVersionedSerializer { - public static void serialize(InetAddress endpoint, DataOutput out) throws IOException + public static final IVersionedSerializer instance = new CompactEndpointSerializationHelper(); + + /** + * Streaming uses its own version numbering so we need to ignore it and always use currrent version. + * There is no cross version streaming so it will always use the latest address serialization. + **/ + public static final IVersionedSerializer streamingInstance = new IVersionedSerializer() { - byte[] buf = endpoint.getAddress(); - out.writeByte(buf.length); - out.write(buf); + public void serialize(InetAddressAndPort inetAddressAndPort, DataOutputPlus out, int version) throws IOException + { + instance.serialize(inetAddressAndPort, out, MessagingService.current_version); + } + + public InetAddressAndPort deserialize(DataInputPlus in, int version) throws IOException + { + return instance.deserialize(in, MessagingService.current_version); + } + + public long serializedSize(InetAddressAndPort inetAddressAndPort, int version) + { + return instance.serializedSize(inetAddressAndPort, MessagingService.current_version); + } + }; + + private CompactEndpointSerializationHelper() {} + + public void serialize(InetAddressAndPort endpoint, DataOutputPlus out, int version) throws IOException + { + if (version >= MessagingService.VERSION_40) + { + byte[] buf = endpoint.addressBytes; + out.writeByte(buf.length + 2); + out.write(buf); + out.writeShort(endpoint.port); + } + else + { + byte[] buf = endpoint.addressBytes; + out.writeByte(buf.length); + out.write(buf); + } } - public static InetAddress deserialize(DataInput in) throws IOException + public InetAddressAndPort deserialize(DataInputPlus in, int version) throws IOException { - byte[] bytes = new byte[in.readByte()]; - in.readFully(bytes, 0, bytes.length); - return InetAddress.getByAddress(bytes); + int size = in.readByte() & 0xFF; + switch(size) + { + //The original pre-4.0 serialiation of just an address + case 4: + case 16: + { + byte[] bytes = new byte[size]; + in.readFully(bytes, 0, bytes.length); + return InetAddressAndPort.getByAddress(bytes); + } + //Address and one port + case 6: + case 18: + { + byte[] bytes = new byte[size - 2]; + in.readFully(bytes); + + int port = in.readShort() & 0xFFFF; + return InetAddressAndPort.getByAddressOverrideDefaults(InetAddress.getByAddress(bytes), bytes, port); + } + default: + throw new AssertionError("Unexpected size " + size); + + } } - public static int serializedSize(InetAddress from) + public long serializedSize(InetAddressAndPort from, int version) { - if (from instanceof Inet4Address) - return 1 + 4; - assert from instanceof Inet6Address; - return 1 + 16; + //4.0 includes a port number + if (version >= MessagingService.VERSION_40) + { + if (from.address instanceof Inet4Address) + return 1 + 4 + 2; + assert from.address instanceof Inet6Address; + return 1 + 16 + 2; + } + else + { + if (from.address instanceof Inet4Address) + return 1 + 4; + assert from.address instanceof Inet6Address; + return 1 + 16; + } } } diff --git a/src/java/org/apache/cassandra/net/ForwardToContainer.java b/src/java/org/apache/cassandra/net/ForwardToContainer.java new file mode 100644 index 0000000000..ac9e725fa0 --- /dev/null +++ b/src/java/org/apache/cassandra/net/ForwardToContainer.java @@ -0,0 +1,43 @@ +/* + * 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.cassandra.net; + +import java.util.Collection; + +import com.google.common.base.Preconditions; + +import org.apache.cassandra.locator.InetAddressAndPort; + +/** + * Contains forward to information until it can be serialized as part of a message using a version + * specific serialization + */ +public class ForwardToContainer +{ + public final Collection targets; + public final int[] messageIds; + + public ForwardToContainer(Collection targets, + int[] messageIds) + { + Preconditions.checkArgument(targets.size() == messageIds.length); + this.targets = targets; + this.messageIds = messageIds; + } +} diff --git a/src/java/org/apache/cassandra/net/ForwardToSerializer.java b/src/java/org/apache/cassandra/net/ForwardToSerializer.java new file mode 100644 index 0000000000..c4e8843ec2 --- /dev/null +++ b/src/java/org/apache/cassandra/net/ForwardToSerializer.java @@ -0,0 +1,86 @@ +/* + * 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.cassandra.net; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.cassandra.io.IVersionedSerializer; +import org.apache.cassandra.io.util.DataInputBuffer; +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; + +public class ForwardToSerializer implements IVersionedSerializer +{ + public static ForwardToSerializer instance = new ForwardToSerializer(); + + private ForwardToSerializer() {} + + public void serialize(ForwardToContainer forwardToContainer, DataOutputPlus out, int version) throws IOException + { + out.writeInt(forwardToContainer.targets.size()); + Iterator iter = forwardToContainer.targets.iterator(); + for (int ii = 0; ii < forwardToContainer.messageIds.length; ii++) + { + CompactEndpointSerializationHelper.instance.serialize(iter.next(), out, version); + out.writeInt(forwardToContainer.messageIds[ii]); + } + } + + public ForwardToContainer deserialize(DataInputPlus in, int version) throws IOException + { + int[] ids = new int[in.readInt()]; + List hosts = new ArrayList<>(ids.length); + for (int ii = 0; ii < ids.length; ii++) + { + hosts.add(CompactEndpointSerializationHelper.instance.deserialize(in, version)); + ids[ii] = in.readInt(); + } + return new ForwardToContainer(hosts, ids); + } + + public long serializedSize(ForwardToContainer forwardToContainer, int version) + { + //Number of forward addresses, 4 bytes per for each id + long size = 4 + + (4 * forwardToContainer.targets.size()); + //Depending on ipv6 or ipv4 the address size is different. + for (InetAddressAndPort forwardTo : forwardToContainer.targets) + { + size += CompactEndpointSerializationHelper.instance.serializedSize(forwardTo, version); + } + + return size; + } + + public static ForwardToContainer fromBytes(byte[] bytes, int version) + { + try (DataInputBuffer input = new DataInputBuffer(bytes)) + { + return instance.deserialize(input, version); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/src/java/org/apache/cassandra/net/IAsyncCallback.java b/src/java/org/apache/cassandra/net/IAsyncCallback.java index 7835079116..251d263baa 100644 --- a/src/java/org/apache/cassandra/net/IAsyncCallback.java +++ b/src/java/org/apache/cassandra/net/IAsyncCallback.java @@ -17,11 +17,10 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; - import com.google.common.base.Predicate; import org.apache.cassandra.gms.FailureDetector; +import org.apache.cassandra.locator.InetAddressAndPort; /** * implementors of IAsyncCallback need to make sure that any public methods @@ -31,9 +30,9 @@ import org.apache.cassandra.gms.FailureDetector; */ public interface IAsyncCallback { - Predicate isAlive = new Predicate() + Predicate isAlive = new Predicate() { - public boolean apply(InetAddress endpoint) + public boolean apply(InetAddressAndPort endpoint) { return FailureDetector.instance.isAlive(endpoint); } diff --git a/src/java/org/apache/cassandra/net/IAsyncCallbackWithFailure.java b/src/java/org/apache/cassandra/net/IAsyncCallbackWithFailure.java index 1cd27b65c5..2b91f2056c 100644 --- a/src/java/org/apache/cassandra/net/IAsyncCallbackWithFailure.java +++ b/src/java/org/apache/cassandra/net/IAsyncCallbackWithFailure.java @@ -17,9 +17,8 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; - import org.apache.cassandra.exceptions.RequestFailureReason; +import org.apache.cassandra.locator.InetAddressAndPort; public interface IAsyncCallbackWithFailure extends IAsyncCallback { @@ -27,5 +26,5 @@ public interface IAsyncCallbackWithFailure extends IAsyncCallback /** * Called when there is an exception on the remote node or timeout happens */ - void onFailure(InetAddress from, RequestFailureReason failureReason); + void onFailure(InetAddressAndPort from, RequestFailureReason failureReason); } diff --git a/src/java/org/apache/cassandra/net/IMessageSink.java b/src/java/org/apache/cassandra/net/IMessageSink.java index 51509018b1..090d2c21cd 100644 --- a/src/java/org/apache/cassandra/net/IMessageSink.java +++ b/src/java/org/apache/cassandra/net/IMessageSink.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; public interface IMessageSink { @@ -26,7 +26,7 @@ public interface IMessageSink * * @return true if the message is allowed, false if it should be dropped */ - boolean allowOutgoingMessage(MessageOut message, int id, InetAddress to); + boolean allowOutgoingMessage(MessageOut message, int id, InetAddressAndPort to); /** * Allow or drop an incoming message diff --git a/src/java/org/apache/cassandra/net/MessageDeliveryTask.java b/src/java/org/apache/cassandra/net/MessageDeliveryTask.java index c7fc99139f..6e132a8fa4 100644 --- a/src/java/org/apache/cassandra/net/MessageDeliveryTask.java +++ b/src/java/org/apache/cassandra/net/MessageDeliveryTask.java @@ -20,6 +20,7 @@ package org.apache.cassandra.net; import java.io.IOException; import java.util.EnumSet; +import com.google.common.primitives.Shorts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ import org.apache.cassandra.db.monitoring.ApproximateTime; import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.index.IndexNotAvailableException; +import org.apache.cassandra.io.DummyByteVersionedSerializer; import org.apache.cassandra.io.util.DataOutputBuffer; public class MessageDeliveryTask implements Runnable @@ -96,19 +98,11 @@ public class MessageDeliveryTask implements Runnable if (message.doCallbackOnFailure()) { MessageOut response = new MessageOut(MessagingService.Verb.INTERNAL_RESPONSE) - .withParameter(MessagingService.FAILURE_RESPONSE_PARAM, MessagingService.ONE_BYTE); + .withParameter(ParameterType.FAILURE_RESPONSE, MessagingService.ONE_BYTE); if (t instanceof TombstoneOverwhelmingException) { - try (DataOutputBuffer out = new DataOutputBuffer()) - { - out.writeShort(RequestFailureReason.READ_TOO_MANY_TOMBSTONES.code); - response = response.withParameter(MessagingService.FAILURE_REASON_PARAM, out.getData()); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } + response = response.withParameter(ParameterType.FAILURE_REASON, Shorts.checkedCast(RequestFailureReason.READ_TOO_MANY_TOMBSTONES.code)); } MessagingService.instance().sendReply(response, id, message.from); diff --git a/src/java/org/apache/cassandra/net/MessageIn.java b/src/java/org/apache/cassandra/net/MessageIn.java index d520fa98f6..a426ef02e4 100644 --- a/src/java/org/apache/cassandra/net/MessageIn.java +++ b/src/java/org/apache/cassandra/net/MessageIn.java @@ -18,7 +18,6 @@ package org.apache.cassandra.net; import java.io.IOException; -import java.net.InetAddress; import java.util.Collections; import java.util.Map; @@ -31,8 +30,8 @@ import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService.Verb; - /** * The receiving node's view of a {@link MessageOut}. See documentation on {@link MessageOut} for details on the * serialization format. @@ -41,16 +40,16 @@ import org.apache.cassandra.net.MessagingService.Verb; */ public class MessageIn { - public final InetAddress from; + public final InetAddressAndPort from; public final T payload; - public final Map parameters; - public final Verb verb; + public final Map parameters; + public final MessagingService.Verb verb; public final int version; public final long constructionTime; - private MessageIn(InetAddress from, + private MessageIn(InetAddressAndPort from, T payload, - Map parameters, + Map parameters, Verb verb, int version, long constructionTime) @@ -63,9 +62,9 @@ public class MessageIn this.constructionTime = constructionTime; } - public static MessageIn create(InetAddress from, + public static MessageIn create(InetAddressAndPort from, T payload, - Map parameters, + Map parameters, Verb verb, int version, long constructionTime) @@ -73,9 +72,9 @@ public class MessageIn return new MessageIn<>(from, payload, parameters, verb, version, constructionTime); } - public static MessageIn create(InetAddress from, + public static MessageIn create(InetAddressAndPort from, T payload, - Map parameters, + Map parameters, MessagingService.Verb verb, int version) { @@ -89,37 +88,46 @@ public class MessageIn public static MessageIn read(DataInputPlus in, int version, int id, long constructionTime) throws IOException { - InetAddress from = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort from = CompactEndpointSerializationHelper.instance.deserialize(in, version); MessagingService.Verb verb = MessagingService.Verb.fromId(in.readInt()); - Map parameters = readParameters(in); + Map parameters = readParameters(in, version); int payloadSize = in.readInt(); return read(in, version, id, constructionTime, from, payloadSize, verb, parameters); } - public static Map readParameters(DataInputPlus in) throws IOException + public static Map readParameters(DataInputPlus in, int version) throws IOException { int parameterCount = in.readInt(); + Map parameters; if (parameterCount == 0) { return Collections.emptyMap(); } else { - ImmutableMap.Builder builder = ImmutableMap.builder(); + ImmutableMap.Builder builder = ImmutableMap.builder(); for (int i = 0; i < parameterCount; i++) { String key = in.readUTF(); - byte[] value = new byte[in.readInt()]; - in.readFully(value); - builder.put(key, value); + ParameterType type = ParameterType.byName.get(key); + if (type != null) + { + byte[] value = new byte[in.readInt()]; + in.readFully(value); + builder.put(type, type.serializer.deserialize(new DataInputBuffer(value), version)); + } + else + { + in.skipBytes(in.readInt()); + } } return builder.build(); } } public static MessageIn read(DataInputPlus in, int version, int id, long constructionTime, - InetAddress from, int payloadSize, Verb verb, Map parameters) throws IOException + InetAddressAndPort from, int payloadSize, Verb verb, Map parameters) throws IOException { IVersionedSerializer serializer = (IVersionedSerializer) MessagingService.verbSerializers.get(verb); if (serializer instanceof MessagingService.CallbackDeterminedSerializer) @@ -140,7 +148,7 @@ public class MessageIn return MessageIn.create(from, payload, parameters, verb, version, constructionTime); } - public static long deriveConstructionTime(InetAddress from, int messageTimestamp, long currentTime) + public static long deriveConstructionTime(InetAddressAndPort from, int messageTimestamp, long currentTime) { // Reconstruct the message construction time sent by the remote host (we sent only the lower 4 bytes, assuming the // higher 4 bytes wouldn't change between the sender and receiver) @@ -182,36 +190,18 @@ public class MessageIn public boolean doCallbackOnFailure() { - return parameters.containsKey(MessagingService.FAILURE_CALLBACK_PARAM); + return parameters.containsKey(ParameterType.FAILURE_CALLBACK); } public boolean isFailureResponse() { - return parameters.containsKey(MessagingService.FAILURE_RESPONSE_PARAM); - } - - public boolean containsFailureReason() - { - return parameters.containsKey(MessagingService.FAILURE_REASON_PARAM); + return parameters.containsKey(ParameterType.FAILURE_RESPONSE); } public RequestFailureReason getFailureReason() { - if (containsFailureReason()) - { - try (DataInputBuffer in = new DataInputBuffer(parameters.get(MessagingService.FAILURE_REASON_PARAM))) - { - return RequestFailureReason.fromCode(in.readUnsignedShort()); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } - } - else - { - return RequestFailureReason.UNKNOWN; - } + Short code = (Short)parameters.get(ParameterType.FAILURE_REASON); + return code != null ? RequestFailureReason.fromCode(code) : RequestFailureReason.UNKNOWN; } public long getTimeout() diff --git a/src/java/org/apache/cassandra/net/MessageOut.java b/src/java/org/apache/cassandra/net/MessageOut.java index 379aff5ccd..7d3c0afd06 100644 --- a/src/java/org/apache/cassandra/net/MessageOut.java +++ b/src/java/org/apache/cassandra/net/MessageOut.java @@ -19,17 +19,18 @@ package org.apache.cassandra.net; import java.io.IOException; -import java.net.InetAddress; -import java.util.Collections; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; @@ -80,12 +81,20 @@ import static org.apache.cassandra.tracing.Tracing.isTracing; public class MessageOut { private static final int SERIALIZED_SIZE_VERSION_UNDEFINED = -1; + //Parameters are stored in an object array as tuples of size two + public static final int PARAMETER_TUPLE_SIZE = 2; + //Offset in a parameter tuple containing the type of the parameter + public static final int PARAMETER_TUPLE_TYPE_OFFSET = 0; + //Offset in a parameter tuple containing the actual parameter represented as a POJO + public static final int PARAMETER_TUPLE_PARAMETER_OFFSET = 1; - public final InetAddress from; + public final InetAddressAndPort from; public final MessagingService.Verb verb; public final T payload; public final IVersionedSerializer serializer; - public final Map parameters; + //A list of tuples, first object is the ParameterType enum value, + //the second object is the POJO to serialize + public final List parameters; /** * Memoization of the serialized size of the just the payload. @@ -115,16 +124,16 @@ public class MessageOut serializer, isTracing() ? Tracing.instance.getTraceHeaders() - : Collections.emptyMap()); + : ImmutableList.of()); } - private MessageOut(MessagingService.Verb verb, T payload, IVersionedSerializer serializer, Map parameters) + private MessageOut(MessagingService.Verb verb, T payload, IVersionedSerializer serializer, List parameters) { - this(FBUtilities.getBroadcastAddress(), verb, payload, serializer, parameters); + this(FBUtilities.getBroadcastAddressAndPort(), verb, payload, serializer, parameters); } @VisibleForTesting - public MessageOut(InetAddress from, MessagingService.Verb verb, T payload, IVersionedSerializer serializer, Map parameters) + public MessageOut(InetAddressAndPort from, MessagingService.Verb verb, T payload, IVersionedSerializer serializer, List parameters) { this.from = from; this.verb = verb; @@ -133,11 +142,13 @@ public class MessageOut this.parameters = parameters; } - public MessageOut withParameter(String key, byte[] value) + public MessageOut withParameter(ParameterType type, VT value) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.putAll(parameters).put(key, value); - return new MessageOut(verb, payload, serializer, builder.build()); + List newParameters = new ArrayList<>(parameters.size() + 2); + newParameters.addAll(parameters); + newParameters.add(type); + newParameters.add(value); + return new MessageOut(verb, payload, serializer, newParameters); } public Stage getStage() @@ -159,15 +170,19 @@ public class MessageOut public void serialize(DataOutputPlus out, int version) throws IOException { - CompactEndpointSerializationHelper.serialize(from, out); + CompactEndpointSerializationHelper.instance.serialize(from, out, version); out.writeInt(verb.getId()); - out.writeInt(parameters.size()); - for (Map.Entry entry : parameters.entrySet()) + assert parameters.size() % PARAMETER_TUPLE_SIZE == 0; + out.writeInt(parameters.size() / PARAMETER_TUPLE_SIZE); + for (int ii = 0; ii < parameters.size(); ii += PARAMETER_TUPLE_SIZE) { - out.writeUTF(entry.getKey()); - out.writeInt(entry.getValue().length); - out.write(entry.getValue()); + ParameterType type = (ParameterType)parameters.get(ii + PARAMETER_TUPLE_TYPE_OFFSET); + out.writeUTF(type.key); + IVersionedSerializer serializer = type.serializer; + Object parameter = parameters.get(ii + PARAMETER_TUPLE_PARAMETER_OFFSET); + out.writeInt(Ints.checkedCast(serializer.serializedSize(parameter, version))); + serializer.serialize(parameter, out, version); } if (payload != null) @@ -187,15 +202,19 @@ public class MessageOut private Pair calculateSerializedSize(int version) { - long size = CompactEndpointSerializationHelper.serializedSize(from); + long size = 0; + size += CompactEndpointSerializationHelper.instance.serializedSize(from, version); size += TypeSizes.sizeof(verb.getId()); size += TypeSizes.sizeof(parameters.size()); - for (Map.Entry entry : parameters.entrySet()) + for (int ii = 0; ii < parameters.size(); ii += PARAMETER_TUPLE_SIZE) { - size += TypeSizes.sizeof(entry.getKey()); - size += TypeSizes.sizeof(entry.getValue().length); - size += entry.getValue().length; + ParameterType type = (ParameterType)parameters.get(ii + PARAMETER_TUPLE_TYPE_OFFSET); + size += TypeSizes.sizeof(type.key()); + size += 4;//length prefix + IVersionedSerializer serializer = type.serializer; + Object parameter = parameters.get(ii + PARAMETER_TUPLE_PARAMETER_OFFSET); + size += serializer.serializedSize(parameter, version); } long payloadSize = payload == null ? 0 : serializer.serializedSize(payload, version); @@ -237,4 +256,16 @@ public class MessageOut return sizes.left.intValue(); } + + public Object getParameter(ParameterType type) + { + for (int ii = 0; ii < parameters.size(); ii += PARAMETER_TUPLE_SIZE) + { + if (((ParameterType)parameters.get(ii + PARAMETER_TUPLE_TYPE_OFFSET)).equals(type)) + { + return parameters.get(ii + PARAMETER_TUPLE_PARAMETER_OFFSET); + } + } + return null; + } } diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java index 4e6fe1c3c9..9f00d27c3e 100644 --- a/src/java/org/apache/cassandra/net/MessagingService.java +++ b/src/java/org/apache/cassandra/net/MessagingService.java @@ -20,8 +20,6 @@ package org.apache.cassandra.net; import java.io.IOError; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -37,6 +35,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; + import javax.management.MBeanServer; import javax.management.ObjectName; @@ -91,6 +90,7 @@ import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.locator.IEndpointSnitch; import org.apache.cassandra.locator.ILatencySubscriber; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.CassandraMetricsRegistry; import org.apache.cassandra.metrics.ConnectionMetrics; import org.apache.cassandra.metrics.DroppedMessageMetrics; @@ -127,10 +127,7 @@ public final class MessagingService implements MessagingServiceMBean public static final int VERSION_40 = 12; public static final int current_version = VERSION_40; - public static final String FAILURE_CALLBACK_PARAM = "CAL_BAC"; public static final byte[] ONE_BYTE = new byte[1]; - public static final String FAILURE_RESPONSE_PARAM = "FAIL"; - public static final String FAILURE_REASON_PARAM = "FAIL_REASON"; /** * we preface every message with this number so the recipient can validate the sender is sane @@ -447,7 +444,7 @@ public final class MessagingService implements MessagingServiceMBean private final Map verbHandlers; @VisibleForTesting - public final ConcurrentMap channelManagers = new NonBlockingHashMap<>(); + public final ConcurrentMap channelManagers = new NonBlockingHashMap<>(); final List serverChannels = Lists.newArrayList(); private static final Logger logger = LoggerFactory.getLogger(MessagingService.class); @@ -506,7 +503,7 @@ public final class MessagingService implements MessagingServiceMBean private final List subscribers = new ArrayList(); // protocol versions of the other nodes in the cluster - private final ConcurrentMap versions = new NonBlockingHashMap(); + private final ConcurrentMap versions = new NonBlockingHashMap<>(); // message sinks are a testing hook private final Set messageSinks = new CopyOnWriteArraySet<>(); @@ -629,7 +626,7 @@ public final class MessagingService implements MessagingServiceMBean * @param callback The message callback. * @param message The actual message. */ - public void updateBackPressureOnSend(InetAddress host, IAsyncCallback callback, MessageOut message) + public void updateBackPressureOnSend(InetAddressAndPort host, IAsyncCallback callback, MessageOut message) { if (DatabaseDescriptor.backPressureEnabled() && callback.supportsBackPressure()) { @@ -646,7 +643,7 @@ public final class MessagingService implements MessagingServiceMBean * @param callback The message callback. * @param timeout True if updated following a timeout, false otherwise. */ - public void updateBackPressureOnReceive(InetAddress host, IAsyncCallback callback, boolean timeout) + public void updateBackPressureOnReceive(InetAddressAndPort host, IAsyncCallback callback, boolean timeout) { if (DatabaseDescriptor.backPressureEnabled() && callback.supportsBackPressure()) { @@ -669,14 +666,14 @@ public final class MessagingService implements MessagingServiceMBean * @param hosts The hosts to apply back-pressure to. * @param timeoutInNanos The max back-pressure timeout. */ - public void applyBackPressure(Iterable hosts, long timeoutInNanos) + public void applyBackPressure(Iterable hosts, long timeoutInNanos) { if (DatabaseDescriptor.backPressureEnabled()) { Set states = new HashSet(); - for (InetAddress host : hosts) + for (InetAddressAndPort host : hosts) { - if (host.equals(FBUtilities.getBroadcastAddress())) + if (host.equals(FBUtilities.getBroadcastAddressAndPort())) continue; OutboundMessagingPool pool = getMessagingConnection(host); if (pool != null) @@ -686,13 +683,13 @@ public final class MessagingService implements MessagingServiceMBean } } - BackPressureState getBackPressureState(InetAddress host) + BackPressureState getBackPressureState(InetAddressAndPort host) { OutboundMessagingPool messagingConnection = getMessagingConnection(host); return messagingConnection != null ? messagingConnection.getBackPressureState() : null; } - void markTimeout(InetAddress addr) + void markTimeout(InetAddressAndPort addr) { OutboundMessagingPool conn = channelManagers.get(addr); if (conn != null) @@ -706,13 +703,13 @@ public final class MessagingService implements MessagingServiceMBean * @param address the host that replied to the message * @param latency */ - public void maybeAddLatency(IAsyncCallback cb, InetAddress address, long latency) + public void maybeAddLatency(IAsyncCallback cb, InetAddressAndPort address, long latency) { if (cb.isLatencyForSnitch()) addLatency(address, latency); } - public void addLatency(InetAddress address, long latency) + public void addLatency(InetAddressAndPort address, long latency) { for (ILatencySubscriber subscriber : subscribers) subscriber.receiveTiming(address, latency); @@ -721,7 +718,7 @@ public final class MessagingService implements MessagingServiceMBean /** * called from gossiper when it notices a node is not responding. */ - public void convict(InetAddress ep) + public void convict(InetAddressAndPort ep) { logger.trace("Resetting pool for {}", ep); reset(ep); @@ -735,24 +732,24 @@ public final class MessagingService implements MessagingServiceMBean public void listen(ServerEncryptionOptions serverEncryptionOptions) { callbacks.reset(); // hack to allow tests to stop/restart MS - listen(FBUtilities.getLocalAddress(), serverEncryptionOptions); + listen(FBUtilities.getLocalAddressAndPort(), serverEncryptionOptions); if (shouldListenOnBroadcastAddress()) - listen(FBUtilities.getBroadcastAddress(), serverEncryptionOptions); + listen(FBUtilities.getBroadcastAddressAndPort(), serverEncryptionOptions); listenGate.signalAll(); } public static boolean shouldListenOnBroadcastAddress() { return DatabaseDescriptor.shouldListenOnBroadcastAddress() - && !FBUtilities.getLocalAddress().equals(FBUtilities.getBroadcastAddress()); + && !FBUtilities.getLocalAddressAndPort().equals(FBUtilities.getBroadcastAddressAndPort()); } /** * Listen on the specified port. * - * @param localEp InetAddress whose port to listen on. + * @param localEp InetAddressAndPort whose port to listen on. */ - private void listen(InetAddress localEp, ServerEncryptionOptions serverEncryptionOptions) throws ConfigurationException + private void listen(InetAddressAndPort localEp, ServerEncryptionOptions serverEncryptionOptions) throws ConfigurationException { IInternodeAuthenticator authenticator = DatabaseDescriptor.getInternodeAuthenticator(); int receiveBufferSize = DatabaseDescriptor.getInternodeRecvBufferSize(); @@ -766,7 +763,7 @@ public final class MessagingService implements MessagingServiceMBean ServerEncryptionOptions legacyEncOptions = new ServerEncryptionOptions(serverEncryptionOptions); legacyEncOptions.optional = false; - InetSocketAddress localAddr = new InetSocketAddress(localEp, DatabaseDescriptor.getSSLStoragePort()); + InetAddressAndPort localAddr = InetAddressAndPort.getByAddressOverrideDefaults(localEp.address, DatabaseDescriptor.getSSLStoragePort()); ChannelGroup channelGroup = new DefaultChannelGroup("LegacyEncryptedInternodeMessagingGroup", NettyFactory.executorForChannelGroups()); InboundInitializer initializer = new InboundInitializer(authenticator, legacyEncOptions, channelGroup); Channel encryptedChannel = NettyFactory.instance.createInboundChannel(localAddr, initializer, receiveBufferSize); @@ -774,7 +771,8 @@ public final class MessagingService implements MessagingServiceMBean } // this is for the socket that can be plain, only ssl, or optional plain/ssl - InetSocketAddress localAddr = new InetSocketAddress(localEp, DatabaseDescriptor.getStoragePort()); + assert localEp.port == DatabaseDescriptor.getStoragePort() : String.format("Local endpoint port %d doesn't match YAML configured port %d%n", localEp.port, DatabaseDescriptor.getStoragePort()); + InetAddressAndPort localAddr = InetAddressAndPort.getByAddressOverrideDefaults(localEp.address, DatabaseDescriptor.getStoragePort()); ChannelGroup channelGroup = new DefaultChannelGroup("InternodeMessagingGroup", NettyFactory.executorForChannelGroups()); InboundInitializer initializer = new InboundInitializer(authenticator, serverEncryptionOptions, channelGroup); Channel channel = NettyFactory.instance.createInboundChannel(localAddr, initializer, receiveBufferSize); @@ -809,10 +807,10 @@ public final class MessagingService implements MessagingServiceMBean * the inbound connections/channels can be closed when the listening socket itself is being closed. */ private final ChannelGroup connectedChannels; - private final InetSocketAddress address; + private final InetAddressAndPort address; private final SecurityLevel securityLevel; - private ServerChannel(Channel channel, ChannelGroup channelGroup, InetSocketAddress address, SecurityLevel securityLevel) + private ServerChannel(Channel channel, ChannelGroup channelGroup, InetAddressAndPort address, SecurityLevel securityLevel) { this.channel = channel; this.connectedChannels = channelGroup; @@ -840,7 +838,7 @@ public final class MessagingService implements MessagingServiceMBean return channel; } - InetSocketAddress getAddress() + InetAddressAndPort getAddress() { return address; } @@ -869,7 +867,7 @@ public final class MessagingService implements MessagingServiceMBean } - public void destroyConnectionPool(InetAddress to) + public void destroyConnectionPool(InetAddressAndPort to) { OutboundMessagingPool pool = channelManagers.remove(to); if (pool != null) @@ -884,26 +882,26 @@ public final class MessagingService implements MessagingServiceMBean * @param address IP Address to identify the peer * @param preferredAddress IP Address to use (and prefer) going forward for connecting to the peer */ - public void reconnectWithNewIp(InetAddress address, InetAddress preferredAddress) + public void reconnectWithNewIp(InetAddressAndPort address, InetAddressAndPort preferredAddress) { SystemKeyspace.updatePreferredIP(address, preferredAddress); OutboundMessagingPool messagingPool = channelManagers.get(address); if (messagingPool != null) - messagingPool.reconnectWithNewIp(new InetSocketAddress(preferredAddress, portFor(address))); + messagingPool.reconnectWithNewIp(InetAddressAndPort.getByAddressOverrideDefaults(preferredAddress.address, portFor(address))); } - private void reset(InetAddress address) + private void reset(InetAddressAndPort address) { OutboundMessagingPool messagingPool = channelManagers.remove(address); if (messagingPool != null) messagingPool.close(false); } - public InetAddress getCurrentEndpoint(InetAddress publicAddress) + public InetAddressAndPort getCurrentEndpoint(InetAddressAndPort publicAddress) { OutboundMessagingPool messagingPool = getMessagingConnection(publicAddress); - return messagingPool != null ? messagingPool.getPreferredRemoteAddr().getAddress() : null; + return messagingPool != null ? messagingPool.getPreferredRemoteAddr() : null; } /** @@ -931,7 +929,7 @@ public final class MessagingService implements MessagingServiceMBean return verbHandlers.get(type); } - public int addCallback(IAsyncCallback cb, MessageOut message, InetAddress to, long timeout, boolean failureCallback) + public int addCallback(IAsyncCallback cb, MessageOut message, InetAddressAndPort to, long timeout, boolean failureCallback) { assert message.verb != Verb.MUTATION; // mutations need to call the overload with a ConsistencyLevel int messageId = nextId(); @@ -942,7 +940,7 @@ public final class MessagingService implements MessagingServiceMBean public int addCallback(IAsyncCallback cb, MessageOut message, - InetAddress to, + InetAddressAndPort to, long timeout, ConsistencyLevel consistencyLevel, boolean allowHints) @@ -971,12 +969,12 @@ public final class MessagingService implements MessagingServiceMBean return idGen.incrementAndGet(); } - public int sendRR(MessageOut message, InetAddress to, IAsyncCallback cb) + public int sendRR(MessageOut message, InetAddressAndPort to, IAsyncCallback cb) { return sendRR(message, to, cb, message.getTimeout(), false); } - public int sendRRWithFailure(MessageOut message, InetAddress to, IAsyncCallbackWithFailure cb) + public int sendRRWithFailure(MessageOut message, InetAddressAndPort to, IAsyncCallbackWithFailure cb) { return sendRR(message, to, cb, message.getTimeout(), true); } @@ -992,11 +990,11 @@ public final class MessagingService implements MessagingServiceMBean * @param timeout the timeout used for expiration * @return an reference to message id used to match with the result */ - public int sendRR(MessageOut message, InetAddress to, IAsyncCallback cb, long timeout, boolean failureCallback) + public int sendRR(MessageOut message, InetAddressAndPort to, IAsyncCallback cb, long timeout, boolean failureCallback) { int id = addCallback(cb, message, to, timeout, failureCallback); updateBackPressureOnSend(to, cb, message); - sendOneWay(failureCallback ? message.withParameter(FAILURE_CALLBACK_PARAM, ONE_BYTE) : message, id, to); + sendOneWay(failureCallback ? message.withParameter(ParameterType.FAILURE_CALLBACK, ONE_BYTE) : message, id, to); return id; } @@ -1013,22 +1011,22 @@ public final class MessagingService implements MessagingServiceMBean * @return an reference to message id used to match with the result */ public int sendRR(MessageOut message, - InetAddress to, + InetAddressAndPort to, AbstractWriteResponseHandler handler, boolean allowHints) { int id = addCallback(handler, message, to, message.getTimeout(), handler.consistencyLevel, allowHints); updateBackPressureOnSend(to, handler, message); - sendOneWay(message.withParameter(FAILURE_CALLBACK_PARAM, ONE_BYTE), id, to); + sendOneWay(message.withParameter(ParameterType.FAILURE_CALLBACK, ONE_BYTE), id, to); return id; } - public void sendOneWay(MessageOut message, InetAddress to) + public void sendOneWay(MessageOut message, InetAddressAndPort to) { sendOneWay(message, nextId(), to); } - public void sendReply(MessageOut message, int id, InetAddress to) + public void sendReply(MessageOut message, int id, InetAddressAndPort to) { sendOneWay(message, id, to); } @@ -1040,12 +1038,12 @@ public final class MessagingService implements MessagingServiceMBean * @param message messages to be sent. * @param to endpoint to which the message needs to be sent */ - public void sendOneWay(MessageOut message, int id, InetAddress to) + public void sendOneWay(MessageOut message, int id, InetAddressAndPort to) { if (logger.isTraceEnabled()) - logger.trace("{} sending {} to {}@{}", FBUtilities.getBroadcastAddress(), message.verb, id, to); + logger.trace("{} sending {} to {}@{}", FBUtilities.getBroadcastAddressAndPort(), message.verb, id, to); - if (to.equals(FBUtilities.getBroadcastAddress())) + if (to.equals(FBUtilities.getBroadcastAddressAndPort())) logger.trace("Message-to-self {} going over MessagingService", message); // message sinks are a testing hook @@ -1058,7 +1056,7 @@ public final class MessagingService implements MessagingServiceMBean outboundMessagingPool.sendMessage(message, id); } - public AsyncOneResponse sendRR(MessageOut message, InetAddress to) + public AsyncOneResponse sendRR(MessageOut message, InetAddressAndPort to) { AsyncOneResponse iar = new AsyncOneResponse(); sendRR(message, to, iar); @@ -1176,7 +1174,7 @@ public final class MessagingService implements MessagingServiceMBean /** * @return the last version associated with address, or @param version if this is the first such version */ - public int setVersion(InetAddress endpoint, int version) + public int setVersion(InetAddressAndPort endpoint, int version) { logger.trace("Setting version {} for {}", version, endpoint); @@ -1184,7 +1182,7 @@ public final class MessagingService implements MessagingServiceMBean return v == null ? version : v; } - public void resetVersion(InetAddress endpoint) + public void resetVersion(InetAddressAndPort endpoint) { logger.trace("Resetting version for {}", endpoint); versions.remove(endpoint); @@ -1194,7 +1192,7 @@ public final class MessagingService implements MessagingServiceMBean * Returns the messaging-version as announced by the given node but capped * to the min of the version as announced by the node and {@link #current_version}. */ - public int getVersion(InetAddress endpoint) + public int getVersion(InetAddressAndPort endpoint) { Integer v = versions.get(endpoint); if (v == null) @@ -1209,13 +1207,13 @@ public final class MessagingService implements MessagingServiceMBean public int getVersion(String endpoint) throws UnknownHostException { - return getVersion(InetAddress.getByName(endpoint)); + return getVersion(InetAddressAndPort.getByName(endpoint)); } /** * Returns the messaging-version exactly as announced by the given endpoint. */ - public int getRawVersion(InetAddress endpoint) + public int getRawVersion(InetAddressAndPort endpoint) { Integer v = versions.get(endpoint); if (v == null) @@ -1223,7 +1221,7 @@ public final class MessagingService implements MessagingServiceMBean return v; } - public boolean knowsVersion(InetAddress endpoint) + public boolean knowsVersion(InetAddressAndPort endpoint) { return versions.containsKey(endpoint); } @@ -1358,72 +1356,144 @@ public final class MessagingService implements MessagingServiceMBean public Map getLargeMessagePendingTasks() { Map pendingTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - pendingTasks.put(entry.getKey().getHostAddress(), entry.getValue().largeMessageChannel.getPendingMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(false), entry.getValue().largeMessageChannel.getPendingMessages()); return pendingTasks; } public Map getLargeMessageCompletedTasks() { Map completedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - completedTasks.put(entry.getKey().getHostAddress(), entry.getValue().largeMessageChannel.getCompletedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(false), entry.getValue().largeMessageChannel.getCompletedMessages()); return completedTasks; } public Map getLargeMessageDroppedTasks() { Map droppedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - droppedTasks.put(entry.getKey().getHostAddress(), entry.getValue().largeMessageChannel.getDroppedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(false), entry.getValue().largeMessageChannel.getDroppedMessages()); return droppedTasks; } public Map getSmallMessagePendingTasks() { Map pendingTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - pendingTasks.put(entry.getKey().getHostAddress(), entry.getValue().smallMessageChannel.getPendingMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(false), entry.getValue().smallMessageChannel.getPendingMessages()); return pendingTasks; } public Map getSmallMessageCompletedTasks() { Map completedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - completedTasks.put(entry.getKey().getHostAddress(), entry.getValue().smallMessageChannel.getCompletedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(false), entry.getValue().smallMessageChannel.getCompletedMessages()); return completedTasks; } public Map getSmallMessageDroppedTasks() { Map droppedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - droppedTasks.put(entry.getKey().getHostAddress(), entry.getValue().smallMessageChannel.getDroppedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(false), entry.getValue().smallMessageChannel.getDroppedMessages()); return droppedTasks; } public Map getGossipMessagePendingTasks() { Map pendingTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - pendingTasks.put(entry.getKey().getHostAddress(), entry.getValue().gossipChannel.getPendingMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(false), entry.getValue().gossipChannel.getPendingMessages()); return pendingTasks; } public Map getGossipMessageCompletedTasks() { Map completedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - completedTasks.put(entry.getKey().getHostAddress(), entry.getValue().gossipChannel.getCompletedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(false), entry.getValue().gossipChannel.getCompletedMessages()); return completedTasks; } public Map getGossipMessageDroppedTasks() { Map droppedTasks = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - droppedTasks.put(entry.getKey().getHostAddress(), entry.getValue().gossipChannel.getDroppedMessages()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(false), entry.getValue().gossipChannel.getDroppedMessages()); + return droppedTasks; + } + + public Map getLargeMessagePendingTasksWithPort() + { + Map pendingTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(), entry.getValue().largeMessageChannel.getPendingMessages()); + return pendingTasks; + } + + public Map getLargeMessageCompletedTasksWithPort() + { + Map completedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(), entry.getValue().largeMessageChannel.getCompletedMessages()); + return completedTasks; + } + + public Map getLargeMessageDroppedTasksWithPort() + { + Map droppedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(), entry.getValue().largeMessageChannel.getDroppedMessages()); + return droppedTasks; + } + + public Map getSmallMessagePendingTasksWithPort() + { + Map pendingTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(), entry.getValue().smallMessageChannel.getPendingMessages()); + return pendingTasks; + } + + public Map getSmallMessageCompletedTasksWithPort() + { + Map completedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(), entry.getValue().smallMessageChannel.getCompletedMessages()); + return completedTasks; + } + + public Map getSmallMessageDroppedTasksWithPort() + { + Map droppedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(), entry.getValue().smallMessageChannel.getDroppedMessages()); + return droppedTasks; + } + + public Map getGossipMessagePendingTasksWithPort() + { + Map pendingTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + pendingTasks.put(entry.getKey().toString(), entry.getValue().gossipChannel.getPendingMessages()); + return pendingTasks; + } + + public Map getGossipMessageCompletedTasksWithPort() + { + Map completedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + completedTasks.put(entry.getKey().toString(), entry.getValue().gossipChannel.getCompletedMessages()); + return completedTasks; + } + + public Map getGossipMessageDroppedTasksWithPort() + { + Map droppedTasks = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + droppedTasks.put(entry.getKey().toString(), entry.getValue().gossipChannel.getDroppedMessages()); return droppedTasks; } @@ -1435,7 +1505,6 @@ public final class MessagingService implements MessagingServiceMBean return map; } - public long getTotalTimeouts() { return ConnectionMetrics.totalTimeouts.getCount(); @@ -1444,9 +1513,21 @@ public final class MessagingService implements MessagingServiceMBean public Map getTimeoutsPerHost() { Map result = new HashMap(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) + for (Map.Entry entry : channelManagers.entrySet()) { - String ip = entry.getKey().getHostAddress(); + String ip = entry.getKey().toString(false); + long recent = entry.getValue().getTimeouts(); + result.put(ip, recent); + } + return result; + } + + public Map getTimeoutsPerHostWithPort() + { + Map result = new HashMap(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + { + String ip = entry.getKey().toString(); long recent = entry.getValue().getTimeouts(); result.put(ip, recent); } @@ -1456,8 +1537,17 @@ public final class MessagingService implements MessagingServiceMBean public Map getBackPressurePerHost() { Map map = new HashMap<>(channelManagers.size()); - for (Map.Entry entry : channelManagers.entrySet()) - map.put(entry.getKey().getHostAddress(), entry.getValue().getBackPressureState().getBackPressureRateLimit()); + for (Map.Entry entry : channelManagers.entrySet()) + map.put(entry.getKey().toString(false), entry.getValue().getBackPressureState().getBackPressureRateLimit()); + + return map; + } + + public Map getBackPressurePerHostWithPort() + { + Map map = new HashMap<>(channelManagers.size()); + for (Map.Entry entry : channelManagers.entrySet()) + map.put(entry.getKey().toString(false), entry.getValue().getBackPressureState().getBackPressureRateLimit()); return map; } @@ -1493,18 +1583,18 @@ public final class MessagingService implements MessagingServiceMBean bounds.left.getPartitioner().getClass().getName())); } - private OutboundMessagingPool getMessagingConnection(InetAddress to) + private OutboundMessagingPool getMessagingConnection(InetAddressAndPort to) { OutboundMessagingPool pool = channelManagers.get(to); if (pool == null) { final boolean secure = isEncryptedConnection(to); final int port = portFor(to, secure); - if (!DatabaseDescriptor.getInternodeAuthenticator().authenticate(to, port)) + if (!DatabaseDescriptor.getInternodeAuthenticator().authenticate(to.address, port)) return null; - InetSocketAddress preferredRemote = new InetSocketAddress(SystemKeyspace.getPreferredIP(to), port); - InetSocketAddress local = new InetSocketAddress(FBUtilities.getLocalAddress(), 0); + InetAddressAndPort preferredRemote = SystemKeyspace.getPreferredIP(to); + InetAddressAndPort local = FBUtilities.getLocalAddressAndPort(); ServerEncryptionOptions encryptionOptions = secure ? DatabaseDescriptor.getServerEncryptionOptions() : null; IInternodeAuthenticator authenticator = DatabaseDescriptor.getInternodeAuthenticator(); @@ -1519,16 +1609,16 @@ public final class MessagingService implements MessagingServiceMBean return pool; } - public int portFor(InetAddress addr) + public int portFor(InetAddressAndPort addr) { final boolean secure = isEncryptedConnection(addr); return portFor(addr, secure); } - private int portFor(InetAddress address, boolean secure) + private int portFor(InetAddressAndPort address, boolean secure) { if (!secure) - return DatabaseDescriptor.getStoragePort(); + return address.port; Integer v = versions.get(address); // if we don't know the version of the peer, assume it is 4.0 (or higher) as the only time is would be lower @@ -1536,12 +1626,15 @@ public final class MessagingService implements MessagingServiceMBean // unfortunately fail - however the peer should connect to this node (at some point), and once we learn it's version, it'll be // in versions map. thus, when we attempt to reconnect to that node, we'll have the version and we can get the correct port. // we will be able to remove this logic at 5.0. + // Also as of 4.0 we will propagate the "regular" port (which will support both SSL and non-SSL) via gossip so + // for SSL and version 4.0 always connect to the gossiped port because if SSL is enabled it should ALWAYS + // listen for SSL on the "regular" port. int version = v != null ? v.intValue() : VERSION_40; - return version < VERSION_40 ? DatabaseDescriptor.getSSLStoragePort() : DatabaseDescriptor.getStoragePort(); + return version < VERSION_40 ? DatabaseDescriptor.getSSLStoragePort() : address.port; } @VisibleForTesting - boolean isConnected(InetAddress address, MessageOut messageOut) + boolean isConnected(InetAddressAndPort address, MessageOut messageOut) { OutboundMessagingPool pool = channelManagers.get(address); if (pool == null) @@ -1549,7 +1642,7 @@ public final class MessagingService implements MessagingServiceMBean return pool.getConnection(messageOut).isConnected(); } - public static boolean isEncryptedConnection(InetAddress address) + public static boolean isEncryptedConnection(InetAddressAndPort address) { IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); switch (DatabaseDescriptor.getServerEncryptionOptions().internode_encryption) @@ -1559,13 +1652,13 @@ public final class MessagingService implements MessagingServiceMBean case all: break; case dc: - if (snitch.getDatacenter(address).equals(snitch.getDatacenter(FBUtilities.getBroadcastAddress()))) + if (snitch.getDatacenter(address).equals(snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort()))) return false; break; case rack: // for rack then check if the DC's are the same. - if (snitch.getRack(address).equals(snitch.getRack(FBUtilities.getBroadcastAddress())) - && snitch.getDatacenter(address).equals(snitch.getDatacenter(FBUtilities.getBroadcastAddress()))) + if (snitch.getRack(address).equals(snitch.getRack(FBUtilities.getBroadcastAddressAndPort())) + && snitch.getDatacenter(address).equals(snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort()))) return false; break; } diff --git a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java index b2e79e070c..f4a0c434cf 100644 --- a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java +++ b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java @@ -30,47 +30,69 @@ public interface MessagingServiceMBean /** * Pending tasks for large message TCP Connections */ + @Deprecated public Map getLargeMessagePendingTasks(); + public Map getLargeMessagePendingTasksWithPort(); /** * Completed tasks for large message) TCP Connections */ + @Deprecated public Map getLargeMessageCompletedTasks(); + public Map getLargeMessageCompletedTasksWithPort(); /** * Dropped tasks for large message TCP Connections */ + @Deprecated public Map getLargeMessageDroppedTasks(); + public Map getLargeMessageDroppedTasksWithPort(); + /** * Pending tasks for small message TCP Connections */ + @Deprecated public Map getSmallMessagePendingTasks(); + public Map getSmallMessagePendingTasksWithPort(); + /** * Completed tasks for small message TCP Connections */ + @Deprecated public Map getSmallMessageCompletedTasks(); + public Map getSmallMessageCompletedTasksWithPort(); + /** * Dropped tasks for small message TCP Connections */ + @Deprecated public Map getSmallMessageDroppedTasks(); + public Map getSmallMessageDroppedTasksWithPort(); + /** * Pending tasks for gossip message TCP Connections */ + @Deprecated public Map getGossipMessagePendingTasks(); + public Map getGossipMessagePendingTasksWithPort(); /** * Completed tasks for gossip message TCP Connections */ + @Deprecated public Map getGossipMessageCompletedTasks(); + public Map getGossipMessageCompletedTasksWithPort(); /** * Dropped tasks for gossip message TCP Connections */ + @Deprecated public Map getGossipMessageDroppedTasks(); + public Map getGossipMessageDroppedTasksWithPort(); /** * dropped message counts for server lifetime @@ -85,12 +107,16 @@ public interface MessagingServiceMBean /** * Number of timeouts per host */ + @Deprecated public Map getTimeoutsPerHost(); + public Map getTimeoutsPerHostWithPort(); /** * Back-pressure rate limiting per host */ + @Deprecated public Map getBackPressurePerHost(); + public Map getBackPressurePerHostWithPort(); /** * Enable/Disable back-pressure diff --git a/src/java/org/apache/cassandra/net/ParameterType.java b/src/java/org/apache/cassandra/net/ParameterType.java new file mode 100644 index 0000000000..0a1f73f329 --- /dev/null +++ b/src/java/org/apache/cassandra/net/ParameterType.java @@ -0,0 +1,69 @@ +/* + * 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.cassandra.net; + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +import org.apache.cassandra.io.DummyByteVersionedSerializer; +import org.apache.cassandra.io.IVersionedSerializer; +import org.apache.cassandra.io.ShortVersionedSerializer; +import org.apache.cassandra.tracing.Tracing; +import org.apache.cassandra.utils.UUIDSerializer; + +/** + * Type names and serializers for various parameters that + */ +public enum ParameterType +{ + FORWARD_TO("FORWARD_TO", ForwardToSerializer.instance), + FORWARD_FROM("FORWARD_FROM", CompactEndpointSerializationHelper.instance), + FAILURE_RESPONSE("FAIL", DummyByteVersionedSerializer.instance), + FAILURE_REASON("FAIL_REASON", ShortVersionedSerializer.instance), + FAILURE_CALLBACK("CAL_BAC", DummyByteVersionedSerializer.instance), + TRACE_SESSION("TraceSession", UUIDSerializer.serializer), + TRACE_TYPE("TraceType", Tracing.traceTypeSerializer); + + public static final Map byName; + public final String key; + public final IVersionedSerializer serializer; + + static + { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (ParameterType type : values()) + { + builder.put(type.key, type); + } + byName = builder.build(); + } + + ParameterType(String key, IVersionedSerializer serializer) + { + this.key = key; + this.serializer = serializer; + } + + public String key() + { + return key; + } + +} diff --git a/src/java/org/apache/cassandra/net/RateBasedBackPressure.java b/src/java/org/apache/cassandra/net/RateBasedBackPressure.java index 64685b03d4..b951bc0322 100644 --- a/src/java/org/apache/cassandra/net/RateBasedBackPressure.java +++ b/src/java/org/apache/cassandra/net/RateBasedBackPressure.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -34,6 +33,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.ParameterizedClass; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.NoSpamLogger; import org.apache.cassandra.utils.SystemTimeSource; import org.apache.cassandra.utils.TimeSource; @@ -253,7 +253,7 @@ public class RateBasedBackPressure implements BackPressureStrategy serializer, @@ -43,7 +42,7 @@ public class WriteCallbackInfo extends CallbackInfo assert message != null; this.mutation = shouldHint(allowHints, message, consistencyLevel); //Local writes shouldn't go through messaging service (https://issues.apache.org/jira/browse/CASSANDRA-10477) - assert (!target.equals(FBUtilities.getBroadcastAddress())); + assert (!target.equals(FBUtilities.getBroadcastAddressAndPort())); } public boolean shouldHint() diff --git a/src/java/org/apache/cassandra/net/async/HandshakeProtocol.java b/src/java/org/apache/cassandra/net/async/HandshakeProtocol.java index 9b8df80434..327b20e736 100644 --- a/src/java/org/apache/cassandra/net/async/HandshakeProtocol.java +++ b/src/java/org/apache/cassandra/net/async/HandshakeProtocol.java @@ -24,11 +24,15 @@ import java.net.InetAddress; import java.util.Objects; import com.google.common.annotations.VisibleForTesting; +import com.google.common.primitives.Ints; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; @@ -227,9 +231,9 @@ public class HandshakeProtocol private static final int MIN_LENGTH = 9; final int messagingVersion; - final InetAddress address; + final InetAddressAndPort address; - ThirdHandshakeMessage(int messagingVersion, InetAddress address) + ThirdHandshakeMessage(int messagingVersion, InetAddressAndPort address) { this.messagingVersion = messagingVersion; this.address = address; @@ -238,14 +242,14 @@ public class HandshakeProtocol @SuppressWarnings("resource") public ByteBuf encode(ByteBufAllocator allocator) { - int bufLength = Integer.BYTES + CompactEndpointSerializationHelper.serializedSize(address); + int bufLength = Ints.checkedCast(Integer.BYTES + CompactEndpointSerializationHelper.instance.serializedSize(address, messagingVersion)); ByteBuf buffer = allocator.directBuffer(bufLength, bufLength); buffer.writerIndex(0); buffer.writeInt(messagingVersion); try { - DataOutput bbos = new ByteBufOutputStream(buffer); - CompactEndpointSerializationHelper.serialize(address, bbos); + DataOutputPlus dop = new ByteBufDataOutputPlus(buffer); + CompactEndpointSerializationHelper.instance.serialize(address, dop, messagingVersion); return buffer; } catch (IOException e) @@ -263,10 +267,10 @@ public class HandshakeProtocol in.markReaderIndex(); int version = in.readInt(); - DataInput inputStream = new ByteBufInputStream(in); + DataInputPlus input = new ByteBufDataInputPlus(in); try { - InetAddress address = CompactEndpointSerializationHelper.deserialize(inputStream); + InetAddressAndPort address = CompactEndpointSerializationHelper.instance.deserialize(input, version); return new ThirdHandshakeMessage(version, address); } catch (IOException e) diff --git a/src/java/org/apache/cassandra/net/async/InboundHandshakeHandler.java b/src/java/org/apache/cassandra/net/async/InboundHandshakeHandler.java index 625f03d820..a84112e7ef 100644 --- a/src/java/org/apache/cassandra/net/async/InboundHandshakeHandler.java +++ b/src/java/org/apache/cassandra/net/async/InboundHandshakeHandler.java @@ -22,6 +22,7 @@ import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.ssl.SslHandler; import org.apache.cassandra.auth.IInternodeAuthenticator; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.HandshakeProtocol.FirstHandshakeMessage; import org.apache.cassandra.net.async.HandshakeProtocol.SecondHandshakeMessage; @@ -209,7 +210,7 @@ class InboundHandshakeHandler extends ByteToMessageDecoder { ChannelPipeline pipeline = ctx.pipeline(); InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); - pipeline.addLast(NettyFactory.instance.streamingGroup, "streamInbound", new StreamingInboundHandler(address, protocolVersion, null)); + pipeline.addLast(NettyFactory.instance.streamingGroup, "streamInbound", new StreamingInboundHandler(InetAddressAndPort.getByAddressOverrideDefaults(address.getAddress(), address.getPort()), protocolVersion, null)); pipeline.remove(this); // pass a custom recv ByteBuf allocator to the channel. the default recv ByteBuf size is 1k, but in streaming we're @@ -244,7 +245,7 @@ class InboundHandshakeHandler extends ByteToMessageDecoder } // record the (true) version of the endpoint - InetAddress from = msg.address; + InetAddressAndPort from = msg.address; MessagingService.instance().setVersion(from, maxVersion); logger.trace("Set version for {} to {} (will use {})", from, maxVersion, MessagingService.instance().getVersion(from)); @@ -253,7 +254,7 @@ class InboundHandshakeHandler extends ByteToMessageDecoder } @VisibleForTesting - void setupMessagingPipeline(ChannelPipeline pipeline, InetAddress peer, boolean compressed, int messagingVersion) + void setupMessagingPipeline(ChannelPipeline pipeline, InetAddressAndPort peer, boolean compressed, int messagingVersion) { if (compressed) pipeline.addLast(NettyFactory.INBOUND_COMPRESSOR_HANDLER_NAME, NettyFactory.createLz4Decoder(messagingVersion)); diff --git a/src/java/org/apache/cassandra/net/async/MessageInHandler.java b/src/java/org/apache/cassandra/net/async/MessageInHandler.java index b400512c10..0423b8035d 100644 --- a/src/java/org/apache/cassandra/net/async/MessageInHandler.java +++ b/src/java/org/apache/cassandra/net/async/MessageInHandler.java @@ -21,7 +21,6 @@ package org.apache.cassandra.net.async; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; -import java.net.InetAddress; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -37,9 +36,12 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import org.apache.cassandra.db.monitoring.ApproximateTime; import org.apache.cassandra.exceptions.UnknownTableException; +import org.apache.cassandra.io.util.DataInputBuffer; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; /** * Parses out individual messages from the incoming buffers. Each message, both header and payload, is incrementally built up @@ -79,7 +81,7 @@ class MessageInHandler extends ByteToMessageDecoder */ private static final int SECOND_SECTION_BYTE_COUNT = 8; - private final InetAddress peer; + private final InetAddressAndPort peer; private final int messagingVersion; /** @@ -91,12 +93,12 @@ class MessageInHandler extends ByteToMessageDecoder private State state; private MessageHeader messageHeader; - MessageInHandler(InetAddress peer, int messagingVersion) + MessageInHandler(InetAddressAndPort peer, int messagingVersion) { this (peer, messagingVersion, MESSAGING_SERVICE_CONSUMER); } - MessageInHandler(InetAddress peer, int messagingVersion, BiConsumer messageConsumer) + MessageInHandler(InetAddressAndPort peer, int messagingVersion, BiConsumer messageConsumer) { this.peer = peer; this.messagingVersion = messagingVersion; @@ -140,7 +142,7 @@ class MessageInHandler extends ByteToMessageDecoder int serializedAddrSize; if (readableBytes < 1 || readableBytes < (serializedAddrSize = in.getByte(in.readerIndex()) + 1)) return; - messageHeader.from = CompactEndpointSerializationHelper.deserialize(inputPlus); + messageHeader.from = CompactEndpointSerializationHelper.instance.deserialize(inputPlus, messagingVersion); state = State.READ_SECOND_CHUNK; readableBytes -= serializedAddrSize; // fall-through @@ -199,7 +201,7 @@ class MessageInHandler extends ByteToMessageDecoder /** * @return true if all the parameters have been read from the {@link ByteBuf}; else, false. */ - private boolean readParameters(ByteBuf in, ByteBufDataInputPlus inputPlus, int parameterCount, Map parameters) throws IOException + private boolean readParameters(ByteBuf in, ByteBufDataInputPlus inputPlus, int parameterCount, Map parameters) throws IOException { // makes the assumption that map.size() is a constant time function (HashMap.size() is) while (parameters.size() < parameterCount) @@ -208,9 +210,10 @@ class MessageInHandler extends ByteToMessageDecoder return false; String key = DataInputStream.readUTF(inputPlus); + ParameterType parameterType = ParameterType.byName.get(key); byte[] value = new byte[in.readInt()]; in.readBytes(value); - parameters.put(key, value); + parameters.put(parameterType, parameterType.serializer.deserialize(new DataInputBuffer(value), messagingVersion)); } return true; @@ -300,11 +303,11 @@ class MessageInHandler extends ByteToMessageDecoder { int messageId; long constructionTime; - InetAddress from; + InetAddressAndPort from; MessagingService.Verb verb; int payloadSize; - Map parameters = Collections.emptyMap(); + Map parameters = Collections.emptyMap(); /** * Total number of incoming parameters. diff --git a/src/java/org/apache/cassandra/net/async/MessageOutHandler.java b/src/java/org/apache/cassandra/net/async/MessageOutHandler.java index e88b56abc0..f1647ab038 100644 --- a/src/java/org/apache/cassandra/net/async/MessageOutHandler.java +++ b/src/java/org/apache/cassandra/net/async/MessageOutHandler.java @@ -40,6 +40,7 @@ import io.netty.handler.timeout.IdleStateEvent; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; import org.apache.cassandra.tracing.TraceState; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.NanoTimeToCurrentTimeMillis; @@ -196,10 +197,9 @@ class MessageOutHandler extends ChannelDuplexHandler { try { - byte[] sessionBytes = msg.message.parameters.get(Tracing.TRACE_HEADER); - if (sessionBytes != null) + UUID sessionId = (UUID)msg.message.getParameter(ParameterType.TRACE_SESSION); + if (sessionId != null) { - UUID sessionId = UUIDGen.getUUID(ByteBuffer.wrap(sessionBytes)); TraceState state = Tracing.instance.get(sessionId); String message = String.format("Sending %s message to %s, size = %d bytes", msg.message.verb, connectionId.connectionAddress(), @@ -207,9 +207,9 @@ class MessageOutHandler extends ChannelDuplexHandler // session may have already finished; see CASSANDRA-5668 if (state == null) { - byte[] traceTypeBytes = msg.message.parameters.get(Tracing.TRACE_TYPE); - Tracing.TraceType traceType = traceTypeBytes == null ? Tracing.TraceType.QUERY : Tracing.TraceType.deserialize(traceTypeBytes[0]); - Tracing.instance.trace(ByteBuffer.wrap(sessionBytes), message, traceType.getTTL()); + Tracing.TraceType traceType = (Tracing.TraceType)msg.message.getParameter(ParameterType.TRACE_TYPE); + traceType = traceType == null ? Tracing.TraceType.QUERY : traceType; + Tracing.instance.trace(ByteBuffer.wrap(UUIDGen.decompose(sessionId)), message, traceType.getTTL()); } else { diff --git a/src/java/org/apache/cassandra/net/async/NettyFactory.java b/src/java/org/apache/cassandra/net/async/NettyFactory.java index 7844c9b689..d89104348e 100644 --- a/src/java/org/apache/cassandra/net/async/NettyFactory.java +++ b/src/java/org/apache/cassandra/net/async/NettyFactory.java @@ -48,6 +48,7 @@ import org.apache.cassandra.auth.IInternodeAuthenticator; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.security.SSLFactory; import org.apache.cassandra.service.NativeTransportService; @@ -184,9 +185,9 @@ public final class NettyFactory * Create a {@link Channel} that listens on the {@code localAddr}. This method will block while trying to bind to the address, * but it does not make a remote call. */ - public Channel createInboundChannel(InetSocketAddress localAddr, InboundInitializer initializer, int receiveBufferSize) throws ConfigurationException + public Channel createInboundChannel(InetAddressAndPort localAddr, InboundInitializer initializer, int receiveBufferSize) throws ConfigurationException { - String nic = FBUtilities.getNetworkInterface(localAddr.getAddress()); + String nic = FBUtilities.getNetworkInterface(localAddr.address); logger.info("Starting Messaging Service on {} {}, encryption: {}", localAddr, nic == null ? "" : String.format(" (%s)", nic), encryptionLogStatement(initializer.encryptionOptions)); Class transport = useEpoll ? EpollServerSocketChannel.class : NioServerSocketChannel.class; @@ -202,7 +203,7 @@ public final class NettyFactory if (receiveBufferSize > 0) bootstrap.childOption(ChannelOption.SO_RCVBUF, receiveBufferSize); - ChannelFuture channelFuture = bootstrap.bind(localAddr); + ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress(localAddr.address, localAddr.port)); if (!channelFuture.awaitUninterruptibly().isSuccess()) { @@ -333,8 +334,9 @@ public final class NettyFactory .option(ChannelOption.TCP_NODELAY, params.tcpNoDelay) .option(ChannelOption.WRITE_BUFFER_WATER_MARK, params.waterMark) .handler(new OutboundInitializer(params)); - bootstrap.localAddress(params.connectionId.local(), 0); - bootstrap.remoteAddress(params.connectionId.connectionAddress()); + bootstrap.localAddress(params.connectionId.local().address, 0); + InetAddressAndPort remoteAddress = params.connectionId.connectionAddress(); + bootstrap.remoteAddress(new InetSocketAddress(remoteAddress.address, remoteAddress.port)); return bootstrap; } @@ -362,7 +364,8 @@ public final class NettyFactory { SslContext sslContext = SSLFactory.getSslContext(params.encryptionOptions, true, false); // for some reason channel.remoteAddress() will return null - InetSocketAddress peer = params.encryptionOptions.require_endpoint_verification ? params.connectionId.remoteAddress() : null; + InetAddressAndPort address = params.connectionId.remote(); + InetSocketAddress peer = params.encryptionOptions.require_endpoint_verification ? new InetSocketAddress(address.address, address.port) : null; SslHandler sslHandler = newSslHandler(channel, sslContext, peer); logger.trace("creating outbound netty SslContext: context={}, engine={}", sslContext.getClass().getName(), sslHandler.engine().getClass().getName()); pipeline.addFirst(SSL_CHANNEL_HANDLER_NAME, sslHandler); diff --git a/src/java/org/apache/cassandra/net/async/OutboundConnectionIdentifier.java b/src/java/org/apache/cassandra/net/async/OutboundConnectionIdentifier.java index 6b2ff0dea6..f3cb55461b 100644 --- a/src/java/org/apache/cassandra/net/async/OutboundConnectionIdentifier.java +++ b/src/java/org/apache/cassandra/net/async/OutboundConnectionIdentifier.java @@ -21,6 +21,8 @@ package org.apache.cassandra.net.async; import java.net.InetAddress; import java.net.InetSocketAddress; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * Identifies an outbound messaging connection. * @@ -38,24 +40,24 @@ public class OutboundConnectionIdentifier /** * Memoization of the local node's broadcast address. */ - private final InetSocketAddress localAddr; + private final InetAddressAndPort localAddr; /** * The address by which the remote is identified. This may be different from {@link #remoteConnectionAddr} for * something like EC2 public IP address which need to be used for communication between EC2 regions. */ - private final InetSocketAddress remoteAddr; + private final InetAddressAndPort remoteAddr; /** * The address to which we're connecting to the node (often the same as {@link #remoteAddr} but not always). */ - private final InetSocketAddress remoteConnectionAddr; + private final InetAddressAndPort remoteConnectionAddr; private final ConnectionType connectionType; - private OutboundConnectionIdentifier(InetSocketAddress localAddr, - InetSocketAddress remoteAddr, - InetSocketAddress remoteConnectionAddr, + private OutboundConnectionIdentifier(InetAddressAndPort localAddr, + InetAddressAndPort remoteAddr, + InetAddressAndPort remoteConnectionAddr, ConnectionType connectionType) { this.localAddr = localAddr; @@ -64,8 +66,8 @@ public class OutboundConnectionIdentifier this.connectionType = connectionType; } - private OutboundConnectionIdentifier(InetSocketAddress localAddr, - InetSocketAddress remoteAddr, + private OutboundConnectionIdentifier(InetAddressAndPort localAddr, + InetAddressAndPort remoteAddr, ConnectionType connectionType) { this(localAddr, remoteAddr, remoteAddr, connectionType); @@ -75,7 +77,7 @@ public class OutboundConnectionIdentifier * Creates an identifier for a small message connection and using the remote "identifying" address as its connection * address. */ - public static OutboundConnectionIdentifier small(InetSocketAddress localAddr, InetSocketAddress remoteAddr) + public static OutboundConnectionIdentifier small(InetAddressAndPort localAddr, InetAddressAndPort remoteAddr) { return new OutboundConnectionIdentifier(localAddr, remoteAddr, ConnectionType.SMALL_MESSAGE); } @@ -84,7 +86,7 @@ public class OutboundConnectionIdentifier * Creates an identifier for a large message connection and using the remote "identifying" address as its connection * address. */ - public static OutboundConnectionIdentifier large(InetSocketAddress localAddr, InetSocketAddress remoteAddr) + public static OutboundConnectionIdentifier large(InetAddressAndPort localAddr, InetAddressAndPort remoteAddr) { return new OutboundConnectionIdentifier(localAddr, remoteAddr, ConnectionType.LARGE_MESSAGE); } @@ -93,7 +95,7 @@ public class OutboundConnectionIdentifier * Creates an identifier for a gossip connection and using the remote "identifying" address as its connection * address. */ - public static OutboundConnectionIdentifier gossip(InetSocketAddress localAddr, InetSocketAddress remoteAddr) + public static OutboundConnectionIdentifier gossip(InetAddressAndPort localAddr, InetAddressAndPort remoteAddr) { return new OutboundConnectionIdentifier(localAddr, remoteAddr, ConnectionType.GOSSIP); } @@ -102,7 +104,7 @@ public class OutboundConnectionIdentifier * Creates an identifier for a gossip connection and using the remote "identifying" address as its connection * address. */ - public static OutboundConnectionIdentifier stream(InetSocketAddress localAddr, InetSocketAddress remoteAddr) + public static OutboundConnectionIdentifier stream(InetAddressAndPort localAddr, InetAddressAndPort remoteAddr) { return new OutboundConnectionIdentifier(localAddr, remoteAddr, ConnectionType.STREAM); } @@ -115,45 +117,37 @@ public class OutboundConnectionIdentifier * @return a newly created connection identifier that differs from this one only by using {@code remoteConnectionAddr} * as connection address to the remote. */ - public OutboundConnectionIdentifier withNewConnectionAddress(InetSocketAddress remoteConnectionAddr) + public OutboundConnectionIdentifier withNewConnectionAddress(InetAddressAndPort remoteConnectionAddr) { return new OutboundConnectionIdentifier(localAddr, remoteAddr, remoteConnectionAddr, connectionType); } public OutboundConnectionIdentifier withNewConnectionPort(int port) { - return new OutboundConnectionIdentifier(localAddr, new InetSocketAddress(remoteAddr.getAddress(), port), - new InetSocketAddress(remoteConnectionAddr.getAddress(), port), connectionType); + return new OutboundConnectionIdentifier(localAddr, InetAddressAndPort.getByAddressOverrideDefaults(remoteAddr.address, port), + InetAddressAndPort.getByAddressOverrideDefaults(remoteConnectionAddr.address, port), connectionType); } /** * The local node address. */ - public InetAddress local() + public InetAddressAndPort local() { - return localAddr.getAddress(); + return localAddr; } /** * The remote node identifying address (the one to use for anything else than connecting to the node). */ - public InetSocketAddress remoteAddress() + public InetAddressAndPort remote() { return remoteAddr; } - /** - * The remote node identifying address (the one to use for anything else than connecting to the node). - */ - public InetAddress remote() - { - return remoteAddr.getAddress(); - } - /** * The remote node connection address (the one to use to actually connect to the remote, and only that). */ - public InetSocketAddress connectionAddress() + public InetAddressAndPort connectionAddress() { return remoteConnectionAddr; } diff --git a/src/java/org/apache/cassandra/net/async/OutboundMessagingConnection.java b/src/java/org/apache/cassandra/net/async/OutboundMessagingConnection.java index 4522ba4d37..28775ef87c 100644 --- a/src/java/org/apache/cassandra/net/async/OutboundMessagingConnection.java +++ b/src/java/org/apache/cassandra/net/async/OutboundMessagingConnection.java @@ -43,6 +43,7 @@ import org.apache.cassandra.concurrent.ScheduledExecutors; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.NettyFactory.Mode; @@ -258,12 +259,12 @@ public class OutboundMessagingConnection logger.debug("connection attempt {} to {}", connectAttemptCount, connectionId); - InetSocketAddress remote = connectionId.remoteAddress(); - if (!authenticator.authenticate(remote.getAddress(), remote.getPort())) + InetAddressAndPort remote = connectionId.remote(); + if (!authenticator.authenticate(remote.address, remote.port)) { logger.warn("Internode auth failed connecting to {}", connectionId); //Remove the connection pool and other thread so messages aren't queued - MessagingService.instance().destroyConnectionPool(remote.getAddress()); + MessagingService.instance().destroyConnectionPool(remote); // don't update the state field as destroyConnectionPool() *should* call OMC.close() // on all the connections in the OMP for the remoteAddress @@ -284,7 +285,7 @@ public class OutboundMessagingConnection } @VisibleForTesting - static boolean shouldCompressConnection(InetAddress localHost, InetAddress remoteHost) + static boolean shouldCompressConnection(InetAddressAndPort localHost, InetAddressAndPort remoteHost) { return (DatabaseDescriptor.internodeCompression() == Config.InternodeCompression.all) || ((DatabaseDescriptor.internodeCompression() == Config.InternodeCompression.dc) && !isLocalDC(localHost, remoteHost)); @@ -355,7 +356,7 @@ public class OutboundMessagingConnection return null; } - static boolean isLocalDC(InetAddress localHost, InetAddress remoteHost) + static boolean isLocalDC(InetAddressAndPort localHost, InetAddressAndPort remoteHost) { String remoteDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(remoteHost); String localDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(localHost); @@ -585,7 +586,7 @@ public class OutboundMessagingConnection * Any outstanding messages in the existing channel will still be sent to the previous address (we won't/can't move them from * one channel to another). */ - void reconnectWithNewIp(InetSocketAddress newAddr) + void reconnectWithNewIp(InetAddressAndPort newAddr) { State currentState = state.get(); diff --git a/src/java/org/apache/cassandra/net/async/OutboundMessagingPool.java b/src/java/org/apache/cassandra/net/async/OutboundMessagingPool.java index 0086da854e..c7012297a7 100644 --- a/src/java/org/apache/cassandra/net/async/OutboundMessagingPool.java +++ b/src/java/org/apache/cassandra/net/async/OutboundMessagingPool.java @@ -28,6 +28,7 @@ import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.ConnectionMetrics; import org.apache.cassandra.net.BackPressureState; import org.apache.cassandra.net.MessageOut; @@ -56,14 +57,14 @@ public class OutboundMessagingPool * An override address on which to communicate with the peer. Typically used for something like EC2 public IP addresses * which need to be used for communication between EC2 regions. */ - private InetSocketAddress preferredRemoteAddr; + private InetAddressAndPort preferredRemoteAddr; - public OutboundMessagingPool(InetSocketAddress remoteAddr, InetSocketAddress localAddr, ServerEncryptionOptions encryptionOptions, + public OutboundMessagingPool(InetAddressAndPort remoteAddr, InetAddressAndPort localAddr, ServerEncryptionOptions encryptionOptions, BackPressureState backPressureState, IInternodeAuthenticator authenticator) { preferredRemoteAddr = remoteAddr; this.backPressureState = backPressureState; - metrics = new ConnectionMetrics(localAddr.getAddress(), this); + metrics = new ConnectionMetrics(localAddr, this); smallMessageChannel = new OutboundMessagingConnection(OutboundConnectionIdentifier.small(localAddr, preferredRemoteAddr), @@ -76,10 +77,10 @@ public class OutboundMessagingPool encryptionOptions, Optional.empty(), authenticator); } - private static Optional coalescingStrategy(InetSocketAddress remoteAddr) + private static Optional coalescingStrategy(InetAddressAndPort remoteAddr) { String strategyName = DatabaseDescriptor.getOtcCoalescingStrategy(); - String displayName = remoteAddr.getAddress().getHostAddress(); + String displayName = remoteAddr.toString(); return CoalescingStrategies.newCoalescingStrategy(strategyName, DatabaseDescriptor.getOtcCoalescingWindow(), OutboundMessagingConnection.logger, @@ -117,7 +118,7 @@ public class OutboundMessagingPool * * @param addr IP Address to use (and prefer) going forward for connecting to the peer */ - public void reconnectWithNewIp(InetSocketAddress addr) + public void reconnectWithNewIp(InetAddressAndPort addr) { preferredRemoteAddr = addr; gossipChannel.reconnectWithNewIp(addr); @@ -166,7 +167,7 @@ public class OutboundMessagingPool return metrics.timeouts.getCount(); } - public InetSocketAddress getPreferredRemoteAddr() + public InetAddressAndPort getPreferredRemoteAddr() { return preferredRemoteAddr; } diff --git a/src/java/org/apache/cassandra/repair/AsymmetricLocalSyncTask.java b/src/java/org/apache/cassandra/repair/AsymmetricLocalSyncTask.java index 5464520a6f..2c4fae4fa6 100644 --- a/src/java/org/apache/cassandra/repair/AsymmetricLocalSyncTask.java +++ b/src/java/org/apache/cassandra/repair/AsymmetricLocalSyncTask.java @@ -18,13 +18,13 @@ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.List; import java.util.UUID; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.streaming.ProgressInfo; import org.apache.cassandra.streaming.StreamEvent; @@ -41,15 +41,15 @@ public class AsymmetricLocalSyncTask extends AsymmetricSyncTask implements Strea private final UUID pendingRepair; private final TraceState state = Tracing.instance.get(); - public AsymmetricLocalSyncTask(RepairJobDesc desc, InetAddress fetchFrom, List> rangesToFetch, UUID pendingRepair, PreviewKind previewKind) + public AsymmetricLocalSyncTask(RepairJobDesc desc, InetAddressAndPort fetchFrom, List> rangesToFetch, UUID pendingRepair, PreviewKind previewKind) { - super(desc, FBUtilities.getBroadcastAddress(), fetchFrom, rangesToFetch, previewKind); + super(desc, FBUtilities.getBroadcastAddressAndPort(), fetchFrom, rangesToFetch, previewKind); this.pendingRepair = pendingRepair; } public void startSync(List> rangesToFetch) { - InetAddress preferred = SystemKeyspace.getPreferredIP(fetchFrom); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(fetchFrom); StreamPlan plan = new StreamPlan(StreamOperation.REPAIR, 1, false, false, diff --git a/src/java/org/apache/cassandra/repair/AsymmetricRemoteSyncTask.java b/src/java/org/apache/cassandra/repair/AsymmetricRemoteSyncTask.java index d70975df72..e24d8542d9 100644 --- a/src/java/org/apache/cassandra/repair/AsymmetricRemoteSyncTask.java +++ b/src/java/org/apache/cassandra/repair/AsymmetricRemoteSyncTask.java @@ -18,12 +18,12 @@ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.List; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.RepairException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.messages.AsymmetricSyncRequest; import org.apache.cassandra.streaming.PreviewKind; @@ -33,14 +33,14 @@ import org.apache.cassandra.utils.FBUtilities; public class AsymmetricRemoteSyncTask extends AsymmetricSyncTask implements CompletableRemoteSyncTask { - public AsymmetricRemoteSyncTask(RepairJobDesc desc, InetAddress fetchNode, InetAddress fetchFrom, List> rangesToFetch, PreviewKind previewKind) + public AsymmetricRemoteSyncTask(RepairJobDesc desc, InetAddressAndPort fetchNode, InetAddressAndPort fetchFrom, List> rangesToFetch, PreviewKind previewKind) { super(desc, fetchNode, fetchFrom, rangesToFetch, previewKind); } public void startSync(List> rangesToFetch) { - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); AsymmetricSyncRequest request = new AsymmetricSyncRequest(desc, local, fetchingNode, fetchFrom, rangesToFetch, previewKind); String message = String.format("Forwarding streaming repair of %d ranges to %s (to be streamed with %s)", request.ranges.size(), request.fetchingNode, request.fetchFrom); Tracing.traceRepair(message); diff --git a/src/java/org/apache/cassandra/repair/AsymmetricSyncTask.java b/src/java/org/apache/cassandra/repair/AsymmetricSyncTask.java index fe000588be..4d38e8a61f 100644 --- a/src/java/org/apache/cassandra/repair/AsymmetricSyncTask.java +++ b/src/java/org/apache/cassandra/repair/AsymmetricSyncTask.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.List; import java.util.concurrent.TimeUnit; @@ -30,6 +29,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.tracing.Tracing; @@ -37,15 +37,15 @@ public abstract class AsymmetricSyncTask extends AbstractFuture implem { private static Logger logger = LoggerFactory.getLogger(AsymmetricSyncTask.class); protected final RepairJobDesc desc; - protected final InetAddress fetchFrom; + protected final InetAddressAndPort fetchFrom; protected final List> rangesToFetch; - protected final InetAddress fetchingNode; + protected final InetAddressAndPort fetchingNode; protected final PreviewKind previewKind; private long startTime = Long.MIN_VALUE; protected volatile SyncStat stat; - public AsymmetricSyncTask(RepairJobDesc desc, InetAddress fetchingNode, InetAddress fetchFrom, List> rangesToFetch, PreviewKind previewKind) + public AsymmetricSyncTask(RepairJobDesc desc, InetAddressAndPort fetchingNode, InetAddressAndPort fetchFrom, List> rangesToFetch, PreviewKind previewKind) { this.desc = desc; this.fetchFrom = fetchFrom; diff --git a/src/java/org/apache/cassandra/repair/LocalSyncTask.java b/src/java/org/apache/cassandra/repair/LocalSyncTask.java index 8545b22b1c..60d571b8bb 100644 --- a/src/java/org/apache/cassandra/repair/LocalSyncTask.java +++ b/src/java/org/apache/cassandra/repair/LocalSyncTask.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.List; import java.util.UUID; @@ -28,7 +27,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; -import org.apache.cassandra.service.ActiveRepairService; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.streaming.ProgressInfo; import org.apache.cassandra.streaming.StreamEvent; @@ -39,8 +38,6 @@ import org.apache.cassandra.streaming.StreamOperation; import org.apache.cassandra.tracing.TraceState; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.MerkleTree; -import org.apache.cassandra.utils.MerkleTrees; /** * LocalSyncTask performs streaming between local(coordinator) node and remote replica. @@ -62,7 +59,7 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler } @VisibleForTesting - StreamPlan createStreamPlan(InetAddress dst, InetAddress preferred, List> differences) + StreamPlan createStreamPlan(InetAddressAndPort dst, InetAddressAndPort preferred, List> differences) { StreamPlan plan = new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair, previewKind) .listeners(this) @@ -84,10 +81,10 @@ public class LocalSyncTask extends SyncTask implements StreamEventHandler @Override protected void startSync(List> differences) { - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); // We can take anyone of the node as source or destination, however if one is localhost, we put at source to avoid a forwarding - InetAddress dst = r2.endpoint.equals(local) ? r1.endpoint : r2.endpoint; - InetAddress preferred = SystemKeyspace.getPreferredIP(dst); + InetAddressAndPort dst = r2.endpoint.equals(local) ? r1.endpoint : r2.endpoint; + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(dst); String message = String.format("Performing streaming repair of %d ranges with %s", differences.size(), dst); logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message); diff --git a/src/java/org/apache/cassandra/repair/NodePair.java b/src/java/org/apache/cassandra/repair/NodePair.java index a73c61a50e..bfb237e74c 100644 --- a/src/java/org/apache/cassandra/repair/NodePair.java +++ b/src/java/org/apache/cassandra/repair/NodePair.java @@ -18,13 +18,13 @@ package org.apache.cassandra.repair; import java.io.IOException; -import java.net.InetAddress; import com.google.common.base.Objects; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; /** @@ -36,10 +36,10 @@ public class NodePair { public static IVersionedSerializer serializer = new NodePairSerializer(); - public final InetAddress endpoint1; - public final InetAddress endpoint2; + public final InetAddressAndPort endpoint1; + public final InetAddressAndPort endpoint2; - public NodePair(InetAddress endpoint1, InetAddress endpoint2) + public NodePair(InetAddressAndPort endpoint1, InetAddressAndPort endpoint2) { this.endpoint1 = endpoint1; this.endpoint2 = endpoint2; @@ -55,6 +55,12 @@ public class NodePair return endpoint1.equals(nodePair.endpoint1) && endpoint2.equals(nodePair.endpoint2); } + @Override + public String toString() + { + return endpoint1.toString() + " - " + endpoint2.toString(); + } + @Override public int hashCode() { @@ -65,20 +71,21 @@ public class NodePair { public void serialize(NodePair nodePair, DataOutputPlus out, int version) throws IOException { - CompactEndpointSerializationHelper.serialize(nodePair.endpoint1, out); - CompactEndpointSerializationHelper.serialize(nodePair.endpoint2, out); + CompactEndpointSerializationHelper.instance.serialize(nodePair.endpoint1, out, version); + CompactEndpointSerializationHelper.instance.serialize(nodePair.endpoint2, out, version); } public NodePair deserialize(DataInputPlus in, int version) throws IOException { - InetAddress ep1 = CompactEndpointSerializationHelper.deserialize(in); - InetAddress ep2 = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort ep1 = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort ep2 = CompactEndpointSerializationHelper.instance.deserialize(in, version); return new NodePair(ep1, ep2); } public long serializedSize(NodePair nodePair, int version) { - return 2 * CompactEndpointSerializationHelper.serializedSize(nodePair.endpoint1); + return CompactEndpointSerializationHelper.instance.serializedSize(nodePair.endpoint1, version) + + CompactEndpointSerializationHelper.instance.serializedSize(nodePair.endpoint2, version); } } } diff --git a/src/java/org/apache/cassandra/repair/RemoteSyncTask.java b/src/java/org/apache/cassandra/repair/RemoteSyncTask.java index 93feb728cb..0a47f73c8b 100644 --- a/src/java/org/apache/cassandra/repair/RemoteSyncTask.java +++ b/src/java/org/apache/cassandra/repair/RemoteSyncTask.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.List; import org.slf4j.Logger; @@ -26,6 +25,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.RepairException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.messages.SyncRequest; import org.apache.cassandra.streaming.PreviewKind; @@ -51,7 +51,7 @@ public class RemoteSyncTask extends SyncTask implements CompletableRemoteSyncTas @Override protected void startSync(List> differences) { - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); SyncRequest request = new SyncRequest(desc, local, r1.endpoint, r2.endpoint, differences, previewKind); String message = String.format("Forwarding streaming repair of %d ranges to %s (to be streamed with %s)", request.ranges.size(), request.src, request.dst); logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message); diff --git a/src/java/org/apache/cassandra/repair/RepairJob.java b/src/java/org/apache/cassandra/repair/RepairJob.java index 7b8eb922c2..48973d21cf 100644 --- a/src/java/org/apache/cassandra/repair/RepairJob.java +++ b/src/java/org/apache/cassandra/repair/RepairJob.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.*; import java.util.stream.Collectors; @@ -33,6 +32,7 @@ import org.apache.cassandra.repair.asymmetric.DifferenceHolder; import org.apache.cassandra.repair.asymmetric.HostDifferences; import org.apache.cassandra.repair.asymmetric.PreferedNodeFilter; import org.apache.cassandra.repair.asymmetric.ReduceHelper; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; @@ -83,14 +83,14 @@ public class RepairJob extends AbstractFuture implements Runnable Keyspace ks = Keyspace.open(desc.keyspace); ColumnFamilyStore cfs = ks.getColumnFamilyStore(desc.columnFamily); cfs.metric.repairsStarted.inc(); - List allEndpoints = new ArrayList<>(session.endpoints); - allEndpoints.add(FBUtilities.getBroadcastAddress()); + List allEndpoints = new ArrayList<>(session.endpoints); + allEndpoints.add(FBUtilities.getBroadcastAddressAndPort()); ListenableFuture> validations; // Create a snapshot at all nodes unless we're using pure parallel repairs if (parallelismDegree != RepairParallelism.PARALLEL) { - ListenableFuture> allSnapshotTasks; + ListenableFuture> allSnapshotTasks; if (isIncremental) { // consistent repair does it's own "snapshotting" @@ -99,8 +99,8 @@ public class RepairJob extends AbstractFuture implements Runnable else { // Request snapshot to all replica - List> snapshotTasks = new ArrayList<>(allEndpoints.size()); - for (InetAddress endpoint : allEndpoints) + List> snapshotTasks = new ArrayList<>(allEndpoints.size()); + for (InetAddressAndPort endpoint : allEndpoints) { SnapshotTask snapshotTask = new SnapshotTask(desc, endpoint); snapshotTasks.add(snapshotTask); @@ -110,9 +110,9 @@ public class RepairJob extends AbstractFuture implements Runnable } // When all snapshot complete, send validation requests - validations = Futures.transformAsync(allSnapshotTasks, new AsyncFunction, List>() + validations = Futures.transformAsync(allSnapshotTasks, new AsyncFunction, List>() { - public ListenableFuture> apply(List endpoints) + public ListenableFuture> apply(List endpoints) { if (parallelismDegree == RepairParallelism.SEQUENTIAL) return sendSequentialValidationRequest(endpoints); @@ -164,7 +164,7 @@ public class RepairJob extends AbstractFuture implements Runnable { return trees -> { - InetAddress local = FBUtilities.getLocalAddress(); + InetAddressAndPort local = FBUtilities.getLocalAddressAndPort(); List syncTasks = new ArrayList<>(); // We need to difference all trees one against another @@ -198,7 +198,7 @@ public class RepairJob extends AbstractFuture implements Runnable { return trees -> { - InetAddress local = FBUtilities.getLocalAddress(); + InetAddressAndPort local = FBUtilities.getLocalAddressAndPort(); List syncTasks = new ArrayList<>(); // We need to difference all trees one against another @@ -210,16 +210,16 @@ public class RepairJob extends AbstractFuture implements Runnable .filter(node -> getDC(streaming) .equals(getDC(node))) .collect(Collectors.toSet()); - ImmutableMap reducedDifferences = ReduceHelper.reduce(diffHolder, preferSameDCFilter); + ImmutableMap reducedDifferences = ReduceHelper.reduce(diffHolder, preferSameDCFilter); for (int i = 0; i < trees.size(); i++) { - InetAddress address = trees.get(i).endpoint; + InetAddressAndPort address = trees.get(i).endpoint; HostDifferences streamsFor = reducedDifferences.get(address); if (streamsFor != null) { assert streamsFor.get(address).isEmpty() : "We should not fetch ranges from ourselves"; - for (InetAddress fetchFrom : streamsFor.hosts()) + for (InetAddressAndPort fetchFrom : streamsFor.hosts()) { List> toFetch = streamsFor.get(fetchFrom); logger.debug("{} is about to fetch {} from {}", address, toFetch, fetchFrom); @@ -246,7 +246,7 @@ public class RepairJob extends AbstractFuture implements Runnable }; } - private String getDC(InetAddress address) + private String getDC(InetAddressAndPort address) { return DatabaseDescriptor.getEndpointSnitch().getDatacenter(address); } @@ -257,14 +257,14 @@ public class RepairJob extends AbstractFuture implements Runnable * @param endpoints Endpoint addresses to send validation request * @return Future that can get all {@link TreeResponse} from replica, if all validation succeed. */ - private ListenableFuture> sendValidationRequest(Collection endpoints) + private ListenableFuture> sendValidationRequest(Collection endpoints) { String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints); logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message); Tracing.traceRepair(message); int nowInSec = FBUtilities.nowInSeconds(); List> tasks = new ArrayList<>(endpoints.size()); - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { ValidationTask task = new ValidationTask(desc, endpoint, nowInSec, previewKind); tasks.add(task); @@ -277,7 +277,7 @@ public class RepairJob extends AbstractFuture implements Runnable /** * Creates {@link ValidationTask} and submit them to task executor so that tasks run sequentially. */ - private ListenableFuture> sendSequentialValidationRequest(Collection endpoints) + private ListenableFuture> sendSequentialValidationRequest(Collection endpoints) { String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints); logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message); @@ -285,8 +285,8 @@ public class RepairJob extends AbstractFuture implements Runnable int nowInSec = FBUtilities.nowInSeconds(); List> tasks = new ArrayList<>(endpoints.size()); - Queue requests = new LinkedList<>(endpoints); - InetAddress address = requests.poll(); + Queue requests = new LinkedList<>(endpoints); + InetAddressAndPort address = requests.poll(); ValidationTask firstTask = new ValidationTask(desc, address, nowInSec, previewKind); logger.info("Validating {}", address); session.waitForValidation(Pair.create(desc, address), firstTask); @@ -294,7 +294,7 @@ public class RepairJob extends AbstractFuture implements Runnable ValidationTask currentTask = firstTask; while (requests.size() > 0) { - final InetAddress nextAddress = requests.poll(); + final InetAddressAndPort nextAddress = requests.poll(); final ValidationTask nextTask = new ValidationTask(desc, nextAddress, nowInSec, previewKind); tasks.add(nextTask); Futures.addCallback(currentTask, new FutureCallback() @@ -319,7 +319,7 @@ public class RepairJob extends AbstractFuture implements Runnable /** * Creates {@link ValidationTask} and submit them to task executor so that tasks run sequentially within each dc. */ - private ListenableFuture> sendDCAwareValidationRequest(Collection endpoints) + private ListenableFuture> sendDCAwareValidationRequest(Collection endpoints) { String message = String.format("Requesting merkle trees for %s (to %s)", desc.columnFamily, endpoints); logger.info("{} {}", previewKind.logPrefix(desc.sessionId), message); @@ -327,11 +327,11 @@ public class RepairJob extends AbstractFuture implements Runnable int nowInSec = FBUtilities.nowInSeconds(); List> tasks = new ArrayList<>(endpoints.size()); - Map> requestsByDatacenter = new HashMap<>(); - for (InetAddress endpoint : endpoints) + Map> requestsByDatacenter = new HashMap<>(); + for (InetAddressAndPort endpoint : endpoints) { String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint); - Queue queue = requestsByDatacenter.get(dc); + Queue queue = requestsByDatacenter.get(dc); if (queue == null) { queue = new LinkedList<>(); @@ -340,10 +340,10 @@ public class RepairJob extends AbstractFuture implements Runnable queue.add(endpoint); } - for (Map.Entry> entry : requestsByDatacenter.entrySet()) + for (Map.Entry> entry : requestsByDatacenter.entrySet()) { - Queue requests = entry.getValue(); - InetAddress address = requests.poll(); + Queue requests = entry.getValue(); + InetAddressAndPort address = requests.poll(); ValidationTask firstTask = new ValidationTask(desc, address, nowInSec, previewKind); logger.info("Validating {}", address); session.waitForValidation(Pair.create(desc, address), firstTask); @@ -351,7 +351,7 @@ public class RepairJob extends AbstractFuture implements Runnable ValidationTask currentTask = firstTask; while (requests.size() > 0) { - final InetAddress nextAddress = requests.poll(); + final InetAddressAndPort nextAddress = requests.poll(); final ValidationTask nextTask = new ValidationTask(desc, nextAddress, nowInSec, previewKind); tasks.add(nextTask); Futures.addCallback(currentTask, new FutureCallback() diff --git a/src/java/org/apache/cassandra/repair/RepairMessageVerbHandler.java b/src/java/org/apache/cassandra/repair/RepairMessageVerbHandler.java index c26d4d151d..4c0a564d29 100644 --- a/src/java/org/apache/cassandra/repair/RepairMessageVerbHandler.java +++ b/src/java/org/apache/cassandra/repair/RepairMessageVerbHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.*; import com.google.common.base.Predicate; @@ -28,10 +27,12 @@ import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.dht.Bounds; import org.apache.cassandra.io.sstable.format.SSTableReader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IVerbHandler; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; import org.apache.cassandra.repair.messages.*; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.service.ActiveRepairService; @@ -225,11 +226,11 @@ public class RepairMessageVerbHandler implements IVerbHandler } } - private void logErrorAndSendFailureResponse(String errorMessage, InetAddress to, int id) + private void logErrorAndSendFailureResponse(String errorMessage, InetAddressAndPort to, int id) { logger.error(errorMessage); MessageOut reply = new MessageOut(MessagingService.Verb.INTERNAL_RESPONSE) - .withParameter(MessagingService.FAILURE_RESPONSE_PARAM, MessagingService.ONE_BYTE); + .withParameter(ParameterType.FAILURE_RESPONSE, MessagingService.ONE_BYTE); MessagingService.instance().sendReply(reply, id, to); } } diff --git a/src/java/org/apache/cassandra/repair/RepairRunnable.java b/src/java/org/apache/cassandra/repair/RepairRunnable.java index 1c9778b3dc..5121874092 100644 --- a/src/java/org/apache/cassandra/repair/RepairRunnable.java +++ b/src/java/org/apache/cassandra/repair/RepairRunnable.java @@ -60,6 +60,7 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.repair.consistent.CoordinatorSession; import org.apache.cassandra.metrics.StorageMetrics; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.service.ActiveRepairService; import org.apache.cassandra.service.QueryState; @@ -141,10 +142,10 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti @VisibleForTesting static class CommonRange { - public final Set endpoints; + public final Set endpoints; public final Collection> ranges; - public CommonRange(Set endpoints, Collection> ranges) + public CommonRange(Set endpoints, Collection> ranges) { Preconditions.checkArgument(endpoints != null && !endpoints.isEmpty()); Preconditions.checkArgument(ranges != null && !ranges.isEmpty()); @@ -232,7 +233,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti traceState = null; } - final Set allNeighbors = new HashSet<>(); + final Set allNeighbors = new HashSet<>(); List commonRanges = new ArrayList<>(); //pre-calculate output of getLocalRanges and pass it to getNeighbors to increase performance and prevent @@ -243,9 +244,9 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti { for (Range range : options.getRanges()) { - Set neighbors = ActiveRepairService.getNeighbors(keyspace, keyspaceLocalRanges, range, - options.getDataCenters(), - options.getHosts()); + Set neighbors = ActiveRepairService.getNeighbors(keyspace, keyspaceLocalRanges, range, + options.getDataCenters(), + options.getHosts()); addRangeToNeighbors(commonRanges, range, neighbors); allNeighbors.addAll(neighbors); @@ -286,7 +287,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti try (Timer.Context ctx = Keyspace.open(keyspace).metric.repairPrepareTime.time()) { - ActiveRepairService.instance.prepareForRepair(parentSession, FBUtilities.getBroadcastAddress(), allNeighbors, options, columnFamilyStores); + ActiveRepairService.instance.prepareForRepair(parentSession, FBUtilities.getBroadcastAddressAndPort(), allNeighbors, options, columnFamilyStores); progress.incrementAndGet(); } catch (Throwable t) @@ -362,7 +363,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti * removes dead nodes from common ranges, and exludes ranges left without any participants */ @VisibleForTesting - static List filterCommonRanges(List commonRanges, Set liveEndpoints, boolean force) + static List filterCommonRanges(List commonRanges, Set liveEndpoints, boolean force) { if (!force) { @@ -374,7 +375,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti for (CommonRange commonRange: commonRanges) { - Set endpoints = ImmutableSet.copyOf(Iterables.filter(commonRange.endpoints, liveEndpoints::contains)); + Set endpoints = ImmutableSet.copyOf(Iterables.filter(commonRange.endpoints, liveEndpoints::contains)); // this node is implicitly a participant in this repair, so a single endpoint is ok here if (!endpoints.isEmpty()) @@ -391,15 +392,15 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti long startTime, boolean forceRepair, TraceState traceState, - Set allNeighbors, + Set allNeighbors, List commonRanges, String... cfnames) { // the local node also needs to be included in the set of participants, since coordinator sessions aren't persisted - Predicate isAlive = FailureDetector.instance::isAlive; - Set allParticipants = ImmutableSet.builder() + Predicate isAlive = FailureDetector.instance::isAlive; + Set allParticipants = ImmutableSet.builder() .addAll(forceRepair ? Iterables.filter(allNeighbors, isAlive) : allNeighbors) - .add(FBUtilities.getBroadcastAddress()) + .add(FBUtilities.getBroadcastAddressAndPort()) .build(); List allRanges = filterCommonRanges(commonRanges, allParticipants, forceRepair); @@ -673,7 +674,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti ImmutableList.of(failureMessage, completionMessage)); } - private void addRangeToNeighbors(List neighborRangeList, Range range, Set neighbors) + private void addRangeToNeighbors(List neighborRangeList, Range range, Set neighbors) { for (int i = 0; i < neighborRangeList.size(); i++) { @@ -708,7 +709,7 @@ public class RepairRunnable extends WrappedRunnable implements ProgressEventNoti SelectStatement statement = (SelectStatement) QueryProcessor.parseStatement(query).prepare().statement; ByteBuffer sessionIdBytes = ByteBufferUtil.bytes(sessionId); - InetAddress source = FBUtilities.getBroadcastAddress(); + InetAddressAndPort source = FBUtilities.getBroadcastAddressAndPort(); HashSet[] seen = new HashSet[] { new HashSet<>(), new HashSet<>() }; int si = 0; diff --git a/src/java/org/apache/cassandra/repair/RepairSession.java b/src/java/org/apache/cassandra/repair/RepairSession.java index 609ec56af2..91d767d03d 100644 --- a/src/java/org/apache/cassandra/repair/RepairSession.java +++ b/src/java/org/apache/cassandra/repair/RepairSession.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -34,6 +33,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.gms.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.streaming.SessionSummary; import org.apache.cassandra.tracing.Tracing; @@ -95,14 +95,14 @@ public class RepairSession extends AbstractFuture implement /** Range to repair */ public final Collection> ranges; - public final Set endpoints; + public final Set endpoints; public final boolean isIncremental; public final PreviewKind previewKind; private final AtomicBoolean isFailed = new AtomicBoolean(false); // Each validation task waits response from replica in validating ConcurrentMap (keyed by CF name and endpoint address) - private final ConcurrentMap, ValidationTask> validating = new ConcurrentHashMap<>(); + private final ConcurrentMap, ValidationTask> validating = new ConcurrentHashMap<>(); // Remote syncing jobs wait response in syncingTasks map private final ConcurrentMap, CompletableRemoteSyncTask> syncingTasks = new ConcurrentHashMap<>(); @@ -130,7 +130,7 @@ public class RepairSession extends AbstractFuture implement Collection> ranges, String keyspace, RepairParallelism parallelismDegree, - Set endpoints, + Set endpoints, boolean isIncremental, boolean pullRepair, boolean force, @@ -152,8 +152,8 @@ public class RepairSession extends AbstractFuture implement if (force) { logger.debug("force flag set, removing dead endpoints"); - final Set removeCandidates = new HashSet<>(); - for (final InetAddress endpoint : endpoints) + final Set removeCandidates = new HashSet<>(); + for (final InetAddressAndPort endpoint : endpoints) { if (!FailureDetector.instance.isAlive(endpoint)) { @@ -189,7 +189,7 @@ public class RepairSession extends AbstractFuture implement return ranges; } - public void waitForValidation(Pair key, ValidationTask task) + public void waitForValidation(Pair key, ValidationTask task) { validating.put(key, task); } @@ -207,7 +207,7 @@ public class RepairSession extends AbstractFuture implement * @param endpoint endpoint that sent merkle tree * @param trees calculated merkle trees, or null if validation failed */ - public void validationComplete(RepairJobDesc desc, InetAddress endpoint, MerkleTrees trees) + public void validationComplete(RepairJobDesc desc, InetAddressAndPort endpoint, MerkleTrees trees) { ValidationTask task = validating.remove(Pair.create(desc, endpoint)); if (task == null) @@ -245,8 +245,8 @@ public class RepairSession extends AbstractFuture implement private String repairedNodes() { StringBuilder sb = new StringBuilder(); - sb.append(FBUtilities.getBroadcastAddress()); - for (InetAddress ep : endpoints) + sb.append(FBUtilities.getBroadcastAddressAndPort()); + for (InetAddressAndPort ep : endpoints) sb.append(", ").append(ep); return sb.toString(); } @@ -285,7 +285,7 @@ public class RepairSession extends AbstractFuture implement } // Checking all nodes are live - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { if (!FailureDetector.instance.isAlive(endpoint) && !skippedReplicas) { @@ -353,23 +353,23 @@ public class RepairSession extends AbstractFuture implement terminate(); } - public void onJoin(InetAddress endpoint, EndpointState epState) {} - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) {} - public void onAlive(InetAddress endpoint, EndpointState state) {} - public void onDead(InetAddress endpoint, EndpointState state) {} + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) {} + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) {} + public void onAlive(InetAddressAndPort endpoint, EndpointState state) {} + public void onDead(InetAddressAndPort endpoint, EndpointState state) {} - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { convict(endpoint, Double.MAX_VALUE); } - public void onRestart(InetAddress endpoint, EndpointState epState) + public void onRestart(InetAddressAndPort endpoint, EndpointState epState) { convict(endpoint, Double.MAX_VALUE); } - public void convict(InetAddress endpoint, double phi) + public void convict(InetAddressAndPort endpoint, double phi) { if (!endpoints.contains(endpoint)) return; diff --git a/src/java/org/apache/cassandra/repair/SnapshotTask.java b/src/java/org/apache/cassandra/repair/SnapshotTask.java index 2b267a7e79..acc5186a5f 100644 --- a/src/java/org/apache/cassandra/repair/SnapshotTask.java +++ b/src/java/org/apache/cassandra/repair/SnapshotTask.java @@ -17,13 +17,13 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.AbstractFuture; import org.apache.cassandra.exceptions.RequestFailureReason; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IAsyncCallbackWithFailure; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessagingService; @@ -32,12 +32,12 @@ import org.apache.cassandra.repair.messages.SnapshotMessage; /** * SnapshotTask is a task that sends snapshot request. */ -public class SnapshotTask extends AbstractFuture implements RunnableFuture +public class SnapshotTask extends AbstractFuture implements RunnableFuture { private final RepairJobDesc desc; - private final InetAddress endpoint; + private final InetAddressAndPort endpoint; - public SnapshotTask(RepairJobDesc desc, InetAddress endpoint) + public SnapshotTask(RepairJobDesc desc, InetAddressAndPort endpoint) { this.desc = desc; this.endpoint = endpoint; @@ -74,7 +74,7 @@ public class SnapshotTask extends AbstractFuture implements Runnabl public boolean isLatencyForSnitch() { return false; } - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { //listener.failedSnapshot(); task.setException(new RuntimeException("Could not create snapshot at " + from)); diff --git a/src/java/org/apache/cassandra/repair/StreamingRepairTask.java b/src/java/org/apache/cassandra/repair/StreamingRepairTask.java index a1b74594f8..59fee0b9d5 100644 --- a/src/java/org/apache/cassandra/repair/StreamingRepairTask.java +++ b/src/java/org/apache/cassandra/repair/StreamingRepairTask.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.UUID; import java.util.Collections; import java.util.Collection; @@ -29,6 +28,8 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.messages.SyncComplete; import org.apache.cassandra.streaming.PreviewKind; @@ -48,14 +49,14 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler private final RepairJobDesc desc; private final boolean asymmetric; - private final InetAddress initiator; - private final InetAddress src; - private final InetAddress dst; + private final InetAddressAndPort initiator; + private final InetAddressAndPort src; + private final InetAddressAndPort dst; private final Collection> ranges; private final UUID pendingRepair; private final PreviewKind previewKind; - public StreamingRepairTask(RepairJobDesc desc, InetAddress initiator, InetAddress src, InetAddress dst, Collection> ranges, UUID pendingRepair, PreviewKind previewKind, boolean asymmetric) + public StreamingRepairTask(RepairJobDesc desc, InetAddressAndPort initiator, InetAddressAndPort src, InetAddressAndPort dst, Collection> ranges, UUID pendingRepair, PreviewKind previewKind, boolean asymmetric) { this.desc = desc; this.initiator = initiator; @@ -69,14 +70,14 @@ public class StreamingRepairTask implements Runnable, StreamEventHandler public void run() { - InetAddress dest = dst; - InetAddress preferred = SystemKeyspace.getPreferredIP(dest); + InetAddressAndPort dest = dst; + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(dest); logger.info("[streaming task #{}] Performing streaming repair of {} ranges with {}", desc.sessionId, ranges.size(), dst); createStreamPlan(dest, preferred).execute(); } @VisibleForTesting - StreamPlan createStreamPlan(InetAddress dest, InetAddress preferred) + StreamPlan createStreamPlan(InetAddressAndPort dest, InetAddressAndPort preferred) { StreamPlan sp = new StreamPlan(StreamOperation.REPAIR, 1, false, false, pendingRepair, previewKind) .listeners(this) diff --git a/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java b/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java index 3770621295..b46ae5ea3f 100644 --- a/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java +++ b/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java @@ -19,7 +19,6 @@ package org.apache.cassandra.repair; import java.io.PrintWriter; import java.io.StringWriter; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; @@ -45,6 +44,7 @@ import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.schema.KeyspaceMetadata; import org.apache.cassandra.schema.KeyspaceParams; @@ -74,48 +74,50 @@ public final class SystemDistributedKeyspace private static final TableMetadata RepairHistory = parse(REPAIR_HISTORY, - "Repair history", - "CREATE TABLE %s (" - + "keyspace_name text," - + "columnfamily_name text," - + "id timeuuid," - + "parent_id timeuuid," - + "range_begin text," - + "range_end text," - + "coordinator inet," - + "participants set," - + "exception_message text," - + "exception_stacktrace text," - + "status text," - + "started_at timestamp," - + "finished_at timestamp," - + "PRIMARY KEY ((keyspace_name, columnfamily_name), id))"); + "Repair history", + "CREATE TABLE %s (" + + "keyspace_name text," + + "columnfamily_name text," + + "id timeuuid," + + "parent_id timeuuid," + + "range_begin text," + + "range_end text," + + "coordinator inet," + + "coordinator_port int," + + "participants set," + + "participants_v2 set," + + "exception_message text," + + "exception_stacktrace text," + + "status text," + + "started_at timestamp," + + "finished_at timestamp," + + "PRIMARY KEY ((keyspace_name, columnfamily_name), id))"); private static final TableMetadata ParentRepairHistory = parse(PARENT_REPAIR_HISTORY, - "Repair history", - "CREATE TABLE %s (" - + "parent_id timeuuid," - + "keyspace_name text," - + "columnfamily_names set," - + "started_at timestamp," - + "finished_at timestamp," - + "exception_message text," - + "exception_stacktrace text," - + "requested_ranges set," - + "successful_ranges set," - + "options map," - + "PRIMARY KEY (parent_id))"); + "Repair history", + "CREATE TABLE %s (" + + "parent_id timeuuid," + + "keyspace_name text," + + "columnfamily_names set," + + "started_at timestamp," + + "finished_at timestamp," + + "exception_message text," + + "exception_stacktrace text," + + "requested_ranges set," + + "successful_ranges set," + + "options map," + + "PRIMARY KEY (parent_id))"); private static final TableMetadata ViewBuildStatus = parse(VIEW_BUILD_STATUS, - "Materialized View build status", - "CREATE TABLE %s (" - + "keyspace_name text," - + "view_name text," - + "host_id uuid," - + "status text," - + "PRIMARY KEY ((keyspace_name, view_name), host_id))"); + "Materialized View build status", + "CREATE TABLE %s (" + + "keyspace_name text," + + "view_name text," + + "host_id uuid," + + "status text," + + "PRIMARY KEY ((keyspace_name, view_name), host_id))"); private static TableMetadata parse(String table, String description, String cql) { @@ -184,17 +186,21 @@ public final class SystemDistributedKeyspace processSilent(fmtQuery); } - public static void startRepairs(UUID id, UUID parent_id, String keyspaceName, String[] cfnames, Collection> ranges, Iterable endpoints) + public static void startRepairs(UUID id, UUID parent_id, String keyspaceName, String[] cfnames, Collection> ranges, Iterable endpoints) { - String coordinator = FBUtilities.getBroadcastAddress().getHostAddress(); - Set participants = Sets.newHashSet(coordinator); + InetAddressAndPort coordinator = FBUtilities.getBroadcastAddressAndPort(); + Set participants = Sets.newHashSet(); + Set participants_v2 = Sets.newHashSet(); - for (InetAddress endpoint : endpoints) - participants.add(endpoint.getHostAddress()); + for (InetAddressAndPort endpoint : endpoints) + { + participants.add(endpoint.getHostAddress(false)); + participants_v2.add(endpoint.toString()); + } String query = - "INSERT INTO %s.%s (keyspace_name, columnfamily_name, id, parent_id, range_begin, range_end, coordinator, participants, status, started_at) " + - "VALUES ( '%s', '%s', %s, %s, '%s', '%s', '%s', { '%s' }, '%s', toTimestamp(now()))"; + "INSERT INTO %s.%s (keyspace_name, columnfamily_name, id, parent_id, range_begin, range_end, coordinator, coordinator_port, participants, participants_v2, status, started_at) " + + "VALUES ( '%s', '%s', %s, %s, '%s', '%s', '%s', %d, { '%s' }, { '%s' }, '%s', toTimestamp(now()))"; for (String cfname : cfnames) { @@ -207,8 +213,10 @@ public final class SystemDistributedKeyspace parent_id.toString(), range.left.toString(), range.right.toString(), - coordinator, + coordinator.getHostAddress(false), + coordinator.port, Joiner.on("', '").join(participants), + Joiner.on("', '").join(participants_v2), RepairState.STARTED.toString()); processSilent(fmtQry); } diff --git a/src/java/org/apache/cassandra/repair/TreeResponse.java b/src/java/org/apache/cassandra/repair/TreeResponse.java index c898b36968..8571caaa5c 100644 --- a/src/java/org/apache/cassandra/repair/TreeResponse.java +++ b/src/java/org/apache/cassandra/repair/TreeResponse.java @@ -17,8 +17,7 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; - +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.MerkleTrees; /** @@ -26,10 +25,10 @@ import org.apache.cassandra.utils.MerkleTrees; */ public class TreeResponse { - public final InetAddress endpoint; + public final InetAddressAndPort endpoint; public final MerkleTrees trees; - public TreeResponse(InetAddress endpoint, MerkleTrees trees) + public TreeResponse(InetAddressAndPort endpoint, MerkleTrees trees) { this.endpoint = endpoint; this.trees = trees; diff --git a/src/java/org/apache/cassandra/repair/ValidationTask.java b/src/java/org/apache/cassandra/repair/ValidationTask.java index 175709fc7b..fc500cfe4b 100644 --- a/src/java/org/apache/cassandra/repair/ValidationTask.java +++ b/src/java/org/apache/cassandra/repair/ValidationTask.java @@ -17,11 +17,10 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; - import com.google.common.util.concurrent.AbstractFuture; import org.apache.cassandra.exceptions.RepairException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.messages.ValidationRequest; import org.apache.cassandra.streaming.PreviewKind; @@ -34,11 +33,11 @@ import org.apache.cassandra.utils.MerkleTrees; public class ValidationTask extends AbstractFuture implements Runnable { private final RepairJobDesc desc; - private final InetAddress endpoint; + private final InetAddressAndPort endpoint; private final int nowInSec; private final PreviewKind previewKind; - public ValidationTask(RepairJobDesc desc, InetAddress endpoint, int nowInSec, PreviewKind previewKind) + public ValidationTask(RepairJobDesc desc, InetAddressAndPort endpoint, int nowInSec, PreviewKind previewKind) { this.desc = desc; this.endpoint = endpoint; diff --git a/src/java/org/apache/cassandra/repair/Validator.java b/src/java/org/apache/cassandra/repair/Validator.java index 9803638e95..4c2856d840 100644 --- a/src/java/org/apache/cassandra/repair/Validator.java +++ b/src/java/org/apache/cassandra/repair/Validator.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; @@ -41,6 +40,7 @@ import org.apache.cassandra.db.rows.UnfilteredRowIterator; import org.apache.cassandra.db.rows.UnfilteredRowIterators; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.messages.ValidationComplete; import org.apache.cassandra.streaming.PreviewKind; @@ -64,7 +64,7 @@ public class Validator implements Runnable private static final Logger logger = LoggerFactory.getLogger(Validator.class); public final RepairJobDesc desc; - public final InetAddress initiator; + public final InetAddressAndPort initiator; public final int nowInSec; private final boolean evenTreeDistribution; public final boolean isIncremental; @@ -81,17 +81,17 @@ public class Validator implements Runnable private final PreviewKind previewKind; - public Validator(RepairJobDesc desc, InetAddress initiator, int nowInSec, PreviewKind previewKind) + public Validator(RepairJobDesc desc, InetAddressAndPort initiator, int nowInSec, PreviewKind previewKind) { this(desc, initiator, nowInSec, false, false, previewKind); } - public Validator(RepairJobDesc desc, InetAddress initiator, int nowInSec, boolean isIncremental, PreviewKind previewKind) + public Validator(RepairJobDesc desc, InetAddressAndPort initiator, int nowInSec, boolean isIncremental, PreviewKind previewKind) { this(desc, initiator, nowInSec, false, isIncremental, previewKind); } - public Validator(RepairJobDesc desc, InetAddress initiator, int nowInSec, boolean evenTreeDistribution, boolean isIncremental, PreviewKind previewKind) + public Validator(RepairJobDesc desc, InetAddressAndPort initiator, int nowInSec, boolean evenTreeDistribution, boolean isIncremental, PreviewKind previewKind) { this.desc = desc; this.initiator = initiator; @@ -352,7 +352,7 @@ public class Validator implements Runnable public void run() { // respond to the request that triggered this validation - if (!initiator.equals(FBUtilities.getBroadcastAddress())) + if (!initiator.equals(FBUtilities.getBroadcastAddressAndPort())) { logger.info("{} Sending completed merkle tree to {} for {}.{}", previewKind.logPrefix(desc.sessionId), initiator, desc.keyspace, desc.columnFamily); Tracing.traceRepair("Sending completed merkle tree to {} for {}.{}", initiator, desc.keyspace, desc.columnFamily); diff --git a/src/java/org/apache/cassandra/repair/asymmetric/DifferenceHolder.java b/src/java/org/apache/cassandra/repair/asymmetric/DifferenceHolder.java index eb99977a07..c9b7ed7ccf 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/DifferenceHolder.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/DifferenceHolder.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.List; import java.util.Map; import java.util.Set; @@ -28,6 +27,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.TreeResponse; import org.apache.cassandra.utils.MerkleTrees; @@ -36,11 +36,11 @@ import org.apache.cassandra.utils.MerkleTrees; */ public class DifferenceHolder { - private final ImmutableMap differences; + private final ImmutableMap differences; public DifferenceHolder(List trees) { - ImmutableMap.Builder diffBuilder = ImmutableMap.builder(); + ImmutableMap.Builder diffBuilder = ImmutableMap.builder(); for (int i = 0; i < trees.size() - 1; ++i) { TreeResponse r1 = trees.get(i); @@ -58,9 +58,9 @@ public class DifferenceHolder } @VisibleForTesting - DifferenceHolder(Map differences) + DifferenceHolder(Map differences) { - ImmutableMap.Builder diffBuilder = ImmutableMap.builder(); + ImmutableMap.Builder diffBuilder = ImmutableMap.builder(); diffBuilder.putAll(differences); this.differences = diffBuilder.build(); } @@ -68,12 +68,12 @@ public class DifferenceHolder /** * differences only holds one 'side' of the difference - if A and B mismatch, only A will be a key in the map */ - public Set keyHosts() + public Set keyHosts() { return differences.keySet(); } - public HostDifferences get(InetAddress hostWithDifference) + public HostDifferences get(InetAddressAndPort hostWithDifference) { return differences.get(hostWithDifference); } @@ -85,7 +85,7 @@ public class DifferenceHolder '}'; } - public boolean hasDifferenceBetween(InetAddress node1, InetAddress node2, Range range) + public boolean hasDifferenceBetween(InetAddressAndPort node1, InetAddressAndPort node2, Range range) { HostDifferences diffsNode1 = differences.get(node1); if (diffsNode1 != null && diffsNode1.hasDifferencesFor(node2, range)) diff --git a/src/java/org/apache/cassandra/repair/asymmetric/HostDifferences.java b/src/java/org/apache/cassandra/repair/asymmetric/HostDifferences.java index 6cbe987323..ab294b90a1 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/HostDifferences.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/HostDifferences.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -28,23 +27,24 @@ import java.util.Set; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Tracks the differences for a single host */ public class HostDifferences { - private final Map>> perHostDifferences = new HashMap<>(); + private final Map>> perHostDifferences = new HashMap<>(); /** * Adds a set of differences between the node this instance is tracking and endpoint */ - public void add(InetAddress endpoint, List> difference) + public void add(InetAddressAndPort endpoint, List> difference) { perHostDifferences.put(endpoint, difference); } - public void addSingleRange(InetAddress remoteNode, Range rangeToFetch) + public void addSingleRange(InetAddressAndPort remoteNode, Range rangeToFetch) { perHostDifferences.computeIfAbsent(remoteNode, (x) -> new ArrayList<>()).add(rangeToFetch); } @@ -52,7 +52,7 @@ public class HostDifferences /** * Does this instance have differences for range with node2? */ - public boolean hasDifferencesFor(InetAddress node2, Range range) + public boolean hasDifferencesFor(InetAddressAndPort node2, Range range) { List> differences = get(node2); for (Range diff : differences) @@ -64,12 +64,12 @@ public class HostDifferences return false; } - public Set hosts() + public Set hosts() { return perHostDifferences.keySet(); } - public List> get(InetAddress differingHost) + public List> get(InetAddressAndPort differingHost) { return perHostDifferences.getOrDefault(differingHost, Collections.emptyList()); } diff --git a/src/java/org/apache/cassandra/repair/asymmetric/IncomingRepairStreamTracker.java b/src/java/org/apache/cassandra/repair/asymmetric/IncomingRepairStreamTracker.java index b41ddd858e..450336f31e 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/IncomingRepairStreamTracker.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/IncomingRepairStreamTracker.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -29,6 +28,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Tracks incoming streams for a single host @@ -60,7 +60,7 @@ public class IncomingRepairStreamTracker * @param range the range we need to stream from streamFromNode * @param streamFromNode the node we should stream from */ - public void addIncomingRangeFrom(Range range, InetAddress streamFromNode) + public void addIncomingRangeFrom(Range range, InetAddressAndPort streamFromNode) { logger.trace("adding incoming range {} from {}", range, streamFromNode); Set> newInput = RangeDenormalizer.denormalize(range, incoming); diff --git a/src/java/org/apache/cassandra/repair/asymmetric/PreferedNodeFilter.java b/src/java/org/apache/cassandra/repair/asymmetric/PreferedNodeFilter.java index 90788dce17..e8ca85d02e 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/PreferedNodeFilter.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/PreferedNodeFilter.java @@ -18,10 +18,11 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.Set; +import org.apache.cassandra.locator.InetAddressAndPort; + public interface PreferedNodeFilter { - public Set apply(InetAddress streamingNode, Set toStream); + public Set apply(InetAddressAndPort streamingNode, Set toStream); } \ No newline at end of file diff --git a/src/java/org/apache/cassandra/repair/asymmetric/ReduceHelper.java b/src/java/org/apache/cassandra/repair/asymmetric/ReduceHelper.java index ce05e9369f..c7d45bfa2d 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/ReduceHelper.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/ReduceHelper.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -31,6 +30,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Basic idea is that we track incoming ranges instead of blindly just exchanging the ranges that mismatch between two nodes @@ -47,19 +47,19 @@ public class ReduceHelper /** * Reduces the differences provided by the merkle trees to a minimum set of differences */ - public static ImmutableMap reduce(DifferenceHolder differences, PreferedNodeFilter filter) + public static ImmutableMap reduce(DifferenceHolder differences, PreferedNodeFilter filter) { - Map trackers = createIncomingRepairStreamTrackers(differences); - Map outgoingStreamCounts = new HashMap<>(); - ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); - for (Map.Entry trackerEntry : trackers.entrySet()) + Map trackers = createIncomingRepairStreamTrackers(differences); + Map outgoingStreamCounts = new HashMap<>(); + ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); + for (Map.Entry trackerEntry : trackers.entrySet()) { IncomingRepairStreamTracker tracker = trackerEntry.getValue(); HostDifferences rangesToFetch = new HostDifferences(); for (Map.Entry, StreamFromOptions> entry : tracker.getIncoming().entrySet()) { Range rangeToFetch = entry.getKey(); - for (InetAddress remoteNode : pickLeastStreaming(trackerEntry.getKey(), entry.getValue(), outgoingStreamCounts, filter)) + for (InetAddressAndPort remoteNode : pickLeastStreaming(trackerEntry.getKey(), entry.getValue(), outgoingStreamCounts, filter)) rangesToFetch.addSingleRange(remoteNode, rangeToFetch); } mapBuilder.put(trackerEntry.getKey(), rangesToFetch); @@ -69,14 +69,14 @@ public class ReduceHelper } @VisibleForTesting - static Map createIncomingRepairStreamTrackers(DifferenceHolder differences) + static Map createIncomingRepairStreamTrackers(DifferenceHolder differences) { - Map trackers = new HashMap<>(); + Map trackers = new HashMap<>(); - for (InetAddress hostWithDifference : differences.keyHosts()) + for (InetAddressAndPort hostWithDifference : differences.keyHosts()) { HostDifferences hostDifferences = differences.get(hostWithDifference); - for (InetAddress differingHost : hostDifferences.hosts()) + for (InetAddressAndPort differingHost : hostDifferences.hosts()) { List> differingRanges = hostDifferences.get(differingHost); // hostWithDifference has mismatching ranges differingRanges with differingHost: @@ -93,24 +93,24 @@ public class ReduceHelper } private static IncomingRepairStreamTracker getTracker(DifferenceHolder differences, - Map trackers, - InetAddress host) + Map trackers, + InetAddressAndPort host) { return trackers.computeIfAbsent(host, (h) -> new IncomingRepairStreamTracker(differences)); } // greedily pick the nodes doing the least amount of streaming - private static Collection pickLeastStreaming(InetAddress streamingNode, + private static Collection pickLeastStreaming(InetAddressAndPort streamingNode, StreamFromOptions toStreamFrom, - Map outgoingStreamCounts, + Map outgoingStreamCounts, PreferedNodeFilter filter) { - Set retSet = new HashSet<>(); - for (Set toStream : toStreamFrom.allStreams()) + Set retSet = new HashSet<>(); + for (Set toStream : toStreamFrom.allStreams()) { - InetAddress candidate = null; - Set prefered = filter.apply(streamingNode, toStream); - for (InetAddress node : prefered) + InetAddressAndPort candidate = null; + Set prefered = filter.apply(streamingNode, toStream); + for (InetAddressAndPort node : prefered) { if (candidate == null || outgoingStreamCounts.getOrDefault(candidate, 0) > outgoingStreamCounts.getOrDefault(node, 0)) { @@ -120,7 +120,7 @@ public class ReduceHelper // ok, found no prefered hosts, try all of them if (candidate == null) { - for (InetAddress node : toStream) + for (InetAddressAndPort node : toStream) { if (candidate == null || outgoingStreamCounts.getOrDefault(candidate, 0) > outgoingStreamCounts.getOrDefault(node, 0)) { diff --git a/src/java/org/apache/cassandra/repair/asymmetric/StreamFromOptions.java b/src/java/org/apache/cassandra/repair/asymmetric/StreamFromOptions.java index 4516f23b40..60709831f3 100644 --- a/src/java/org/apache/cassandra/repair/asymmetric/StreamFromOptions.java +++ b/src/java/org/apache/cassandra/repair/asymmetric/StreamFromOptions.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -28,6 +27,7 @@ import com.google.common.collect.Sets; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Keeps track of where a node needs to stream a given range from. @@ -53,18 +53,18 @@ public class StreamFromOptions /** * Contains the hosts to stream from - if two nodes are in the same inner set, they are identical for the range we are handling */ - private final Set> streamOptions = new HashSet<>(); + private final Set> streamOptions = new HashSet<>(); public StreamFromOptions(DifferenceHolder differences, Range range) { this(differences, range, Collections.emptySet()); } - private StreamFromOptions(DifferenceHolder differences, Range range, Set> existing) + private StreamFromOptions(DifferenceHolder differences, Range range, Set> existing) { this.differences = differences; this.range = range; - for (Set addresses : existing) + for (Set addresses : existing) this.streamOptions.add(Sets.newHashSet(addresses)); } @@ -75,11 +75,11 @@ public class StreamFromOptions * range we are tracking, then just add it to the set with the identical remote nodes. Otherwise create a new group * of nodes containing this new node. */ - public void add(InetAddress streamFromNode) + public void add(InetAddressAndPort streamFromNode) { - for (Set options : streamOptions) + for (Set options : streamOptions) { - InetAddress first = options.iterator().next(); + InetAddressAndPort first = options.iterator().next(); if (!differences.hasDifferenceBetween(first, streamFromNode, range)) { options.add(streamFromNode); @@ -94,7 +94,7 @@ public class StreamFromOptions return new StreamFromOptions(differences, withRange, streamOptions); } - public Iterable> allStreams() + public Iterable> allStreams() { return streamOptions; } diff --git a/src/java/org/apache/cassandra/repair/consistent/ConsistentSession.java b/src/java/org/apache/cassandra/repair/consistent/ConsistentSession.java index c137346ccc..78057e2361 100644 --- a/src/java/org/apache/cassandra/repair/consistent/ConsistentSession.java +++ b/src/java/org/apache/cassandra/repair/consistent/ConsistentSession.java @@ -33,6 +33,7 @@ import com.google.common.collect.Iterables; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.messages.FailSession; import org.apache.cassandra.repair.messages.FinalizeCommit; import org.apache.cassandra.repair.messages.FinalizePromise; @@ -55,7 +56,7 @@ import org.apache.cassandra.tools.nodetool.RepairAdmin; * There are 4 stages to a consistent incremental repair. * *

Repair prepare

- * First, the normal {@link ActiveRepairService#prepareForRepair(UUID, InetAddress, Set, RepairOption, List)} stuff + * First, the normal {@link ActiveRepairService#prepareForRepair(UUID, InetAddressAndPort, Set, RepairOption, List)} stuff * happens, which sends out {@link PrepareMessage} and creates a {@link ActiveRepairService.ParentRepairSession} * on the coordinator and each of the neighbors. * @@ -68,7 +69,7 @@ import org.apache.cassandra.tools.nodetool.RepairAdmin; * coordinator indicating success or failure. If the pending anti-compaction fails, the local session state is set * to {@code FAILED}. *

- * (see {@link LocalSessions#handlePrepareMessage(InetAddress, PrepareConsistentRequest)} + * (see {@link LocalSessions#handlePrepareMessage(InetAddressAndPort, PrepareConsistentRequest)} *

* Once the coordinator recieves positive {@code PrepareConsistentResponse} messages from all the participants, the * coordinator begins the normal repair process. @@ -99,8 +100,8 @@ import org.apache.cassandra.tools.nodetool.RepairAdmin; * & {@link CoordinatorSession#finalizeCommit()} *

* - * On the local session side, see {@link LocalSessions#handleFinalizeProposeMessage(InetAddress, FinalizePropose)} - * & {@link LocalSessions#handleFinalizeCommitMessage(InetAddress, FinalizeCommit)} + * On the local session side, see {@link LocalSessions#handleFinalizeProposeMessage(InetAddressAndPort, FinalizePropose)} + * & {@link LocalSessions#handleFinalizeCommitMessage(InetAddressAndPort, FinalizeCommit)} * *

Failure

* If there are any failures or problems during the process above, the session will be failed. When a session is failed, @@ -187,11 +188,11 @@ public abstract class ConsistentSession private volatile State state; public final UUID sessionID; - public final InetAddress coordinator; + public final InetAddressAndPort coordinator; public final ImmutableSet tableIds; public final long repairedAt; public final ImmutableSet> ranges; - public final ImmutableSet participants; + public final ImmutableSet participants; ConsistentSession(AbstractBuilder builder) { @@ -260,11 +261,11 @@ public abstract class ConsistentSession { private State state; private UUID sessionID; - private InetAddress coordinator; + private InetAddressAndPort coordinator; private Set ids; private long repairedAt; private Collection> ranges; - private Set participants; + private Set participants; void withState(State state) { @@ -276,7 +277,7 @@ public abstract class ConsistentSession this.sessionID = sessionID; } - void withCoordinator(InetAddress coordinator) + void withCoordinator(InetAddressAndPort coordinator) { this.coordinator = coordinator; } @@ -301,7 +302,7 @@ public abstract class ConsistentSession this.ranges = ranges; } - void withParticipants(Set peers) + void withParticipants(Set peers) { this.participants = peers; } diff --git a/src/java/org/apache/cassandra/repair/consistent/CoordinatorSession.java b/src/java/org/apache/cassandra/repair/consistent/CoordinatorSession.java index ba0025f2f0..f52a28de31 100644 --- a/src/java/org/apache/cassandra/repair/consistent/CoordinatorSession.java +++ b/src/java/org/apache/cassandra/repair/consistent/CoordinatorSession.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,6 +38,7 @@ import org.apache.commons.lang3.time.DurationFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.RepairSessionResult; @@ -58,7 +58,7 @@ public class CoordinatorSession extends ConsistentSession { private static final Logger logger = LoggerFactory.getLogger(CoordinatorSession.class); - private final Map participantStates = new HashMap<>(); + private final Map participantStates = new HashMap<>(); private final SettableFuture prepareFuture = SettableFuture.create(); private final SettableFuture finalizeProposeFuture = SettableFuture.create(); @@ -69,7 +69,7 @@ public class CoordinatorSession extends ConsistentSession public CoordinatorSession(Builder builder) { super(builder); - for (InetAddress participant : participants) + for (InetAddressAndPort participant : participants) { participantStates.put(participant, State.PREPARING); } @@ -95,7 +95,7 @@ public class CoordinatorSession extends ConsistentSession super.setState(state); } - public synchronized void setParticipantState(InetAddress participant, State state) + public synchronized void setParticipantState(InetAddressAndPort participant, State state) { logger.trace("Setting participant {} to state {} for repair {}", participant, state, sessionID); Preconditions.checkArgument(participantStates.containsKey(participant), @@ -115,7 +115,7 @@ public class CoordinatorSession extends ConsistentSession synchronized void setAll(State state) { - for (InetAddress participant : participants) + for (InetAddressAndPort participant : participants) { setParticipantState(participant, state); } @@ -131,7 +131,7 @@ public class CoordinatorSession extends ConsistentSession return getState() == State.FAILED || Iterables.any(participantStates.values(), v -> v == State.FAILED); } - protected void sendMessage(InetAddress destination, RepairMessage message) + protected void sendMessage(InetAddressAndPort destination, RepairMessage message) { logger.trace("Sending {} to {}", message, destination); MessageOut messageOut = new MessageOut(MessagingService.Verb.REPAIR_MESSAGE, message, RepairMessage.serializer); @@ -144,14 +144,14 @@ public class CoordinatorSession extends ConsistentSession logger.debug("Beginning prepare phase of incremental repair session {}", sessionID); PrepareConsistentRequest message = new PrepareConsistentRequest(sessionID, coordinator, participants); - for (final InetAddress participant : participants) + for (final InetAddressAndPort participant : participants) { sendMessage(participant, message); } return prepareFuture; } - public synchronized void handlePrepareResponse(InetAddress participant, boolean success) + public synchronized void handlePrepareResponse(InetAddressAndPort participant, boolean success) { if (getState() == State.FAILED) { @@ -185,14 +185,14 @@ public class CoordinatorSession extends ConsistentSession Preconditions.checkArgument(allStates(State.REPAIRING)); logger.debug("Proposing finalization of repair session {}", sessionID); FinalizePropose message = new FinalizePropose(sessionID); - for (final InetAddress participant : participants) + for (final InetAddressAndPort participant : participants) { sendMessage(participant, message); } return finalizeProposeFuture; } - public synchronized void handleFinalizePromise(InetAddress participant, boolean success) + public synchronized void handleFinalizePromise(InetAddressAndPort participant, boolean success) { if (getState() == State.FAILED) { @@ -221,7 +221,7 @@ public class CoordinatorSession extends ConsistentSession Preconditions.checkArgument(allStates(State.FINALIZE_PROMISED)); logger.debug("Committing finalization of repair session {}", sessionID); FinalizeCommit message = new FinalizeCommit(sessionID); - for (final InetAddress participant : participants) + for (final InetAddressAndPort participant : participants) { sendMessage(participant, message); } @@ -233,7 +233,7 @@ public class CoordinatorSession extends ConsistentSession { logger.info("Incremental repair session {} failed", sessionID); FailSession message = new FailSession(sessionID); - for (final InetAddress participant : participants) + for (final InetAddressAndPort participant : participants) { if (participantStates.get(participant) != State.FAILED) { diff --git a/src/java/org/apache/cassandra/repair/consistent/CoordinatorSessions.java b/src/java/org/apache/cassandra/repair/consistent/CoordinatorSessions.java index 211e0c14d6..bb84d0a11b 100644 --- a/src/java/org/apache/cassandra/repair/consistent/CoordinatorSessions.java +++ b/src/java/org/apache/cassandra/repair/consistent/CoordinatorSessions.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -26,6 +25,7 @@ import java.util.UUID; import com.google.common.base.Preconditions; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.messages.FailSession; import org.apache.cassandra.repair.messages.FinalizePromise; import org.apache.cassandra.repair.messages.PrepareConsistentResponse; @@ -43,7 +43,7 @@ public class CoordinatorSessions return new CoordinatorSession(builder); } - public synchronized CoordinatorSession registerSession(UUID sessionId, Set participants) + public synchronized CoordinatorSession registerSession(UUID sessionId, Set participants) { Preconditions.checkArgument(!sessions.containsKey(sessionId), "A coordinator already exists for session %s", sessionId); ActiveRepairService.ParentRepairSession prs = ActiveRepairService.instance.getParentRepairSession(sessionId); diff --git a/src/java/org/apache/cassandra/repair/consistent/LocalSessionInfo.java b/src/java/org/apache/cassandra/repair/consistent/LocalSessionInfo.java index 903aeb5477..98b883a7d5 100644 --- a/src/java/org/apache/cassandra/repair/consistent/LocalSessionInfo.java +++ b/src/java/org/apache/cassandra/repair/consistent/LocalSessionInfo.java @@ -21,10 +21,12 @@ package org.apache.cassandra.repair.consistent; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableMetadata; @@ -40,6 +42,7 @@ public class LocalSessionInfo public static final String LAST_UPDATE = "LAST_UPDATE"; public static final String COORDINATOR = "COORDINATOR"; public static final String PARTICIPANTS = "PARTICIPANTS"; + public static final String PARTICIPANTS_WP = "PARTICIPANTS_WP"; public static final String TABLES = "TABLES"; @@ -59,7 +62,8 @@ public class LocalSessionInfo m.put(STARTED, Integer.toString(session.getStartedAt())); m.put(LAST_UPDATE, Integer.toString(session.getLastUpdate())); m.put(COORDINATOR, session.coordinator.toString()); - m.put(PARTICIPANTS, Joiner.on(',').join(Iterables.transform(session.participants, InetAddress::toString))); + m.put(PARTICIPANTS, Joiner.on(',').join(Iterables.transform(session.participants.stream().map(peer -> peer.address).collect(Collectors.toList()), InetAddress::getHostAddress))); + m.put(PARTICIPANTS_WP, Joiner.on(',').join(Iterables.transform(session.participants, InetAddressAndPort::toString))); m.put(TABLES, Joiner.on(',').join(Iterables.transform(session.tableIds, LocalSessionInfo::tableString))); return m; diff --git a/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java b/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java index 4ef2c2cb42..e62f6fdb4e 100644 --- a/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java +++ b/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java @@ -20,6 +20,7 @@ package org.apache.cassandra.repair.consistent; import java.io.IOException; import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.time.Instant; import java.util.Date; @@ -33,6 +34,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.annotation.Nullable; import com.google.common.annotations.VisibleForTesting; @@ -49,13 +51,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.db.ColumnFamilyStore; +import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.marshal.BytesType; -import org.apache.cassandra.db.marshal.InetAddressType; import org.apache.cassandra.db.marshal.UUIDType; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Range; @@ -141,13 +148,13 @@ public class LocalSessions } @VisibleForTesting - protected InetAddress getBroadcastAddress() + protected InetAddressAndPort getBroadcastAddressAndPort() { - return FBUtilities.getBroadcastAddress(); + return FBUtilities.getBroadcastAddressAndPort(); } @VisibleForTesting - protected boolean isAlive(InetAddress address) + protected boolean isAlive(InetAddressAndPort address) { return FailureDetector.instance.isAlive(address); } @@ -177,14 +184,14 @@ public class LocalSessions logger.info("Cancelling local repair session {}", sessionID); LocalSession session = getSession(sessionID); Preconditions.checkArgument(session != null, "Session {} does not exist", sessionID); - Preconditions.checkArgument(force || session.coordinator.equals(getBroadcastAddress()), + Preconditions.checkArgument(force || session.coordinator.equals(getBroadcastAddressAndPort()), "Cancel session %s from it's coordinator (%s) or use --force", sessionID, session.coordinator); setStateAndSave(session, FAILED); - for (InetAddress participant : session.participants) + for (InetAddressAndPort participant : session.participants) { - if (!participant.equals(getBroadcastAddress())) + if (!participant.equals(getBroadcastAddressAndPort())) sendMessage(participant, new FailSession(sessionID)); } } @@ -335,10 +342,12 @@ public class LocalSessions "repaired_at, " + "state, " + "coordinator, " + + "coordinator_port, " + "participants, " + + "participants_wp," + "ranges, " + "cfids) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; QueryProcessor.executeInternal(String.format(query, keyspace, table), session.sessionID, @@ -346,8 +355,10 @@ public class LocalSessions Date.from(Instant.ofEpochSecond(session.getLastUpdate())), Date.from(Instant.ofEpochMilli(session.repairedAt)), session.getState().ordinal(), - session.coordinator, - session.participants, + session.coordinator.address, + session.coordinator.port, + session.participants.stream().map(participant -> participant.address).collect(Collectors.toSet()), + session.participants.stream().map(participant -> participant.toString()).collect(Collectors.toSet()), serializeRanges(session.ranges), tableIdToUuid(session.tableIds)); } @@ -362,12 +373,27 @@ public class LocalSessions LocalSession.Builder builder = LocalSession.builder(); builder.withState(ConsistentSession.State.valueOf(row.getInt("state"))); builder.withSessionID(row.getUUID("parent_id")); - builder.withCoordinator(row.getInetAddress("coordinator")); + InetAddressAndPort coordinator = InetAddressAndPort.getByAddressOverrideDefaults( + row.getInetAddress("coordinator"), + row.getInt("coordinator_port")); + builder.withCoordinator(coordinator); builder.withTableIds(uuidToTableId(row.getSet("cfids", UUIDType.instance))); builder.withRepairedAt(row.getTimestamp("repaired_at").getTime()); builder.withRanges(deserializeRanges(row.getSet("ranges", BytesType.instance))); - builder.withParticipants(row.getSet("participants", InetAddressType.instance)); - + //There is no cross version streaming and thus no cross version repair so assume that + //any valid repair sessions has the participants_wp column and any that doesn't is malformed + Set participants = row.getSet("participants_wp", UTF8Type.instance); + builder.withParticipants(participants.stream().map(participant -> + { + try + { + return InetAddressAndPort.getByName(participant); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + }).collect(Collectors.toSet())); builder.withStartedAt(dateToSeconds(row.getTimestamp("started_at"))); builder.withLastUpdate(dateToSeconds(row.getTimestamp("last_update"))); @@ -440,7 +466,7 @@ public class LocalSessions } @VisibleForTesting - LocalSession createSessionUnsafe(UUID sessionId, ActiveRepairService.ParentRepairSession prs, Set peers) + LocalSession createSessionUnsafe(UUID sessionId, ActiveRepairService.ParentRepairSession prs, Set peers) { LocalSession.Builder builder = LocalSession.builder(); builder.withState(ConsistentSession.State.PREPARING); @@ -464,7 +490,7 @@ public class LocalSessions return ActiveRepairService.instance.getParentRepairSession(sessionID); } - protected void sendMessage(InetAddress destination, RepairMessage message) + protected void sendMessage(InetAddressAndPort destination, RepairMessage message) { logger.trace("sending {} to {}", message, destination); MessageOut messageOut = new MessageOut(MessagingService.Verb.REPAIR_MESSAGE, message, RepairMessage.serializer); @@ -536,12 +562,12 @@ public class LocalSessions * successfully. If the pending anti compaction fails, a failure message is sent to the coordinator, * cancelling the session. */ - public void handlePrepareMessage(InetAddress from, PrepareConsistentRequest request) + public void handlePrepareMessage(InetAddressAndPort from, PrepareConsistentRequest request) { logger.trace("received {} from {}", request, from); UUID sessionID = request.parentSession; - InetAddress coordinator = request.coordinator; - Set peers = request.participants; + InetAddressAndPort coordinator = request.coordinator; + Set peers = request.participants; ActiveRepairService.ParentRepairSession parentSession; try @@ -568,7 +594,7 @@ public class LocalSessions { logger.debug("Prepare phase for incremental repair session {} completed", sessionID); setStateAndSave(session, PREPARED); - sendMessage(coordinator, new PrepareConsistentResponse(sessionID, getBroadcastAddress(), true)); + sendMessage(coordinator, new PrepareConsistentResponse(sessionID, getBroadcastAddressAndPort(), true)); executor.shutdown(); } @@ -587,7 +613,7 @@ public class LocalSessions { logger.error("Prepare phase for incremental repair session {} failed", sessionID, t); } - sendMessage(coordinator, new PrepareConsistentResponse(sessionID, getBroadcastAddress(), false)); + sendMessage(coordinator, new PrepareConsistentResponse(sessionID, getBroadcastAddressAndPort(), false)); failSession(sessionID, false); executor.shutdown(); } @@ -604,7 +630,7 @@ public class LocalSessions } } - public void handleFinalizeProposeMessage(InetAddress from, FinalizePropose propose) + public void handleFinalizeProposeMessage(InetAddressAndPort from, FinalizePropose propose) { logger.trace("received {} from {}", propose, from); UUID sessionID = propose.sessionID; @@ -629,7 +655,7 @@ public class LocalSessions */ syncTable(); - sendMessage(from, new FinalizePromise(sessionID, getBroadcastAddress(), true)); + sendMessage(from, new FinalizePromise(sessionID, getBroadcastAddressAndPort(), true)); logger.debug("Received FinalizePropose message for incremental repair session {}, responded with FinalizePromise", sessionID); } catch (IllegalArgumentException e) @@ -659,7 +685,7 @@ public class LocalSessions * as part of the compaction process, and avoids having to worry about in progress compactions interfering with the * promotion. */ - public void handleFinalizeCommitMessage(InetAddress from, FinalizeCommit commit) + public void handleFinalizeCommitMessage(InetAddressAndPort from, FinalizeCommit commit) { logger.trace("received {} from {}", commit, from); UUID sessionID = commit.sessionID; @@ -674,7 +700,7 @@ public class LocalSessions logger.info("Finalized local repair session {}", sessionID); } - public void handleFailSessionMessage(InetAddress from, FailSession msg) + public void handleFailSessionMessage(InetAddressAndPort from, FailSession msg) { logger.trace("received {} from {}", msg, from); failSession(msg.sessionID, false); @@ -684,16 +710,16 @@ public class LocalSessions { logger.debug("Attempting to learn the outcome of unfinished local incremental repair session {}", session.sessionID); StatusRequest request = new StatusRequest(session.sessionID); - for (InetAddress participant : session.participants) + for (InetAddressAndPort participant : session.participants) { - if (!getBroadcastAddress().equals(participant) && isAlive(participant)) + if (!getBroadcastAddressAndPort().equals(participant) && isAlive(participant)) { sendMessage(participant, request); } } } - public void handleStatusRequest(InetAddress from, StatusRequest request) + public void handleStatusRequest(InetAddressAndPort from, StatusRequest request) { logger.trace("received {} from {}", request, from); UUID sessionID = request.sessionID; @@ -710,7 +736,7 @@ public class LocalSessions } } - public void handleStatusResponse(InetAddress from, StatusResponse response) + public void handleStatusResponse(InetAddressAndPort from, StatusResponse response) { logger.trace("received {} from {}", response, from); UUID sessionID = response.sessionID; diff --git a/src/java/org/apache/cassandra/repair/consistent/SyncStatSummary.java b/src/java/org/apache/cassandra/repair/consistent/SyncStatSummary.java index 015b55855b..156fde7739 100644 --- a/src/java/org/apache/cassandra/repair/consistent/SyncStatSummary.java +++ b/src/java/org/apache/cassandra/repair/consistent/SyncStatSummary.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -27,6 +26,7 @@ import java.util.Objects; import com.google.common.collect.Lists; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.RepairResult; import org.apache.cassandra.repair.RepairSessionResult; import org.apache.cassandra.repair.SyncStat; @@ -42,14 +42,14 @@ public class SyncStatSummary private static class Session { - final InetAddress src; - final InetAddress dst; + final InetAddressAndPort src; + final InetAddressAndPort dst; int files = 0; long bytes = 0; long ranges = 0; - Session(InetAddress src, InetAddress dst) + Session(InetAddressAndPort src, InetAddressAndPort dst) { this.src = src; this.dst = dst; @@ -84,7 +84,7 @@ public class SyncStatSummary int ranges = -1; boolean totalsCalculated = false; - final Map, Session> sessions = new HashMap<>(); + final Map, Session> sessions = new HashMap<>(); Table(String keyspace, String table) { @@ -92,9 +92,9 @@ public class SyncStatSummary this.table = table; } - Session getOrCreate(InetAddress from, InetAddress to) + Session getOrCreate(InetAddressAndPort from, InetAddressAndPort to) { - Pair k = Pair.create(from, to); + Pair k = Pair.create(from, to); if (!sessions.containsKey(k)) { sessions.put(k, new Session(from, to)); diff --git a/src/java/org/apache/cassandra/repair/messages/AsymmetricSyncRequest.java b/src/java/org/apache/cassandra/repair/messages/AsymmetricSyncRequest.java index b75ad7fb52..6d7626972c 100644 --- a/src/java/org/apache/cassandra/repair/messages/AsymmetricSyncRequest.java +++ b/src/java/org/apache/cassandra/repair/messages/AsymmetricSyncRequest.java @@ -30,6 +30,7 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.RepairJobDesc; @@ -39,13 +40,13 @@ public class AsymmetricSyncRequest extends RepairMessage { public static MessageSerializer serializer = new SyncRequestSerializer(); - public final InetAddress initiator; - public final InetAddress fetchingNode; - public final InetAddress fetchFrom; + public final InetAddressAndPort initiator; + public final InetAddressAndPort fetchingNode; + public final InetAddressAndPort fetchFrom; public final Collection> ranges; public final PreviewKind previewKind; - public AsymmetricSyncRequest(RepairJobDesc desc, InetAddress initiator, InetAddress fetchingNode, InetAddress fetchFrom, Collection> ranges, PreviewKind previewKind) + public AsymmetricSyncRequest(RepairJobDesc desc, InetAddressAndPort initiator, InetAddressAndPort fetchingNode, InetAddressAndPort fetchFrom, Collection> ranges, PreviewKind previewKind) { super(Type.ASYMMETRIC_SYNC_REQUEST, desc); this.initiator = initiator; @@ -80,9 +81,9 @@ public class AsymmetricSyncRequest extends RepairMessage public void serialize(AsymmetricSyncRequest message, DataOutputPlus out, int version) throws IOException { RepairJobDesc.serializer.serialize(message.desc, out, version); - CompactEndpointSerializationHelper.serialize(message.initiator, out); - CompactEndpointSerializationHelper.serialize(message.fetchingNode, out); - CompactEndpointSerializationHelper.serialize(message.fetchFrom, out); + CompactEndpointSerializationHelper.instance.serialize(message.initiator, out, version); + CompactEndpointSerializationHelper.instance.serialize(message.fetchingNode, out, version); + CompactEndpointSerializationHelper.instance.serialize(message.fetchFrom, out, version); out.writeInt(message.ranges.size()); for (Range range : message.ranges) { @@ -95,9 +96,9 @@ public class AsymmetricSyncRequest extends RepairMessage public AsymmetricSyncRequest deserialize(DataInputPlus in, int version) throws IOException { RepairJobDesc desc = RepairJobDesc.serializer.deserialize(in, version); - InetAddress owner = CompactEndpointSerializationHelper.deserialize(in); - InetAddress src = CompactEndpointSerializationHelper.deserialize(in); - InetAddress dst = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort owner = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort src = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort dst = CompactEndpointSerializationHelper.instance.deserialize(in, version); int rangesCount = in.readInt(); List> ranges = new ArrayList<>(rangesCount); for (int i = 0; i < rangesCount; ++i) @@ -109,7 +110,9 @@ public class AsymmetricSyncRequest extends RepairMessage public long serializedSize(AsymmetricSyncRequest message, int version) { long size = RepairJobDesc.serializer.serializedSize(message.desc, version); - size += 3 * CompactEndpointSerializationHelper.serializedSize(message.initiator); + size += CompactEndpointSerializationHelper.instance.serializedSize(message.initiator, version); + size += CompactEndpointSerializationHelper.instance.serializedSize(message.fetchingNode, version); + size += CompactEndpointSerializationHelper.instance.serializedSize(message.fetchFrom, version); size += TypeSizes.sizeof(message.ranges.size()); for (Range range : message.ranges) size += AbstractBounds.tokenSerializer.serializedSize(range, version); diff --git a/src/java/org/apache/cassandra/repair/messages/FinalizePromise.java b/src/java/org/apache/cassandra/repair/messages/FinalizePromise.java index 6c2834794e..449748ab45 100644 --- a/src/java/org/apache/cassandra/repair/messages/FinalizePromise.java +++ b/src/java/org/apache/cassandra/repair/messages/FinalizePromise.java @@ -19,24 +19,22 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.UUID; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.serializers.InetAddressSerializer; -import org.apache.cassandra.serializers.TypeSerializer; -import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.utils.UUIDSerializer; public class FinalizePromise extends RepairMessage { public final UUID sessionID; - public final InetAddress participant; + public final InetAddressAndPort participant; public final boolean promised; - public FinalizePromise(UUID sessionID, InetAddress participant, boolean promised) + public FinalizePromise(UUID sessionID, InetAddressAndPort participant, boolean promised) { super(Type.FINALIZE_PROMISE, null); assert sessionID != null; @@ -68,26 +66,24 @@ public class FinalizePromise extends RepairMessage public static MessageSerializer serializer = new MessageSerializer() { - private TypeSerializer inetSerializer = InetAddressSerializer.instance; - public void serialize(FinalizePromise msg, DataOutputPlus out, int version) throws IOException { UUIDSerializer.serializer.serialize(msg.sessionID, out, version); - ByteBufferUtil.writeWithShortLength(inetSerializer.serialize(msg.participant), out); + CompactEndpointSerializationHelper.instance.serialize(msg.participant, out, version); out.writeBoolean(msg.promised); } public FinalizePromise deserialize(DataInputPlus in, int version) throws IOException { return new FinalizePromise(UUIDSerializer.serializer.deserialize(in, version), - inetSerializer.deserialize(ByteBufferUtil.readWithShortLength(in)), + CompactEndpointSerializationHelper.instance.deserialize(in, version), in.readBoolean()); } public long serializedSize(FinalizePromise msg, int version) { long size = UUIDSerializer.serializer.serializedSize(msg.sessionID, version); - size += ByteBufferUtil.serializedSizeWithShortLength(inetSerializer.serialize(msg.participant)); + size += CompactEndpointSerializationHelper.instance.serializedSize(msg.participant, version); size += TypeSizes.sizeof(msg.promised); return size; } diff --git a/src/java/org/apache/cassandra/repair/messages/PrepareConsistentRequest.java b/src/java/org/apache/cassandra/repair/messages/PrepareConsistentRequest.java index 57056ef801..9aae256e5a 100644 --- a/src/java/org/apache/cassandra/repair/messages/PrepareConsistentRequest.java +++ b/src/java/org/apache/cassandra/repair/messages/PrepareConsistentRequest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -29,18 +28,17 @@ import com.google.common.collect.ImmutableSet; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.serializers.InetAddressSerializer; -import org.apache.cassandra.serializers.TypeSerializer; -import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.utils.UUIDSerializer; public class PrepareConsistentRequest extends RepairMessage { public final UUID parentSession; - public final InetAddress coordinator; - public final Set participants; + public final InetAddressAndPort coordinator; + public final Set participants; - public PrepareConsistentRequest(UUID parentSession, InetAddress coordinator, Set participants) + public PrepareConsistentRequest(UUID parentSession, InetAddressAndPort coordinator, Set participants) { super(Type.CONSISTENT_REQUEST, null); assert parentSession != null; @@ -82,28 +80,27 @@ public class PrepareConsistentRequest extends RepairMessage public static MessageSerializer serializer = new MessageSerializer() { - private TypeSerializer inetSerializer = InetAddressSerializer.instance; public void serialize(PrepareConsistentRequest request, DataOutputPlus out, int version) throws IOException { UUIDSerializer.serializer.serialize(request.parentSession, out, version); - ByteBufferUtil.writeWithShortLength(inetSerializer.serialize(request.coordinator), out); + CompactEndpointSerializationHelper.instance.serialize(request.coordinator, out, version); out.writeInt(request.participants.size()); - for (InetAddress peer : request.participants) + for (InetAddressAndPort peer : request.participants) { - ByteBufferUtil.writeWithShortLength(inetSerializer.serialize(peer), out); + CompactEndpointSerializationHelper.instance.serialize(peer, out, version); } } public PrepareConsistentRequest deserialize(DataInputPlus in, int version) throws IOException { UUID sessionId = UUIDSerializer.serializer.deserialize(in, version); - InetAddress coordinator = inetSerializer.deserialize(ByteBufferUtil.readWithShortLength(in)); + InetAddressAndPort coordinator = CompactEndpointSerializationHelper.instance.deserialize(in, version); int numPeers = in.readInt(); - Set peers = new HashSet<>(numPeers); + Set peers = new HashSet<>(numPeers); for (int i = 0; i < numPeers; i++) { - InetAddress peer = inetSerializer.deserialize(ByteBufferUtil.readWithShortLength(in)); + InetAddressAndPort peer = CompactEndpointSerializationHelper.instance.deserialize(in, version); peers.add(peer); } return new PrepareConsistentRequest(sessionId, coordinator, peers); @@ -112,11 +109,11 @@ public class PrepareConsistentRequest extends RepairMessage public long serializedSize(PrepareConsistentRequest request, int version) { long size = UUIDSerializer.serializer.serializedSize(request.parentSession, version); - size += ByteBufferUtil.serializedSizeWithShortLength(inetSerializer.serialize(request.coordinator)); + size += CompactEndpointSerializationHelper.instance.serializedSize(request.coordinator, version); size += TypeSizes.sizeof(request.participants.size()); - for (InetAddress peer : request.participants) + for (InetAddressAndPort peer : request.participants) { - size += ByteBufferUtil.serializedSizeWithShortLength(inetSerializer.serialize(peer)); + size += CompactEndpointSerializationHelper.instance.serializedSize(peer, version); } return size; } diff --git a/src/java/org/apache/cassandra/repair/messages/PrepareConsistentResponse.java b/src/java/org/apache/cassandra/repair/messages/PrepareConsistentResponse.java index cf4410ae4f..630f18efda 100644 --- a/src/java/org/apache/cassandra/repair/messages/PrepareConsistentResponse.java +++ b/src/java/org/apache/cassandra/repair/messages/PrepareConsistentResponse.java @@ -19,24 +19,22 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.UUID; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.serializers.InetAddressSerializer; -import org.apache.cassandra.serializers.TypeSerializer; -import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.utils.UUIDSerializer; public class PrepareConsistentResponse extends RepairMessage { public final UUID parentSession; - public final InetAddress participant; + public final InetAddressAndPort participant; public final boolean success; - public PrepareConsistentResponse(UUID parentSession, InetAddress participant, boolean success) + public PrepareConsistentResponse(UUID parentSession, InetAddressAndPort participant, boolean success) { super(Type.CONSISTENT_RESPONSE, null); assert parentSession != null; @@ -68,25 +66,24 @@ public class PrepareConsistentResponse extends RepairMessage public static MessageSerializer serializer = new MessageSerializer() { - private TypeSerializer inetSerializer = InetAddressSerializer.instance; public void serialize(PrepareConsistentResponse response, DataOutputPlus out, int version) throws IOException { UUIDSerializer.serializer.serialize(response.parentSession, out, version); - ByteBufferUtil.writeWithShortLength(inetSerializer.serialize(response.participant), out); + CompactEndpointSerializationHelper.instance.serialize(response.participant, out, version); out.writeBoolean(response.success); } public PrepareConsistentResponse deserialize(DataInputPlus in, int version) throws IOException { return new PrepareConsistentResponse(UUIDSerializer.serializer.deserialize(in, version), - inetSerializer.deserialize(ByteBufferUtil.readWithShortLength(in)), + CompactEndpointSerializationHelper.instance.deserialize(in, version), in.readBoolean()); } public long serializedSize(PrepareConsistentResponse response, int version) { long size = UUIDSerializer.serializer.serializedSize(response.parentSession, version); - size += ByteBufferUtil.serializedSizeWithShortLength(inetSerializer.serialize(response.participant)); + size += CompactEndpointSerializationHelper.instance.serializedSize(response.participant, version); size += TypeSizes.sizeof(response.success); return size; } diff --git a/src/java/org/apache/cassandra/repair/messages/SyncComplete.java b/src/java/org/apache/cassandra/repair/messages/SyncComplete.java index 7b68daf6bd..1f1344da52 100644 --- a/src/java/org/apache/cassandra/repair/messages/SyncComplete.java +++ b/src/java/org/apache/cassandra/repair/messages/SyncComplete.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -26,6 +25,7 @@ import java.util.Objects; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.NodePair; import org.apache.cassandra.repair.RepairJobDesc; import org.apache.cassandra.streaming.SessionSummary; @@ -53,7 +53,7 @@ public class SyncComplete extends RepairMessage this.summaries = summaries; } - public SyncComplete(RepairJobDesc desc, InetAddress endpoint1, InetAddress endpoint2, boolean success, List summaries) + public SyncComplete(RepairJobDesc desc, InetAddressAndPort endpoint1, InetAddressAndPort endpoint2, boolean success, List summaries) { super(Type.SYNC_COMPLETE, desc); this.summaries = summaries; diff --git a/src/java/org/apache/cassandra/repair/messages/SyncRequest.java b/src/java/org/apache/cassandra/repair/messages/SyncRequest.java index 01601e2276..a0bf4e2f90 100644 --- a/src/java/org/apache/cassandra/repair/messages/SyncRequest.java +++ b/src/java/org/apache/cassandra/repair/messages/SyncRequest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -30,6 +29,7 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.RepairJobDesc; @@ -45,14 +45,14 @@ public class SyncRequest extends RepairMessage { public static MessageSerializer serializer = new SyncRequestSerializer(); - public final InetAddress initiator; - public final InetAddress src; - public final InetAddress dst; + public final InetAddressAndPort initiator; + public final InetAddressAndPort src; + public final InetAddressAndPort dst; public final Collection> ranges; public final PreviewKind previewKind; - public SyncRequest(RepairJobDesc desc, InetAddress initiator, InetAddress src, InetAddress dst, Collection> ranges, PreviewKind previewKind) - { + public SyncRequest(RepairJobDesc desc, InetAddressAndPort initiator, InetAddressAndPort src, InetAddressAndPort dst, Collection> ranges, PreviewKind previewKind) + { super(Type.SYNC_REQUEST, desc); this.initiator = initiator; this.src = src; @@ -87,9 +87,9 @@ public class SyncRequest extends RepairMessage public void serialize(SyncRequest message, DataOutputPlus out, int version) throws IOException { RepairJobDesc.serializer.serialize(message.desc, out, version); - CompactEndpointSerializationHelper.serialize(message.initiator, out); - CompactEndpointSerializationHelper.serialize(message.src, out); - CompactEndpointSerializationHelper.serialize(message.dst, out); + CompactEndpointSerializationHelper.instance.serialize(message.initiator, out, version); + CompactEndpointSerializationHelper.instance.serialize(message.src, out, version); + CompactEndpointSerializationHelper.instance.serialize(message.dst, out, version); out.writeInt(message.ranges.size()); for (Range range : message.ranges) { @@ -102,9 +102,9 @@ public class SyncRequest extends RepairMessage public SyncRequest deserialize(DataInputPlus in, int version) throws IOException { RepairJobDesc desc = RepairJobDesc.serializer.deserialize(in, version); - InetAddress owner = CompactEndpointSerializationHelper.deserialize(in); - InetAddress src = CompactEndpointSerializationHelper.deserialize(in); - InetAddress dst = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort owner = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort src = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort dst = CompactEndpointSerializationHelper.instance.deserialize(in, version); int rangesCount = in.readInt(); List> ranges = new ArrayList<>(rangesCount); for (int i = 0; i < rangesCount; ++i) @@ -116,7 +116,7 @@ public class SyncRequest extends RepairMessage public long serializedSize(SyncRequest message, int version) { long size = RepairJobDesc.serializer.serializedSize(message.desc, version); - size += 3 * CompactEndpointSerializationHelper.serializedSize(message.initiator); + size += 3 * CompactEndpointSerializationHelper.instance.serializedSize(message.initiator, version); size += TypeSizes.sizeof(message.ranges.size()); for (Range range : message.ranges) size += AbstractBounds.tokenSerializer.serializedSize(range, version); diff --git a/src/java/org/apache/cassandra/schema/MigrationManager.java b/src/java/org/apache/cassandra/schema/MigrationManager.java index ef19c25b55..c8881e5e68 100644 --- a/src/java/org/apache/cassandra/schema/MigrationManager.java +++ b/src/java/org/apache/cassandra/schema/MigrationManager.java @@ -18,7 +18,6 @@ package org.apache.cassandra.schema; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.*; import java.lang.management.ManagementFactory; @@ -39,6 +38,7 @@ import org.apache.cassandra.gms.*; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.service.StorageService; @@ -59,10 +59,10 @@ public class MigrationManager private MigrationManager() {} - public static void scheduleSchemaPull(InetAddress endpoint, EndpointState state) + public static void scheduleSchemaPull(InetAddressAndPort endpoint, EndpointState state) { UUID schemaVersion = state.getSchemaVersion(); - if (!endpoint.equals(FBUtilities.getBroadcastAddress()) && schemaVersion != null) + if (!endpoint.equals(FBUtilities.getBroadcastAddressAndPort()) && schemaVersion != null) maybeScheduleSchemaPull(schemaVersion, endpoint); } @@ -70,7 +70,7 @@ public class MigrationManager * If versions differ this node sends request with local migration list to the endpoint * and expecting to receive a list of migrations to apply locally. */ - private static void maybeScheduleSchemaPull(final UUID theirVersion, final InetAddress endpoint) + private static void maybeScheduleSchemaPull(final UUID theirVersion, final InetAddressAndPort endpoint) { if (Schema.instance.getVersion() == null) { @@ -130,7 +130,7 @@ public class MigrationManager } } - private static Future submitMigrationTask(InetAddress endpoint) + private static Future submitMigrationTask(InetAddressAndPort endpoint) { /* * Do not de-ref the future because that causes distributed deadlock (CASSANDRA-3832) because we are @@ -139,7 +139,7 @@ public class MigrationManager return StageManager.getStage(Stage.MIGRATION).submit(new MigrationTask(endpoint)); } - static boolean shouldPullSchemaFrom(InetAddress endpoint) + static boolean shouldPullSchemaFrom(InetAddressAndPort endpoint) { /* * Don't request schema from nodes with a differnt or unknonw major version (may have incompatible schema) @@ -427,7 +427,7 @@ public class MigrationManager FBUtilities.waitOnFuture(announce(mutations)); } - private static void pushSchemaMutation(InetAddress endpoint, Collection schema) + private static void pushSchemaMutation(InetAddressAndPort endpoint, Collection schema) { MessageOut> msg = new MessageOut<>(MessagingService.Verb.DEFINITIONS_UPDATE, schema, @@ -446,10 +446,10 @@ public class MigrationManager } }); - for (InetAddress endpoint : Gossiper.instance.getLiveMembers()) + for (InetAddressAndPort endpoint : Gossiper.instance.getLiveMembers()) { // only push schema to nodes with known and equal versions - if (!endpoint.equals(FBUtilities.getBroadcastAddress()) && + if (!endpoint.equals(FBUtilities.getBroadcastAddressAndPort()) && MessagingService.instance().knowsVersion(endpoint) && MessagingService.instance().getRawVersion(endpoint) == MessagingService.current_version) pushSchemaMutation(endpoint, schema); @@ -486,11 +486,11 @@ public class MigrationManager Schema.instance.clear(); - Set liveEndpoints = Gossiper.instance.getLiveMembers(); - liveEndpoints.remove(FBUtilities.getBroadcastAddress()); + Set liveEndpoints = Gossiper.instance.getLiveMembers(); + liveEndpoints.remove(FBUtilities.getBroadcastAddressAndPort()); // force migration if there are nodes around - for (InetAddress node : liveEndpoints) + for (InetAddressAndPort node : liveEndpoints) { if (shouldPullSchemaFrom(node)) { diff --git a/src/java/org/apache/cassandra/schema/MigrationTask.java b/src/java/org/apache/cassandra/schema/MigrationTask.java index 73e396de73..6ff206a6ba 100644 --- a/src/java/org/apache/cassandra/schema/MigrationTask.java +++ b/src/java/org/apache/cassandra/schema/MigrationTask.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.schema; -import java.net.InetAddress; import java.util.Collection; import java.util.EnumSet; import java.util.Set; @@ -32,6 +31,7 @@ import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.SystemKeyspace.BootstrapState; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.gms.FailureDetector; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IAsyncCallback; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessageOut; @@ -46,9 +46,9 @@ final class MigrationTask extends WrappedRunnable private static final Set monitoringBootstrapStates = EnumSet.of(BootstrapState.NEEDS_BOOTSTRAP, BootstrapState.IN_PROGRESS); - private final InetAddress endpoint; + private final InetAddressAndPort endpoint; - MigrationTask(InetAddress endpoint) + MigrationTask(InetAddressAndPort endpoint) { this.endpoint = endpoint; } diff --git a/src/java/org/apache/cassandra/service/AbstractReadExecutor.java b/src/java/org/apache/cassandra/service/AbstractReadExecutor.java index 7ff0b9b4ee..e06131eda6 100644 --- a/src/java/org/apache/cassandra/service/AbstractReadExecutor.java +++ b/src/java/org/apache/cassandra/service/AbstractReadExecutor.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Collection; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -38,6 +37,7 @@ import org.apache.cassandra.db.partitions.PartitionIterator; import org.apache.cassandra.exceptions.ReadFailureException; import org.apache.cassandra.exceptions.ReadTimeoutException; import org.apache.cassandra.exceptions.UnavailableException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.ReadRepairMetrics; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; @@ -60,12 +60,12 @@ public abstract class AbstractReadExecutor private static final Logger logger = LoggerFactory.getLogger(AbstractReadExecutor.class); protected final ReadCommand command; - protected final List targetReplicas; + protected final List targetReplicas; protected final ReadCallback handler; protected final TraceState traceState; protected final ColumnFamilyStore cfs; - AbstractReadExecutor(Keyspace keyspace, ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, List targetReplicas, long queryStartNanoTime) + AbstractReadExecutor(Keyspace keyspace, ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, List targetReplicas, long queryStartNanoTime) { this.command = command; this.targetReplicas = targetReplicas; @@ -78,27 +78,27 @@ public abstract class AbstractReadExecutor // TODO: we need this when talking with pre-3.0 nodes. So if we preserve the digest format moving forward, we can get rid of this once // we stop being compatible with pre-3.0 nodes. int digestVersion = MessagingService.current_version; - for (InetAddress replica : targetReplicas) + for (InetAddressAndPort replica : targetReplicas) digestVersion = Math.min(digestVersion, MessagingService.instance().getVersion(replica)); command.setDigestVersion(digestVersion); } - protected void makeDataRequests(Iterable endpoints) + protected void makeDataRequests(Iterable endpoints) { makeRequests(command, endpoints); } - protected void makeDigestRequests(Iterable endpoints) + protected void makeDigestRequests(Iterable endpoints) { makeRequests(command.copyAsDigestQuery(), endpoints); } - private void makeRequests(ReadCommand readCommand, Iterable endpoints) + private void makeRequests(ReadCommand readCommand, Iterable endpoints) { boolean hasLocalEndpoint = false; - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { if (StorageProxy.canDoLocalRequest(endpoint)) { @@ -132,7 +132,7 @@ public abstract class AbstractReadExecutor * * @return target replicas + the extra replica, *IF* we speculated. */ - public abstract Collection getContactedReplicas(); + public abstract Collection getContactedReplicas(); /** * send the initial set of requests @@ -184,12 +184,12 @@ public abstract class AbstractReadExecutor public static AbstractReadExecutor getReadExecutor(SinglePartitionReadCommand command, ConsistencyLevel consistencyLevel, long queryStartNanoTime) throws UnavailableException { Keyspace keyspace = Keyspace.open(command.metadata().keyspace); - List allReplicas = StorageProxy.getLiveSortedEndpoints(keyspace, command.partitionKey()); + List allReplicas = StorageProxy.getLiveSortedEndpoints(keyspace, command.partitionKey()); // 11980: Excluding EACH_QUORUM reads from potential RR, so that we do not miscount DC responses ReadRepairDecision repairDecision = consistencyLevel == ConsistencyLevel.EACH_QUORUM ? ReadRepairDecision.NONE : newReadRepairDecision(command.metadata()); - List targetReplicas = consistencyLevel.filterForQuery(keyspace, allReplicas, repairDecision); + List targetReplicas = consistencyLevel.filterForQuery(keyspace, allReplicas, repairDecision); // Throw UAE early if we don't have enough replicas. consistencyLevel.assureSufficientLiveNodes(keyspace, targetReplicas); @@ -223,12 +223,12 @@ public abstract class AbstractReadExecutor } // RRD.NONE or RRD.DC_LOCAL w/ multiple DCs. - InetAddress extraReplica = allReplicas.get(targetReplicas.size()); + InetAddressAndPort extraReplica = allReplicas.get(targetReplicas.size()); // With repair decision DC_LOCAL all replicas/target replicas may be in different order, so // we might have to find a replacement that's not already in targetReplicas. if (repairDecision == ReadRepairDecision.DC_LOCAL && targetReplicas.contains(extraReplica)) { - for (InetAddress address : allReplicas) + for (InetAddressAndPort address : allReplicas) { if (!targetReplicas.contains(address)) { @@ -269,7 +269,7 @@ public abstract class AbstractReadExecutor */ private final boolean logFailedSpeculation; - public NeverSpeculatingReadExecutor(Keyspace keyspace, ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, List targetReplicas, long queryStartNanoTime, boolean logFailedSpeculation) + public NeverSpeculatingReadExecutor(Keyspace keyspace, ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, List targetReplicas, long queryStartNanoTime, boolean logFailedSpeculation) { super(keyspace, cfs, command, consistencyLevel, targetReplicas, queryStartNanoTime); this.logFailedSpeculation = logFailedSpeculation; @@ -290,7 +290,7 @@ public abstract class AbstractReadExecutor } } - public Collection getContactedReplicas() + public Collection getContactedReplicas() { return targetReplicas; } @@ -304,7 +304,7 @@ public abstract class AbstractReadExecutor ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, - List targetReplicas, + List targetReplicas, long queryStartNanoTime) { super(keyspace, cfs, command, consistencyLevel, targetReplicas, queryStartNanoTime); @@ -314,7 +314,7 @@ public abstract class AbstractReadExecutor { // if CL + RR result in covering all replicas, getReadExecutor forces AlwaysSpeculating. So we know // that the last replica in our list is "extra." - List initialReplicas = targetReplicas.subList(0, targetReplicas.size() - 1); + List initialReplicas = targetReplicas.subList(0, targetReplicas.size() - 1); if (handler.blockfor < initialReplicas.size()) { @@ -347,7 +347,7 @@ public abstract class AbstractReadExecutor if (handler.resolver.isDataPresent()) retryCommand = command.copyAsDigestQuery(); - InetAddress extraReplica = Iterables.getLast(targetReplicas); + InetAddressAndPort extraReplica = Iterables.getLast(targetReplicas); if (traceState != null) traceState.trace("speculating read retry on {}", extraReplica); logger.trace("speculating read retry on {}", extraReplica); @@ -355,7 +355,7 @@ public abstract class AbstractReadExecutor } } - public Collection getContactedReplicas() + public Collection getContactedReplicas() { return speculated ? targetReplicas @@ -378,7 +378,7 @@ public abstract class AbstractReadExecutor ColumnFamilyStore cfs, ReadCommand command, ConsistencyLevel consistencyLevel, - List targetReplicas, + List targetReplicas, long queryStartNanoTime) { super(keyspace, cfs, command, consistencyLevel, targetReplicas, queryStartNanoTime); @@ -389,7 +389,7 @@ public abstract class AbstractReadExecutor // no-op } - public Collection getContactedReplicas() + public Collection getContactedReplicas() { return targetReplicas; } diff --git a/src/java/org/apache/cassandra/service/AbstractWriteResponseHandler.java b/src/java/org/apache/cassandra/service/AbstractWriteResponseHandler.java index b5eaadb160..9d800a08bb 100644 --- a/src/java/org/apache/cassandra/service/AbstractWriteResponseHandler.java +++ b/src/java/org/apache/cassandra/service/AbstractWriteResponseHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -35,6 +34,7 @@ import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.WriteType; import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.IAsyncCallbackWithFailure; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.utils.concurrent.SimpleCondition; @@ -47,15 +47,15 @@ public abstract class AbstractWriteResponseHandler implements IAsyncCallbackW private AtomicInteger responsesAndExpirations; private final SimpleCondition condition = new SimpleCondition(); protected final Keyspace keyspace; - protected final Collection naturalEndpoints; + protected final Collection naturalEndpoints; public final ConsistencyLevel consistencyLevel; protected final Runnable callback; - protected final Collection pendingEndpoints; + protected final Collection pendingEndpoints; protected final WriteType writeType; private static final AtomicIntegerFieldUpdater failuresUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractWriteResponseHandler.class, "failures"); private volatile int failures = 0; - private final Map failureReasonByEndpoint; + private final Map failureReasonByEndpoint; private final long queryStartNanoTime; private volatile boolean supportsBackPressure = true; @@ -72,8 +72,8 @@ public abstract class AbstractWriteResponseHandler implements IAsyncCallbackW * @param queryStartNanoTime */ protected AbstractWriteResponseHandler(Keyspace keyspace, - Collection naturalEndpoints, - Collection pendingEndpoints, + Collection naturalEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistencyLevel, Runnable callback, WriteType writeType, @@ -208,7 +208,7 @@ public abstract class AbstractWriteResponseHandler implements IAsyncCallbackW /** * @return true if the message counts towards the totalBlockFor() threshold */ - protected boolean waitingFor(InetAddress from) + protected boolean waitingFor(InetAddressAndPort from) { return true; } @@ -236,7 +236,7 @@ public abstract class AbstractWriteResponseHandler implements IAsyncCallbackW } @Override - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { logger.trace("Got failure from {}", from); diff --git a/src/java/org/apache/cassandra/service/ActiveRepairService.java b/src/java/org/apache/cassandra/service/ActiveRepairService.java index 02762385ef..c600789042 100644 --- a/src/java/org/apache/cassandra/service/ActiveRepairService.java +++ b/src/java/org/apache/cassandra/service/ActiveRepairService.java @@ -19,7 +19,6 @@ package org.apache.cassandra.service; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.concurrent.*; @@ -61,6 +60,7 @@ import org.apache.cassandra.gms.IEndpointStateChangeSubscriber; import org.apache.cassandra.gms.IFailureDetectionEventListener; import org.apache.cassandra.gms.VersionedValue; import org.apache.cassandra.io.sstable.format.SSTableReader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.net.IAsyncCallbackWithFailure; import org.apache.cassandra.net.MessageIn; @@ -208,7 +208,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai Collection> range, String keyspace, RepairParallelism parallelismDegree, - Set endpoints, + Set endpoints, boolean isIncremental, boolean pullRepair, boolean force, @@ -297,12 +297,12 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai * * @return neighbors with whom we share the provided range */ - public static Set getNeighbors(String keyspaceName, Collection> keyspaceLocalRanges, - Range toRepair, Collection dataCenters, - Collection hosts) + public static Set getNeighbors(String keyspaceName, Collection> keyspaceLocalRanges, + Range toRepair, Collection dataCenters, + Collection hosts) { StorageService ss = StorageService.instance; - Map, List> replicaSets = ss.getRangeToAddressMap(keyspaceName); + Map, List> replicaSets = ss.getRangeToAddressMap(keyspaceName); Range rangeSuperSet = null; for (Range range : keyspaceLocalRanges) { @@ -322,17 +322,17 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai if (rangeSuperSet == null || !replicaSets.containsKey(rangeSuperSet)) return Collections.emptySet(); - Set neighbors = new HashSet<>(replicaSets.get(rangeSuperSet)); - neighbors.remove(FBUtilities.getBroadcastAddress()); + Set neighbors = new HashSet<>(replicaSets.get(rangeSuperSet)); + neighbors.remove(FBUtilities.getBroadcastAddressAndPort()); if (dataCenters != null && !dataCenters.isEmpty()) { TokenMetadata.Topology topology = ss.getTokenMetadata().cloneOnlyTokenMap().getTopology(); - Set dcEndpoints = Sets.newHashSet(); - Multimap dcEndpointsMap = topology.getDatacenterEndpoints(); + Set dcEndpoints = Sets.newHashSet(); + Multimap dcEndpointsMap = topology.getDatacenterEndpoints(); for (String dc : dataCenters) { - Collection c = dcEndpointsMap.get(dc); + Collection c = dcEndpointsMap.get(dc); if (c != null) dcEndpoints.addAll(c); } @@ -340,13 +340,13 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai } else if (hosts != null && !hosts.isEmpty()) { - Set specifiedHost = new HashSet<>(); + Set specifiedHost = new HashSet<>(); for (final String host : hosts) { try { - final InetAddress endpoint = InetAddress.getByName(host.trim()); - if (endpoint.equals(FBUtilities.getBroadcastAddress()) || neighbors.contains(endpoint)) + final InetAddressAndPort endpoint = InetAddressAndPort.getByName(host.trim()); + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort()) || neighbors.contains(endpoint)) specifiedHost.add(endpoint); } catch (UnknownHostException e) @@ -355,7 +355,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai } } - if (!specifiedHost.contains(FBUtilities.getBroadcastAddress())) + if (!specifiedHost.contains(FBUtilities.getBroadcastAddressAndPort())) throw new IllegalArgumentException("The current host must be part of the repair"); if (specifiedHost.size() <= 1) @@ -366,7 +366,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai throw new IllegalArgumentException(String.format(msg, hosts, toRepair, neighbors)); } - specifiedHost.remove(FBUtilities.getBroadcastAddress()); + specifiedHost.remove(FBUtilities.getBroadcastAddressAndPort()); return specifiedHost; } @@ -393,7 +393,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai } } - public UUID prepareForRepair(UUID parentRepairSession, InetAddress coordinator, Set endpoints, RepairOption options, List columnFamilyStores) + public UUID prepareForRepair(UUID parentRepairSession, InetAddressAndPort coordinator, Set endpoints, RepairOption options, List columnFamilyStores) { long repairedAt = getRepairedAt(options); registerParentRepairSession(parentRepairSession, coordinator, columnFamilyStores, options.getRanges(), options.isIncremental(), repairedAt, options.isGlobal(), options.getPreviewKind()); @@ -412,10 +412,10 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai return false; } - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { status.set(false); - failedNodes.add(from.getHostAddress()); + failedNodes.add(from.toString()); prepareLatch.countDown(); } }; @@ -424,7 +424,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai for (ColumnFamilyStore cfs : columnFamilyStores) tableIds.add(cfs.metadata.id); - for (InetAddress neighbour : endpoints) + for (InetAddressAndPort neighbour : endpoints) { if (FailureDetector.instance.isAlive(neighbour)) { @@ -471,7 +471,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai throw new RuntimeException(errorMsg); } - public synchronized void registerParentRepairSession(UUID parentRepairSession, InetAddress coordinator, List columnFamilyStores, Collection> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind) + public synchronized void registerParentRepairSession(UUID parentRepairSession, InetAddressAndPort coordinator, List columnFamilyStores, Collection> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind) { assert isIncremental || repairedAt == ActiveRepairService.UNREPAIRED_SSTABLE; if (!registeredForEndpointChanges) @@ -517,7 +517,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai return parentRepairSessions.remove(parentSessionId); } - public void handleMessage(InetAddress endpoint, RepairMessage message) + public void handleMessage(InetAddressAndPort endpoint, RepairMessage message) { RepairJobDesc desc = message.desc; RepairSession session = sessions.get(desc.sessionId); @@ -551,10 +551,10 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai public final boolean isIncremental; public final boolean isGlobal; public final long repairedAt; - public final InetAddress coordinator; + public final InetAddressAndPort coordinator; public final PreviewKind previewKind; - public ParentRepairSession(InetAddress coordinator, List columnFamilyStores, Collection> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind) + public ParentRepairSession(InetAddressAndPort coordinator, List columnFamilyStores, Collection> ranges, boolean isIncremental, long repairedAt, boolean isGlobal, PreviewKind previewKind) { this.coordinator = coordinator; for (ColumnFamilyStore cfs : columnFamilyStores) @@ -636,18 +636,18 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai If the coordinator node dies we should remove the parent repair session from the other nodes. This uses the same notifications as we get in RepairSession */ - public void onJoin(InetAddress endpoint, EndpointState epState) {} - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) {} - public void onAlive(InetAddress endpoint, EndpointState state) {} - public void onDead(InetAddress endpoint, EndpointState state) {} + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) {} + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) {} + public void onAlive(InetAddressAndPort endpoint, EndpointState state) {} + public void onDead(InetAddressAndPort endpoint, EndpointState state) {} - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { convict(endpoint, Double.MAX_VALUE); } - public void onRestart(InetAddress endpoint, EndpointState state) + public void onRestart(InetAddressAndPort endpoint, EndpointState state) { convict(endpoint, Double.MAX_VALUE); } @@ -661,7 +661,7 @@ public class ActiveRepairService implements IEndpointStateChangeSubscriber, IFai * @param ep endpoint to be convicted * @param phi the value of phi with with ep was convicted */ - public void convict(InetAddress ep, double phi) + public void convict(InetAddressAndPort ep, double phi) { // We want a higher confidence in the failure detection than usual because failing a repair wrongly has a high cost. if (phi < 2 * DatabaseDescriptor.getPhiConvictThreshold() || parentRepairSessions.isEmpty()) diff --git a/src/java/org/apache/cassandra/service/BatchlogResponseHandler.java b/src/java/org/apache/cassandra/service/BatchlogResponseHandler.java index 4e261010f0..e373fb6dcb 100644 --- a/src/java/org/apache/cassandra/service/BatchlogResponseHandler.java +++ b/src/java/org/apache/cassandra/service/BatchlogResponseHandler.java @@ -18,12 +18,12 @@ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.exceptions.WriteFailureException; import org.apache.cassandra.exceptions.WriteTimeoutException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; public class BatchlogResponseHandler extends AbstractWriteResponseHandler @@ -59,7 +59,7 @@ public class BatchlogResponseHandler extends AbstractWriteResponseHandler return wrapped.isLatencyForSnitch(); } - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { wrapped.onFailure(from, failureReason); } @@ -84,7 +84,7 @@ public class BatchlogResponseHandler extends AbstractWriteResponseHandler return wrapped.totalEndpoints(); } - protected boolean waitingFor(InetAddress from) + protected boolean waitingFor(InetAddressAndPort from) { return wrapped.waitingFor(from); } diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index 51219e666a..130f3fd763 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -46,6 +46,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.concurrent.ScheduledExecutors; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.Schema; @@ -231,6 +232,8 @@ public class CassandraDaemon } }); + SystemKeyspaceMigrator40.migrate(); + // Populate token metadata before flushing, for token-aware sstable partitioning (#6696) StorageService.instance.populateTokenMetadata(); @@ -377,7 +380,7 @@ public class CassandraDaemon ScheduledExecutors.optionalTasks.schedule(viewRebuild, StorageService.RING_DELAY, TimeUnit.MILLISECONDS); - if (!FBUtilities.getBroadcastAddress().equals(InetAddress.getLoopbackAddress())) + if (!FBUtilities.getBroadcastAddressAndPort().equals(InetAddressAndPort.getLoopbackAddress())) Gossiper.waitToSettle(); // re-enable auto-compaction after gossip is settled, so correct disk boundaries are used @@ -445,7 +448,7 @@ public class CassandraDaemon { try { - logger.info("Hostname: {}", InetAddress.getLocalHost().getHostName()); + logger.info("Hostname: {}", InetAddress.getLocalHost().getHostName() + ":" + DatabaseDescriptor.getStoragePort() + ":" + DatabaseDescriptor.getSSLStoragePort()); } catch (UnknownHostException e1) { diff --git a/src/java/org/apache/cassandra/service/ClientState.java b/src/java/org/apache/cassandra/service/ClientState.java index e41cc4fd60..97b6172fed 100644 --- a/src/java/org/apache/cassandra/service/ClientState.java +++ b/src/java/org/apache/cassandra/service/ClientState.java @@ -60,7 +60,7 @@ public class ClientState { // We want these system cfs to be always readable to authenticated users since many tools rely on them // (nodetool, cqlsh, bulkloader, etc.) - for (String cf : Arrays.asList(SystemKeyspace.LOCAL, SystemKeyspace.PEERS)) + for (String cf : Arrays.asList(SystemKeyspace.LOCAL, SystemKeyspace.LEGACY_PEERS, SystemKeyspace.PEERS_V2)) READABLE_SYSTEM_RESOURCES.add(DataResource.table(SchemaConstants.SYSTEM_KEYSPACE_NAME, cf)); SchemaKeyspace.ALL.forEach(table -> READABLE_SYSTEM_RESOURCES.add(DataResource.table(SchemaConstants.SCHEMA_KEYSPACE_NAME, table))); diff --git a/src/java/org/apache/cassandra/service/DataResolver.java b/src/java/org/apache/cassandra/service/DataResolver.java index 54f4b0c0fd..82db7548a1 100644 --- a/src/java/org/apache/cassandra/service/DataResolver.java +++ b/src/java/org/apache/cassandra/service/DataResolver.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.TimeoutException; @@ -40,6 +39,7 @@ import org.apache.cassandra.dht.AbstractBounds; import org.apache.cassandra.dht.ExcludingBounds; import org.apache.cassandra.dht.Range; import org.apache.cassandra.exceptions.ReadTimeoutException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.*; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.FBUtilities; @@ -87,7 +87,7 @@ public class DataResolver extends ResponseResolver // at the beginning of this method), so grab the response count once and use that through the method. int count = responses.size(); List iters = new ArrayList<>(count); - InetAddress[] sources = new InetAddress[count]; + InetAddressAndPort[] sources = new InetAddressAndPort[count]; for (int i = 0; i < count; i++) { MessageIn msg = responses.get(i); @@ -120,7 +120,7 @@ public class DataResolver extends ResponseResolver } private UnfilteredPartitionIterator mergeWithShortReadProtection(List results, - InetAddress[] sources, + InetAddressAndPort[] sources, DataLimits.Counter mergedResultCounter) { // If we have only one results, there is no read repair to do and we can't get short reads @@ -140,9 +140,9 @@ public class DataResolver extends ResponseResolver private class RepairMergeListener implements UnfilteredPartitionIterators.MergeListener { - private final InetAddress[] sources; + private final InetAddressAndPort[] sources; - private RepairMergeListener(InetAddress[] sources) + private RepairMergeListener(InetAddressAndPort[] sources) { this.sources = sources; } @@ -471,7 +471,7 @@ public class DataResolver extends ResponseResolver sendRepairMutation(repairs[i].build(), sources[i]); } - private void sendRepairMutation(PartitionUpdate partition, InetAddress destination) + private void sendRepairMutation(PartitionUpdate partition, InetAddressAndPort destination) { Mutation mutation = new Mutation(partition); int messagingVersion = MessagingService.instance().getVersion(destination); @@ -514,7 +514,7 @@ public class DataResolver extends ResponseResolver } private UnfilteredPartitionIterator extendWithShortReadProtection(UnfilteredPartitionIterator partitions, - InetAddress source, + InetAddressAndPort source, DataLimits.Counter mergedResultCounter) { DataLimits.Counter singleResultCounter = @@ -557,7 +557,7 @@ public class DataResolver extends ResponseResolver */ private class ShortReadPartitionsProtection extends Transformation implements MorePartitions { - private final InetAddress source; + private final InetAddressAndPort source; private final DataLimits.Counter singleResultCounter; // unmerged per-source counter private final DataLimits.Counter mergedResultCounter; // merged end-result counter @@ -568,7 +568,7 @@ public class DataResolver extends ResponseResolver private final long queryStartNanoTime; - private ShortReadPartitionsProtection(InetAddress source, + private ShortReadPartitionsProtection(InetAddressAndPort source, DataLimits.Counter singleResultCounter, DataLimits.Counter mergedResultCounter, long queryStartNanoTime) diff --git a/src/java/org/apache/cassandra/service/DatacenterSyncWriteResponseHandler.java b/src/java/org/apache/cassandra/service/DatacenterSyncWriteResponseHandler.java index 4137e3a0ef..dbd3667759 100644 --- a/src/java/org/apache/cassandra/service/DatacenterSyncWriteResponseHandler.java +++ b/src/java/org/apache/cassandra/service/DatacenterSyncWriteResponseHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -26,6 +25,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.NetworkTopologyStrategy; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.db.ConsistencyLevel; @@ -41,8 +41,8 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponse private final Map responses = new HashMap(); private final AtomicInteger acks = new AtomicInteger(0); - public DatacenterSyncWriteResponseHandler(Collection naturalEndpoints, - Collection pendingEndpoints, + public DatacenterSyncWriteResponseHandler(Collection naturalEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistencyLevel, Keyspace keyspace, Runnable callback, @@ -63,7 +63,7 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponse // During bootstrap, we have to include the pending endpoints or we may fail the consistency level // guarantees (see #833) - for (InetAddress pending : pendingEndpoints) + for (InetAddressAndPort pending : pendingEndpoints) { responses.get(snitch.getDatacenter(pending)).incrementAndGet(); } diff --git a/src/java/org/apache/cassandra/service/DatacenterWriteResponseHandler.java b/src/java/org/apache/cassandra/service/DatacenterWriteResponseHandler.java index 83dddcfaaa..a8d7b28a00 100644 --- a/src/java/org/apache/cassandra/service/DatacenterWriteResponseHandler.java +++ b/src/java/org/apache/cassandra/service/DatacenterWriteResponseHandler.java @@ -17,10 +17,10 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Collection; import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.WriteType; @@ -30,8 +30,8 @@ import org.apache.cassandra.db.WriteType; */ public class DatacenterWriteResponseHandler extends WriteResponseHandler { - public DatacenterWriteResponseHandler(Collection naturalEndpoints, - Collection pendingEndpoints, + public DatacenterWriteResponseHandler(Collection naturalEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistencyLevel, Keyspace keyspace, Runnable callback, @@ -66,7 +66,7 @@ public class DatacenterWriteResponseHandler extends WriteResponseHandler } @Override - protected boolean waitingFor(InetAddress from) + protected boolean waitingFor(InetAddressAndPort from) { return consistencyLevel.isLocal(from); } diff --git a/src/java/org/apache/cassandra/service/IEndpointLifecycleSubscriber.java b/src/java/org/apache/cassandra/service/IEndpointLifecycleSubscriber.java index 24cb3d780f..bc49d3b027 100644 --- a/src/java/org/apache/cassandra/service/IEndpointLifecycleSubscriber.java +++ b/src/java/org/apache/cassandra/service/IEndpointLifecycleSubscriber.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Interface on which interested parties can be notified of high level endpoint @@ -35,33 +35,33 @@ public interface IEndpointLifecycleSubscriber * * @param endpoint the newly added endpoint. */ - public void onJoinCluster(InetAddress endpoint); + public void onJoinCluster(InetAddressAndPort endpoint); /** * Called when a new node leave the cluster (decommission or removeToken). * * @param endpoint the endpoint that is leaving. */ - public void onLeaveCluster(InetAddress endpoint); + public void onLeaveCluster(InetAddressAndPort endpoint); /** * Called when a node is marked UP. * * @param endpoint the endpoint marked UP. */ - public void onUp(InetAddress endpoint); + public void onUp(InetAddressAndPort endpoint); /** * Called when a node is marked DOWN. * * @param endpoint the endpoint marked DOWN. */ - public void onDown(InetAddress endpoint); + public void onDown(InetAddressAndPort endpoint); /** * Called when a node has moved (to a new token). * * @param endpoint the endpoint that has moved. */ - public void onMove(InetAddress endpoint); + public void onMove(InetAddressAndPort endpoint); } diff --git a/src/java/org/apache/cassandra/service/LoadBroadcaster.java b/src/java/org/apache/cassandra/service/LoadBroadcaster.java index 945dd2fcbb..35c0b626ed 100644 --- a/src/java/org/apache/cassandra/service/LoadBroadcaster.java +++ b/src/java/org/apache/cassandra/service/LoadBroadcaster.java @@ -17,12 +17,12 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.StorageMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,21 +38,21 @@ public class LoadBroadcaster implements IEndpointStateChangeSubscriber private static final Logger logger = LoggerFactory.getLogger(LoadBroadcaster.class); - private ConcurrentMap loadInfo = new ConcurrentHashMap(); + private ConcurrentMap loadInfo = new ConcurrentHashMap<>(); private LoadBroadcaster() { Gossiper.instance.register(this); } - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) { if (state != ApplicationState.LOAD) return; loadInfo.put(endpoint, Double.valueOf(value.value)); } - public void onJoin(InetAddress endpoint, EndpointState epState) + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) { VersionedValue localValue = epState.getApplicationState(ApplicationState.LOAD); if (localValue != null) @@ -61,20 +61,20 @@ public class LoadBroadcaster implements IEndpointStateChangeSubscriber } } - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} - public void onAlive(InetAddress endpoint, EndpointState state) {} + public void onAlive(InetAddressAndPort endpoint, EndpointState state) {} - public void onDead(InetAddress endpoint, EndpointState state) {} + public void onDead(InetAddressAndPort endpoint, EndpointState state) {} - public void onRestart(InetAddress endpoint, EndpointState state) {} + public void onRestart(InetAddressAndPort endpoint, EndpointState state) {} - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { loadInfo.remove(endpoint); } - public Map getLoadInfo() + public Map getLoadInfo() { return Collections.unmodifiableMap(loadInfo); } diff --git a/src/java/org/apache/cassandra/service/ReadCallback.java b/src/java/org/apache/cassandra/service/ReadCallback.java index 7ee6386e4d..e7f30b4794 100644 --- a/src/java/org/apache/cassandra/service/ReadCallback.java +++ b/src/java/org/apache/cassandra/service/ReadCallback.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Collections; import java.util.List; import java.util.Map; @@ -38,6 +37,7 @@ import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.exceptions.ReadFailureException; import org.apache.cassandra.exceptions.ReadTimeoutException; import org.apache.cassandra.exceptions.UnavailableException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.ReadRepairMetrics; import org.apache.cassandra.net.IAsyncCallbackWithFailure; import org.apache.cassandra.net.MessageIn; @@ -56,7 +56,7 @@ public class ReadCallback implements IAsyncCallbackWithFailure final SimpleCondition condition = new SimpleCondition(); private final long queryStartNanoTime; final int blockfor; - final List endpoints; + final List endpoints; private final ReadCommand command; private final ConsistencyLevel consistencyLevel; private static final AtomicIntegerFieldUpdater recievedUpdater @@ -65,14 +65,14 @@ public class ReadCallback implements IAsyncCallbackWithFailure private static final AtomicIntegerFieldUpdater failuresUpdater = AtomicIntegerFieldUpdater.newUpdater(ReadCallback.class, "failures"); private volatile int failures = 0; - private final Map failureReasonByEndpoint; + private final Map failureReasonByEndpoint; private final Keyspace keyspace; // TODO push this into ConsistencyLevel? /** * Constructor when response count has to be calculated and blocked for. */ - public ReadCallback(ResponseResolver resolver, ConsistencyLevel consistencyLevel, ReadCommand command, List filteredEndpoints, long queryStartNanoTime) + public ReadCallback(ResponseResolver resolver, ConsistencyLevel consistencyLevel, ReadCommand command, List filteredEndpoints, long queryStartNanoTime) { this(resolver, consistencyLevel, @@ -83,7 +83,7 @@ public class ReadCallback implements IAsyncCallbackWithFailure queryStartNanoTime); } - public ReadCallback(ResponseResolver resolver, ConsistencyLevel consistencyLevel, int blockfor, ReadCommand command, Keyspace keyspace, List endpoints, long queryStartNanoTime) + public ReadCallback(ResponseResolver resolver, ConsistencyLevel consistencyLevel, int blockfor, ReadCommand command, Keyspace keyspace, List endpoints, long queryStartNanoTime) { this.command = command; this.keyspace = keyspace; @@ -176,7 +176,7 @@ public class ReadCallback implements IAsyncCallbackWithFailure /** * @return true if the message counts towards the blockfor threshold */ - private boolean waitingFor(InetAddress from) + private boolean waitingFor(InetAddressAndPort from) { return consistencyLevel.isDatacenterLocal() ? DatabaseDescriptor.getLocalDataCenter().equals(DatabaseDescriptor.getEndpointSnitch().getDatacenter(from)) @@ -193,9 +193,9 @@ public class ReadCallback implements IAsyncCallbackWithFailure public void response(ReadResponse result) { - MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddress(), + MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddressAndPort(), result, - Collections.emptyMap(), + Collections.emptyMap(), MessagingService.Verb.INTERNAL_RESPONSE, MessagingService.current_version); response(message); @@ -245,14 +245,14 @@ public class ReadCallback implements IAsyncCallbackWithFailure final DataResolver repairResolver = new DataResolver(keyspace, command, consistencyLevel, endpoints.size(), queryStartNanoTime); AsyncRepairCallback repairHandler = new AsyncRepairCallback(repairResolver, endpoints.size()); - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) MessagingService.instance().sendRR(command.createMessage(), endpoint, repairHandler); } } } @Override - public void onFailure(InetAddress from, RequestFailureReason failureReason) + public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason) { int n = waitingFor(from) ? failuresUpdater.incrementAndGet(this) diff --git a/src/java/org/apache/cassandra/service/StartupChecks.java b/src/java/org/apache/cassandra/service/StartupChecks.java index 55099fcc9b..224fd5e7a6 100644 --- a/src/java/org/apache/cassandra/service/StartupChecks.java +++ b/src/java/org/apache/cassandra/service/StartupChecks.java @@ -437,7 +437,7 @@ public class StartupChecks String storedDc = SystemKeyspace.getDatacenter(); if (storedDc != null) { - String currentDc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + String currentDc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); if (!storedDc.equals(currentDc)) { String formatMessage = "Cannot start node if snitch's data center (%s) differs from previous data center (%s). " + @@ -459,7 +459,7 @@ public class StartupChecks String storedRack = SystemKeyspace.getRack(); if (storedRack != null) { - String currentRack = DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddress()); + String currentRack = DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddressAndPort()); if (!storedRack.equals(currentRack)) { String formatMessage = "Cannot start node if snitch's rack (%s) differs from previous rack (%s). " + diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java index be0cf0f1e6..dcf0cab52a 100644 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@ -17,9 +17,7 @@ */ package org.apache.cassandra.service; -import java.io.IOException; import java.lang.management.ManagementFactory; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.file.Paths; import java.util.*; @@ -64,7 +62,6 @@ import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.hints.Hint; import org.apache.cassandra.hints.HintsService; import org.apache.cassandra.index.Index; -import org.apache.cassandra.io.util.DataOutputBuffer; import org.apache.cassandra.locator.*; import org.apache.cassandra.metrics.*; import org.apache.cassandra.net.*; @@ -95,9 +92,9 @@ public class StorageProxy implements StorageProxyMBean public static final StorageProxy instance = new StorageProxy(); private static volatile int maxHintsInProgress = 128 * FBUtilities.getAvailableProcessors(); - private static final CacheLoader hintsInProgress = new CacheLoader() + private static final CacheLoader hintsInProgress = new CacheLoader() { - public AtomicInteger load(InetAddress inetAddress) + public AtomicInteger load(InetAddressAndPort inetAddress) { return new AtomicInteger(0); } @@ -135,7 +132,7 @@ public class StorageProxy implements StorageProxyMBean standardWritePerformer = new WritePerformer() { public void apply(IMutation mutation, - Iterable targets, + Iterable targets, AbstractWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistency_level) @@ -155,7 +152,7 @@ public class StorageProxy implements StorageProxyMBean counterWritePerformer = new WritePerformer() { public void apply(IMutation mutation, - Iterable targets, + Iterable targets, AbstractWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistencyLevel) @@ -167,7 +164,7 @@ public class StorageProxy implements StorageProxyMBean counterWriteOnCoordinatorPerformer = new WritePerformer() { public void apply(IMutation mutation, - Iterable targets, + Iterable targets, AbstractWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistencyLevel) @@ -248,8 +245,8 @@ public class StorageProxy implements StorageProxyMBean while (System.nanoTime() - queryStartNanoTime < timeout) { // for simplicity, we'll do a single liveness check at the start of each attempt - Pair, Integer> p = getPaxosParticipants(metadata, key, consistencyForPaxos); - List liveEndpoints = p.left; + Pair, Integer> p = getPaxosParticipants(metadata, key, consistencyForPaxos); + List liveEndpoints = p.left; int requiredParticipants = p.right; final Pair pair = beginAndRepairPaxos(queryStartNanoTime, key, metadata, liveEndpoints, requiredParticipants, consistencyForPaxos, consistencyForCommit, true, state); @@ -342,34 +339,34 @@ public class StorageProxy implements StorageProxyMBean casWriteMetrics.contention.update(contentions); } - private static Predicate sameDCPredicateFor(final String dc) + private static Predicate sameDCPredicateFor(final String dc) { final IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); - return new Predicate() + return new Predicate() { - public boolean apply(InetAddress host) + public boolean apply(InetAddressAndPort host) { return dc.equals(snitch.getDatacenter(host)); } }; } - private static Pair, Integer> getPaxosParticipants(TableMetadata metadata, DecoratedKey key, ConsistencyLevel consistencyForPaxos) throws UnavailableException + private static Pair, Integer> getPaxosParticipants(TableMetadata metadata, DecoratedKey key, ConsistencyLevel consistencyForPaxos) throws UnavailableException { Token tk = key.getToken(); - List naturalEndpoints = StorageService.instance.getNaturalEndpoints(metadata.keyspace, tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, metadata.keyspace); + List naturalEndpoints = StorageService.instance.getNaturalEndpoints(metadata.keyspace, tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, metadata.keyspace); if (consistencyForPaxos == ConsistencyLevel.LOCAL_SERIAL) { // Restrict naturalEndpoints and pendingEndpoints to node in the local DC only - String localDc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); - Predicate isLocalDc = sameDCPredicateFor(localDc); + String localDc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); + Predicate isLocalDc = sameDCPredicateFor(localDc); naturalEndpoints = ImmutableList.copyOf(Iterables.filter(naturalEndpoints, isLocalDc)); pendingEndpoints = ImmutableList.copyOf(Iterables.filter(pendingEndpoints, isLocalDc)); } int participants = pendingEndpoints.size() + naturalEndpoints.size(); int requiredParticipants = participants / 2 + 1; // See CASSANDRA-8346, CASSANDRA-833 - List liveEndpoints = ImmutableList.copyOf(Iterables.filter(Iterables.concat(naturalEndpoints, pendingEndpoints), IAsyncCallback.isAlive)); + List liveEndpoints = ImmutableList.copyOf(Iterables.filter(Iterables.concat(naturalEndpoints, pendingEndpoints), IAsyncCallback.isAlive)); if (liveEndpoints.size() < requiredParticipants) throw new UnavailableException(consistencyForPaxos, requiredParticipants, liveEndpoints.size()); @@ -394,7 +391,7 @@ public class StorageProxy implements StorageProxyMBean private static Pair beginAndRepairPaxos(long queryStartNanoTime, DecoratedKey key, TableMetadata metadata, - List liveEndpoints, + List liveEndpoints, int requiredParticipants, ConsistencyLevel consistencyForPaxos, ConsistencyLevel consistencyForCommit, @@ -472,7 +469,7 @@ public class StorageProxy implements StorageProxyMBean // Since we waited for quorum nodes, if some of them haven't seen the last commit (which may just be a timing issue, but may also // mean we lost messages), we pro-actively "repair" those nodes, and retry. int nowInSec = Ints.checkedCast(TimeUnit.MICROSECONDS.toSeconds(ballotMicros)); - Iterable missingMRC = summary.replicasMissingMostRecentCommit(metadata, nowInSec); + Iterable missingMRC = summary.replicasMissingMostRecentCommit(metadata, nowInSec); if (Iterables.size(missingMRC) > 0) { Tracing.trace("Repairing replicas that missed the most recent commit"); @@ -494,19 +491,19 @@ public class StorageProxy implements StorageProxyMBean /** * Unlike commitPaxos, this does not wait for replies */ - private static void sendCommit(Commit commit, Iterable replicas) + private static void sendCommit(Commit commit, Iterable replicas) { MessageOut message = new MessageOut(MessagingService.Verb.PAXOS_COMMIT, commit, Commit.serializer); - for (InetAddress target : replicas) + for (InetAddressAndPort target : replicas) MessagingService.instance().sendOneWay(message, target); } - private static PrepareCallback preparePaxos(Commit toPrepare, List endpoints, int requiredParticipants, ConsistencyLevel consistencyForPaxos, long queryStartNanoTime) + private static PrepareCallback preparePaxos(Commit toPrepare, List endpoints, int requiredParticipants, ConsistencyLevel consistencyForPaxos, long queryStartNanoTime) throws WriteTimeoutException { PrepareCallback callback = new PrepareCallback(toPrepare.update.partitionKey(), toPrepare.update.metadata(), requiredParticipants, consistencyForPaxos, queryStartNanoTime); MessageOut message = new MessageOut(MessagingService.Verb.PAXOS_PREPARE, toPrepare, Commit.serializer); - for (InetAddress target : endpoints) + for (InetAddressAndPort target : endpoints) { if (canDoLocalRequest(target)) { @@ -516,9 +513,9 @@ public class StorageProxy implements StorageProxyMBean { try { - MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddress(), + MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddressAndPort(), PrepareVerbHandler.doPrepare(toPrepare), - Collections.emptyMap(), + Collections.emptyMap(), MessagingService.Verb.INTERNAL_RESPONSE, MessagingService.current_version); callback.response(message); @@ -539,12 +536,12 @@ public class StorageProxy implements StorageProxyMBean return callback; } - private static boolean proposePaxos(Commit proposal, List endpoints, int requiredParticipants, boolean timeoutIfPartial, ConsistencyLevel consistencyLevel, long queryStartNanoTime) + private static boolean proposePaxos(Commit proposal, List endpoints, int requiredParticipants, boolean timeoutIfPartial, ConsistencyLevel consistencyLevel, long queryStartNanoTime) throws WriteTimeoutException { ProposeCallback callback = new ProposeCallback(endpoints.size(), requiredParticipants, !timeoutIfPartial, consistencyLevel, queryStartNanoTime); MessageOut message = new MessageOut(MessagingService.Verb.PAXOS_PROPOSE, proposal, Commit.serializer); - for (InetAddress target : endpoints) + for (InetAddressAndPort target : endpoints) { if (canDoLocalRequest(target)) { @@ -554,9 +551,9 @@ public class StorageProxy implements StorageProxyMBean { try { - MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddress(), + MessageIn message = MessageIn.create(FBUtilities.getBroadcastAddressAndPort(), ProposeVerbHandler.doPropose(proposal), - Collections.emptyMap(), + Collections.emptyMap(), MessagingService.Verb.INTERNAL_RESPONSE, MessagingService.current_version); callback.response(message); @@ -590,8 +587,8 @@ public class StorageProxy implements StorageProxyMBean Keyspace keyspace = Keyspace.open(proposal.update.metadata().keyspace); Token tk = proposal.update.partitionKey().getToken(); - List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspace.getName(), tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspace.getName()); + List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspace.getName(), tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspace.getName()); AbstractWriteResponseHandler responseHandler = null; if (shouldBlock) @@ -602,7 +599,7 @@ public class StorageProxy implements StorageProxyMBean } MessageOut message = new MessageOut(MessagingService.Verb.PAXOS_COMMIT, proposal, Commit.serializer); - for (InetAddress destination : Iterables.concat(naturalEndpoints, pendingEndpoints)) + for (InetAddressAndPort destination : Iterables.concat(naturalEndpoints, pendingEndpoints)) { checkHintOverload(destination); @@ -658,7 +655,7 @@ public class StorageProxy implements StorageProxyMBean { if (!(ex instanceof WriteTimeoutException)) logger.error("Failed to apply paxos commit locally : ", ex); - responseHandler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.UNKNOWN); + responseHandler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN); } } @@ -684,7 +681,7 @@ public class StorageProxy implements StorageProxyMBean throws UnavailableException, OverloadedException, WriteTimeoutException, WriteFailureException { Tracing.trace("Determining replicas for mutation"); - final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); long startTime = System.nanoTime(); List> responseHandlers = new ArrayList<>(mutations.size()); @@ -781,13 +778,13 @@ public class StorageProxy implements StorageProxyMBean String keyspaceName = mutation.getKeyspaceName(); Token token = mutation.key().getToken(); - Iterable endpoints = StorageService.instance.getNaturalAndPendingEndpoints(keyspaceName, token); - ArrayList endpointsToHint = new ArrayList<>(Iterables.size(endpoints)); + Iterable endpoints = StorageService.instance.getNaturalAndPendingEndpoints(keyspaceName, token); + ArrayList endpointsToHint = new ArrayList<>(Iterables.size(endpoints)); // local writes can timeout, but cannot be dropped (see LocalMutationRunnable and CASSANDRA-6510), // so there is no need to hint or retry. - for (InetAddress target : endpoints) - if (!target.equals(FBUtilities.getBroadcastAddress()) && shouldHint(target)) + for (InetAddressAndPort target : endpoints) + if (!target.equals(FBUtilities.getBroadcastAddressAndPort()) && shouldHint(target)) endpointsToHint.add(target); submitHint(mutation, endpointsToHint, null); @@ -797,7 +794,7 @@ public class StorageProxy implements StorageProxyMBean { String keyspaceName = mutation.getKeyspaceName(); Token token = mutation.key().getToken(); - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); return StorageService.instance.getNaturalEndpoints(keyspaceName, token).contains(local) || StorageService.instance.getTokenMetadata().pendingEndpointsFor(token, keyspaceName).contains(local); @@ -816,7 +813,7 @@ public class StorageProxy implements StorageProxyMBean throws UnavailableException, OverloadedException, WriteTimeoutException { Tracing.trace("Determining replicas for mutation"); - final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); long startTime = System.nanoTime(); @@ -841,7 +838,7 @@ public class StorageProxy implements StorageProxyMBean ConsistencyLevel consistencyLevel = ConsistencyLevel.ONE; //Since the base -> view replication is 1:1 we only need to store the BL locally - final Collection batchlogEndpoints = Collections.singleton(FBUtilities.getBroadcastAddress()); + final Collection batchlogEndpoints = Collections.singleton(FBUtilities.getBroadcastAddressAndPort()); BatchlogResponseHandler.BatchlogCleanup cleanup = new BatchlogResponseHandler.BatchlogCleanup(mutations.size(), () -> asyncRemoveFromBatchlog(batchlogEndpoints, batchUUID)); // add a handler for each mutation - includes checking availability, but doesn't initiate any writes, yet @@ -849,8 +846,8 @@ public class StorageProxy implements StorageProxyMBean { String keyspaceName = mutation.getKeyspaceName(); Token tk = mutation.key().getToken(); - Optional pairedEndpoint = ViewUtils.getViewNaturalEndpoint(keyspaceName, baseToken, tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); + Optional pairedEndpoint = ViewUtils.getViewNaturalEndpoint(keyspaceName, baseToken, tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); // if there are no paired endpoints there are probably range movements going on, so we write to the local batchlog to replay later if (!pairedEndpoint.isPresent()) @@ -865,7 +862,7 @@ public class StorageProxy implements StorageProxyMBean } // When local node is the paired endpoint just apply the mutation locally. - if (pairedEndpoint.get().equals(FBUtilities.getBroadcastAddress()) && StorageService.instance.isJoined()) + if (pairedEndpoint.get().equals(FBUtilities.getBroadcastAddressAndPort()) && StorageService.instance.isJoined()) { try { @@ -956,7 +953,7 @@ public class StorageProxy implements StorageProxyMBean long startTime = System.nanoTime(); List wrappers = new ArrayList(mutations.size()); - String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); try { @@ -974,7 +971,7 @@ public class StorageProxy implements StorageProxyMBean batchConsistencyLevel = consistency_level; } - final Collection batchlogEndpoints = getBatchlogEndpoints(localDataCenter, batchConsistencyLevel); + final Collection batchlogEndpoints = getBatchlogEndpoints(localDataCenter, batchConsistencyLevel); final UUID batchUUID = UUIDGen.getTimeUUID(); BatchlogResponseHandler.BatchlogCleanup cleanup = new BatchlogResponseHandler.BatchlogCleanup(mutations.size(), () -> asyncRemoveFromBatchlog(batchlogEndpoints, batchUUID)); @@ -1029,16 +1026,16 @@ public class StorageProxy implements StorageProxyMBean } } - public static boolean canDoLocalRequest(InetAddress replica) + public static boolean canDoLocalRequest(InetAddressAndPort replica) { - return replica.equals(FBUtilities.getBroadcastAddress()); + return replica.equals(FBUtilities.getBroadcastAddressAndPort()); } - private static void syncWriteToBatchlog(Collection mutations, Collection endpoints, UUID uuid, long queryStartNanoTime) + private static void syncWriteToBatchlog(Collection mutations, Collection endpoints, UUID uuid, long queryStartNanoTime) throws WriteTimeoutException, WriteFailureException { WriteResponseHandler handler = new WriteResponseHandler<>(endpoints, - Collections.emptyList(), + Collections.emptyList(), endpoints.size() == 1 ? ConsistencyLevel.ONE : ConsistencyLevel.TWO, Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME), null, @@ -1047,7 +1044,7 @@ public class StorageProxy implements StorageProxyMBean Batch batch = Batch.createLocal(uuid, FBUtilities.timestampMicros(), mutations); MessageOut message = new MessageOut<>(MessagingService.Verb.BATCH_STORE, batch, Batch.serializer); - for (InetAddress target : endpoints) + for (InetAddressAndPort target : endpoints) { logger.trace("Sending batchlog store request {} to {} for {} mutations", batch.id, target, batch.size()); @@ -1059,10 +1056,10 @@ public class StorageProxy implements StorageProxyMBean handler.get(); } - private static void asyncRemoveFromBatchlog(Collection endpoints, UUID uuid) + private static void asyncRemoveFromBatchlog(Collection endpoints, UUID uuid) { MessageOut message = new MessageOut<>(MessagingService.Verb.BATCH_REMOVE, uuid, UUIDSerializer.serializer); - for (InetAddress target : endpoints) + for (InetAddressAndPort target : endpoints) { if (logger.isTraceEnabled()) logger.trace("Sending batchlog remove request {} to {}", uuid, target); @@ -1078,7 +1075,7 @@ public class StorageProxy implements StorageProxyMBean { for (WriteResponseHandlerWrapper wrapper : wrappers) { - Iterable endpoints = Iterables.concat(wrapper.handler.naturalEndpoints, wrapper.handler.pendingEndpoints); + Iterable endpoints = Iterables.concat(wrapper.handler.naturalEndpoints, wrapper.handler.pendingEndpoints); try { @@ -1086,7 +1083,7 @@ public class StorageProxy implements StorageProxyMBean } catch (OverloadedException | WriteTimeoutException e) { - wrapper.handler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.UNKNOWN); + wrapper.handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN); } } } @@ -1096,7 +1093,7 @@ public class StorageProxy implements StorageProxyMBean { for (WriteResponseHandlerWrapper wrapper : wrappers) { - Iterable endpoints = Iterables.concat(wrapper.handler.naturalEndpoints, wrapper.handler.pendingEndpoints); + Iterable endpoints = Iterables.concat(wrapper.handler.naturalEndpoints, wrapper.handler.pendingEndpoints); sendToHintedEndpoints(wrapper.mutation, endpoints, wrapper.handler, localDataCenter, stage); } @@ -1132,8 +1129,8 @@ public class StorageProxy implements StorageProxyMBean AbstractReplicationStrategy rs = Keyspace.open(keyspaceName).getReplicationStrategy(); Token tk = mutation.key().getToken(); - List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); + List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); AbstractWriteResponseHandler responseHandler = rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, callback, writeType, queryStartNanoTime); @@ -1156,8 +1153,8 @@ public class StorageProxy implements StorageProxyMBean AbstractReplicationStrategy rs = keyspace.getReplicationStrategy(); String keyspaceName = mutation.getKeyspaceName(); Token tk = mutation.key().getToken(); - List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); + List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); AbstractWriteResponseHandler writeHandler = rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, null, writeType, queryStartNanoTime); BatchlogResponseHandler batchHandler = new BatchlogResponseHandler<>(writeHandler, batchConsistencyLevel.blockFor(keyspace), cleanup, queryStartNanoTime); return new WriteResponseHandlerWrapper(batchHandler, mutation); @@ -1170,7 +1167,7 @@ public class StorageProxy implements StorageProxyMBean private static WriteResponseHandlerWrapper wrapViewBatchResponseHandler(Mutation mutation, ConsistencyLevel consistency_level, ConsistencyLevel batchConsistencyLevel, - List naturalEndpoints, + List naturalEndpoints, AtomicLong baseComplete, WriteType writeType, BatchlogResponseHandler.BatchlogCleanup cleanup, @@ -1180,7 +1177,7 @@ public class StorageProxy implements StorageProxyMBean AbstractReplicationStrategy rs = keyspace.getReplicationStrategy(); String keyspaceName = mutation.getKeyspaceName(); Token tk = mutation.key().getToken(); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); AbstractWriteResponseHandler writeHandler = rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, consistency_level, () -> { long delay = Math.max(0, System.currentTimeMillis() - baseComplete.get()); viewWriteMetrics.viewWriteLatency.update(delay, TimeUnit.MILLISECONDS); @@ -1209,18 +1206,18 @@ public class StorageProxy implements StorageProxyMBean * - choose min(2, number of qualifying candiates above) * - allow the local node to be the only replica only if it's a single-node DC */ - private static Collection getBatchlogEndpoints(String localDataCenter, ConsistencyLevel consistencyLevel) + private static Collection getBatchlogEndpoints(String localDataCenter, ConsistencyLevel consistencyLevel) throws UnavailableException { TokenMetadata.Topology topology = StorageService.instance.getTokenMetadata().cachedOnlyTokenMap().getTopology(); - Multimap localEndpoints = HashMultimap.create(topology.getDatacenterRacks().get(localDataCenter)); - String localRack = DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddress()); + Multimap localEndpoints = HashMultimap.create(topology.getDatacenterRacks().get(localDataCenter)); + String localRack = DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddressAndPort()); - Collection chosenEndpoints = new BatchlogManager.EndpointFilter(localRack, localEndpoints).filter(); + Collection chosenEndpoints = new BatchlogManager.EndpointFilter(localRack, localEndpoints).filter(); if (chosenEndpoints.isEmpty()) { if (consistencyLevel == ConsistencyLevel.ANY) - return Collections.singleton(FBUtilities.getBroadcastAddress()); + return Collections.singleton(FBUtilities.getBroadcastAddressAndPort()); throw new UnavailableException(ConsistencyLevel.ONE, 1, 0); } @@ -1246,7 +1243,7 @@ public class StorageProxy implements StorageProxyMBean * @throws OverloadedException if the hints cannot be written/enqueued */ public static void sendToHintedEndpoints(final Mutation mutation, - Iterable targets, + Iterable targets, AbstractWriteResponseHandler responseHandler, String localDataCenter, Stage stage) @@ -1255,18 +1252,18 @@ public class StorageProxy implements StorageProxyMBean int targetsSize = Iterables.size(targets); // this dc replicas: - Collection localDc = null; + Collection localDc = null; // extra-datacenter replicas, grouped by dc - Map> dcGroups = null; + Map> dcGroups = null; // only need to create a Message for non-local writes MessageOut message = null; boolean insertLocal = false; - ArrayList endpointsToHint = null; + ArrayList endpointsToHint = null; - List backPressureHosts = null; + List backPressureHosts = null; - for (InetAddress destination : targets) + for (InetAddressAndPort destination : targets) { checkHintOverload(destination); @@ -1295,7 +1292,7 @@ public class StorageProxy implements StorageProxyMBean } else { - Collection messages = (dcGroups != null) ? dcGroups.get(dc) : null; + Collection messages = (dcGroups != null) ? dcGroups.get(dc) : null; if (messages == null) { messages = new ArrayList<>(3); // most DCs will have <= 3 replicas @@ -1338,18 +1335,18 @@ public class StorageProxy implements StorageProxyMBean if (localDc != null) { - for (InetAddress destination : localDc) + for (InetAddressAndPort destination : localDc) MessagingService.instance().sendRR(message, destination, responseHandler, true); } if (dcGroups != null) { // for each datacenter, send the message to one node to relay the write to other replicas - for (Collection dcTargets : dcGroups.values()) + for (Collection dcTargets : dcGroups.values()) sendMessagesToNonlocalDC(message, dcTargets, responseHandler); } } - private static void checkHintOverload(InetAddress destination) + private static void checkHintOverload(InetAddressAndPort destination) { // avoid OOMing due to excess hints. we need to do this check even for "live" nodes, since we can // still generate hints for those if it's overloaded or simply dead but not yet known-to-be-dead. @@ -1366,39 +1363,31 @@ public class StorageProxy implements StorageProxyMBean } private static void sendMessagesToNonlocalDC(MessageOut message, - Collection targets, + Collection targets, AbstractWriteResponseHandler handler) { - Iterator iter = targets.iterator(); - InetAddress target = iter.next(); + Iterator iter = targets.iterator(); + int[] messageIds = new int[targets.size()]; + InetAddressAndPort target = iter.next(); + int idIdx = 0; // Add the other destinations of the same message as a FORWARD_HEADER entry - try(DataOutputBuffer out = new DataOutputBuffer()) + while (iter.hasNext()) { - out.writeInt(targets.size() - 1); - while (iter.hasNext()) - { - InetAddress destination = iter.next(); - CompactEndpointSerializationHelper.serialize(destination, out); - int id = MessagingService.instance().addCallback(handler, - message, - destination, - message.getTimeout(), - handler.consistencyLevel, - true); - out.writeInt(id); - logger.trace("Adding FWD message to {}@{}", id, destination); - } - message = message.withParameter(Mutation.FORWARD_TO, out.getData()); - // send the combined message + forward headers - int id = MessagingService.instance().sendRR(message, target, handler, true); - logger.trace("Sending message to {}@{}", id, target); - } - catch (IOException e) - { - // DataOutputBuffer is in-memory, doesn't throw IOException - throw new AssertionError(e); + InetAddressAndPort destination = iter.next(); + int id = MessagingService.instance().addCallback(handler, + message, + destination, + message.getTimeout(), + handler.consistencyLevel, + true); + messageIds[idIdx++] = id; + logger.trace("Adding FWD message to {}@{}", id, destination); } + message = message.withParameter(ParameterType.FORWARD_TO.FORWARD_TO, new ForwardToContainer(targets, messageIds)); + // send the combined message + forward headers + int id = MessagingService.instance().sendRR(message, target, handler, true); + logger.trace("Sending message to {}@{}", id, target); } private static void performLocally(Stage stage, final Runnable runnable) @@ -1440,7 +1429,7 @@ public class StorageProxy implements StorageProxyMBean { if (!(ex instanceof WriteTimeoutException)) logger.error("Failed to apply mutation locally : ", ex); - handler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.UNKNOWN); + handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN); } } @@ -1468,9 +1457,9 @@ public class StorageProxy implements StorageProxyMBean */ public static AbstractWriteResponseHandler mutateCounter(CounterMutation cm, String localDataCenter, long queryStartNanoTime) throws UnavailableException, OverloadedException { - InetAddress endpoint = findSuitableEndpoint(cm.getKeyspaceName(), cm.key(), localDataCenter, cm.consistency()); + InetAddressAndPort endpoint = findSuitableEndpoint(cm.getKeyspaceName(), cm.key(), localDataCenter, cm.consistency()); - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) { return applyCounterMutationOnCoordinator(cm, localDataCenter, queryStartNanoTime); } @@ -1480,8 +1469,8 @@ public class StorageProxy implements StorageProxyMBean String keyspaceName = cm.getKeyspaceName(); AbstractReplicationStrategy rs = Keyspace.open(keyspaceName).getReplicationStrategy(); Token tk = cm.key().getToken(); - List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); - Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); + List naturalEndpoints = StorageService.instance.getNaturalEndpoints(keyspaceName, tk); + Collection pendingEndpoints = StorageService.instance.getTokenMetadata().pendingEndpointsFor(tk, keyspaceName); rs.getWriteResponseHandler(naturalEndpoints, pendingEndpoints, cm.consistency(), null, WriteType.COUNTER, queryStartNanoTime).assureSufficientLiveNodes(); @@ -1504,11 +1493,11 @@ public class StorageProxy implements StorageProxyMBean * is unclear we want to mix those latencies with read latencies, so this * may be a bit involved. */ - private static InetAddress findSuitableEndpoint(String keyspaceName, DecoratedKey key, String localDataCenter, ConsistencyLevel cl) throws UnavailableException + private static InetAddressAndPort findSuitableEndpoint(String keyspaceName, DecoratedKey key, String localDataCenter, ConsistencyLevel cl) throws UnavailableException { Keyspace keyspace = Keyspace.open(keyspaceName); IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); - List endpoints = new ArrayList<>(); + List endpoints = new ArrayList<>(); StorageService.instance.getLiveNaturalEndpoints(keyspace, key, endpoints); // CASSANDRA-13043: filter out those endpoints not accepting clients yet, maybe because still bootstrapping @@ -1518,9 +1507,9 @@ public class StorageProxy implements StorageProxyMBean if (endpoints.isEmpty()) throw new UnavailableException(cl, cl.blockFor(keyspace), 0); - List localEndpoints = new ArrayList<>(endpoints.size()); + List localEndpoints = new ArrayList<>(endpoints.size()); - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) if (snitch.getDatacenter(endpoint).equals(localDataCenter)) localEndpoints.add(endpoint); @@ -1531,7 +1520,7 @@ public class StorageProxy implements StorageProxyMBean throw new UnavailableException(cl, cl.blockFor(keyspace), 0); // No endpoint in local DC, pick the closest endpoint according to the snitch - snitch.sortByProximity(FBUtilities.getBroadcastAddress(), endpoints); + snitch.sortByProximity(FBUtilities.getBroadcastAddressAndPort(), endpoints); return endpoints.get(0); } @@ -1555,7 +1544,7 @@ public class StorageProxy implements StorageProxyMBean } private static Runnable counterWriteTask(final IMutation mutation, - final Iterable targets, + final Iterable targets, final AbstractWriteResponseHandler responseHandler, final String localDataCenter) { @@ -1569,8 +1558,8 @@ public class StorageProxy implements StorageProxyMBean Mutation result = ((CounterMutation) mutation).applyCounterMutation(); responseHandler.response(null); - Set remotes = Sets.difference(ImmutableSet.copyOf(targets), - ImmutableSet.of(FBUtilities.getBroadcastAddress())); + Set remotes = Sets.difference(ImmutableSet.copyOf(targets), + ImmutableSet.of(FBUtilities.getBroadcastAddressAndPort())); if (!remotes.isEmpty()) sendToHintedEndpoints(result, remotes, responseHandler, localDataCenter, Stage.COUNTER_MUTATION); } @@ -1640,8 +1629,8 @@ public class StorageProxy implements StorageProxyMBean try { // make sure any in-progress paxos writes are done (i.e., committed to a majority of replicas), before performing a quorum read - Pair, Integer> p = getPaxosParticipants(metadata, key, consistencyLevel); - List liveEndpoints = p.left; + Pair, Integer> p = getPaxosParticipants(metadata, key, consistencyLevel); + List liveEndpoints = p.left; int requiredParticipants = p.right; // does the work of applying in-progress writes; throws UAE or timeout if it can't @@ -1844,7 +1833,7 @@ public class StorageProxy implements StorageProxyMBean executor.handler.endpoints, queryStartNanoTime); - for (InetAddress endpoint : executor.getContactedReplicas()) + for (InetAddressAndPort endpoint : executor.getContactedReplicas()) { Tracing.trace("Enqueuing full data read to {}", endpoint); MessagingService.instance().sendRRWithFailure(command.createMessage(), endpoint, repairHandler); @@ -1920,47 +1909,47 @@ public class StorageProxy implements StorageProxyMBean else { MessagingService.instance().incrementDroppedMessages(verb, System.currentTimeMillis() - constructionTime); - handler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.UNKNOWN); + handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN); } - MessagingService.instance().addLatency(FBUtilities.getBroadcastAddress(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); + MessagingService.instance().addLatency(FBUtilities.getBroadcastAddressAndPort(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); } catch (Throwable t) { if (t instanceof TombstoneOverwhelmingException) { - handler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); + handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); logger.error(t.getMessage()); } else { - handler.onFailure(FBUtilities.getBroadcastAddress(), RequestFailureReason.UNKNOWN); + handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN); throw t; } } } } - public static List getLiveSortedEndpoints(Keyspace keyspace, ByteBuffer key) + public static List getLiveSortedEndpoints(Keyspace keyspace, ByteBuffer key) { return getLiveSortedEndpoints(keyspace, StorageService.instance.getTokenMetadata().decorateKey(key)); } - public static List getLiveSortedEndpoints(Keyspace keyspace, RingPosition pos) + public static List getLiveSortedEndpoints(Keyspace keyspace, RingPosition pos) { - List liveEndpoints = StorageService.instance.getLiveNaturalEndpoints(keyspace, pos); - DatabaseDescriptor.getEndpointSnitch().sortByProximity(FBUtilities.getBroadcastAddress(), liveEndpoints); + List liveEndpoints = StorageService.instance.getLiveNaturalEndpoints(keyspace, pos); + DatabaseDescriptor.getEndpointSnitch().sortByProximity(FBUtilities.getBroadcastAddressAndPort(), liveEndpoints); return liveEndpoints; } - private static List intersection(List l1, List l2) + private static List intersection(List l1, List l2) { // Note: we don't use Guava Sets.intersection() for 3 reasons: // 1) retainAll would be inefficient if l1 and l2 are large but in practice both are the replicas for a range and // so will be very small (< RF). In that case, retainAll is in fact more efficient. // 2) we do ultimately need a list so converting everything to sets don't make sense // 3) l1 and l2 are sorted by proximity. The use of retainAll maintain that sorting in the result, while using sets wouldn't. - List inter = new ArrayList(l1); + List inter = new ArrayList<>(l1); inter.retainAll(l2); return inter; } @@ -1986,10 +1975,10 @@ public class StorageProxy implements StorageProxyMBean private static class RangeForQuery { public final AbstractBounds range; - public final List liveEndpoints; - public final List filteredEndpoints; + public final List liveEndpoints; + public final List filteredEndpoints; - public RangeForQuery(AbstractBounds range, List liveEndpoints, List filteredEndpoints) + public RangeForQuery(AbstractBounds range, List liveEndpoints, List filteredEndpoints) { this.range = range; this.liveEndpoints = liveEndpoints; @@ -2027,7 +2016,7 @@ public class StorageProxy implements StorageProxyMBean return endOfData(); AbstractBounds range = ranges.next(); - List liveEndpoints = getLiveSortedEndpoints(keyspace, range.right); + List liveEndpoints = getLiveSortedEndpoints(keyspace, range.right); return new RangeForQuery(range, liveEndpoints, consistency.filterForQuery(keyspace, liveEndpoints)); @@ -2069,13 +2058,13 @@ public class StorageProxy implements StorageProxyMBean RangeForQuery next = ranges.peek(); - List merged = intersection(current.liveEndpoints, next.liveEndpoints); + List merged = intersection(current.liveEndpoints, next.liveEndpoints); // Check if there is enough endpoint for the merge to be possible. if (!consistency.isSufficientLiveNodes(keyspace, merged)) break; - List filteredMerged = consistency.filterForQuery(keyspace, merged); + List filteredMerged = consistency.filterForQuery(keyspace, merged); // Estimate whether merging will be a win or not if (!DatabaseDescriptor.getEndpointSnitch().isWorthMergingForRangeQuery(filteredMerged, current.filteredEndpoints, next.filteredEndpoints)) @@ -2237,7 +2226,7 @@ public class StorageProxy implements StorageProxyMBean int blockFor = consistency.blockFor(keyspace); int minResponses = Math.min(toQuery.filteredEndpoints.size(), blockFor); - List minimalEndpoints = toQuery.filteredEndpoints.subList(0, minResponses); + List minimalEndpoints = toQuery.filteredEndpoints.subList(0, minResponses); ReadCallback handler = new ReadCallback(resolver, consistency, rangeCommand, minimalEndpoints, queryStartNanoTime); handler.assureSufficientLiveNodes(); @@ -2248,7 +2237,7 @@ public class StorageProxy implements StorageProxyMBean } else { - for (InetAddress endpoint : toQuery.filteredEndpoints) + for (InetAddressAndPort endpoint : toQuery.filteredEndpoints) { Tracing.trace("Enqueuing request to {}", endpoint); MessagingService.instance().sendRRWithFailure(rangeCommand.createMessage(), endpoint, handler); @@ -2320,7 +2309,12 @@ public class StorageProxy implements StorageProxyMBean public Map> getSchemaVersions() { - return describeSchemaVersions(); + return describeSchemaVersions(false); + } + + public Map> getSchemaVersionsWithPort() + { + return describeSchemaVersions(true); } /** @@ -2328,11 +2322,11 @@ public class StorageProxy implements StorageProxyMBean * migration id. This is useful for determining if a schema change has propagated through the cluster. Disagreement * is assumed if any node fails to respond. */ - public static Map> describeSchemaVersions() + public static Map> describeSchemaVersions(boolean withPort) { final String myVersion = Schema.instance.getVersion().toString(); - final Map versions = new ConcurrentHashMap(); - final Set liveHosts = Gossiper.instance.getLiveMembers(); + final Map versions = new ConcurrentHashMap<>(); + final Set liveHosts = Gossiper.instance.getLiveMembers(); final CountDownLatch latch = new CountDownLatch(liveHosts.size()); IAsyncCallback cb = new IAsyncCallback() @@ -2351,7 +2345,7 @@ public class StorageProxy implements StorageProxyMBean }; // an empty message acts as a request to the SchemaVersionVerbHandler. MessageOut message = new MessageOut(MessagingService.Verb.SCHEMA_CHECK); - for (InetAddress endpoint : liveHosts) + for (InetAddressAndPort endpoint : liveHosts) MessagingService.instance().sendRR(message, endpoint, cb); try @@ -2366,8 +2360,8 @@ public class StorageProxy implements StorageProxyMBean // maps versions to hosts that are on that version. Map> results = new HashMap>(); - Iterable allHosts = Iterables.concat(Gossiper.instance.getLiveMembers(), Gossiper.instance.getUnreachableMembers()); - for (InetAddress host : allHosts) + Iterable allHosts = Iterables.concat(Gossiper.instance.getLiveMembers(), Gossiper.instance.getUnreachableMembers()); + for (InetAddressAndPort host : allHosts) { UUID version = versions.get(host); String stringVersion = version == null ? UNREACHABLE : version.toString(); @@ -2377,7 +2371,7 @@ public class StorageProxy implements StorageProxyMBean hosts = new ArrayList(); results.put(stringVersion, hosts); } - hosts.add(host.getHostAddress()); + hosts.add(host.getHostAddress(withPort)); } // we're done: the results map is ready to return to the client. the rest is just debug logging: @@ -2485,7 +2479,7 @@ public class StorageProxy implements StorageProxyMBean DatabaseDescriptor.setMaxHintWindow(ms); } - public static boolean shouldHint(InetAddress ep) + public static boolean shouldHint(InetAddressAndPort ep) { if (DatabaseDescriptor.hintedHandoffEnabled()) { @@ -2534,7 +2528,7 @@ public class StorageProxy implements StorageProxyMBean throw new UnavailableException(ConsistencyLevel.ALL, liveMembers + Gossiper.instance.getUnreachableMembers().size(), liveMembers); } - Set allEndpoints = StorageService.instance.getLiveRingMembers(true); + Set allEndpoints = StorageService.instance.getLiveRingMembers(true); int blockFor = allEndpoints.size(); final TruncateResponseHandler responseHandler = new TruncateResponseHandler(blockFor); @@ -2543,7 +2537,7 @@ public class StorageProxy implements StorageProxyMBean Tracing.trace("Enqueuing truncate messages to hosts {}", allEndpoints); final Truncation truncation = new Truncation(keyspace, cfname); MessageOut message = truncation.createMessage(); - for (InetAddress endpoint : allEndpoints) + for (InetAddressAndPort endpoint : allEndpoints) MessagingService.instance().sendRR(message, endpoint, responseHandler); // Wait for all @@ -2570,7 +2564,7 @@ public class StorageProxy implements StorageProxyMBean public interface WritePerformer { public void apply(IMutation mutation, - Iterable targets, + Iterable targets, AbstractWriteResponseHandler responseHandler, String localDataCenter, ConsistencyLevel consistencyLevel) throws OverloadedException; @@ -2658,7 +2652,7 @@ public class StorageProxy implements StorageProxyMBean { if (MessagingService.DROPPABLE_VERBS.contains(verb)) MessagingService.instance().incrementDroppedMutations(mutationOpt, timeTaken); - HintRunnable runnable = new HintRunnable(Collections.singleton(FBUtilities.getBroadcastAddress())) + HintRunnable runnable = new HintRunnable(Collections.singleton(FBUtilities.getBroadcastAddressAndPort())) { protected void runMayThrow() throws Exception { @@ -2689,9 +2683,9 @@ public class StorageProxy implements StorageProxyMBean */ private abstract static class HintRunnable implements Runnable { - public final Collection targets; + public final Collection targets; - protected HintRunnable(Collection targets) + protected HintRunnable(Collection targets) { this.targets = targets; } @@ -2709,7 +2703,7 @@ public class StorageProxy implements StorageProxyMBean finally { StorageMetrics.totalHintsInProgress.dec(targets.size()); - for (InetAddress target : targets) + for (InetAddressAndPort target : targets) getHintsInProgressFor(target).decrementAndGet(); } } @@ -2743,7 +2737,7 @@ public class StorageProxy implements StorageProxyMBean logger.warn("Some hints were not written before shutdown. This is not supposed to happen. You should (a) run repair, and (b) file a bug report"); } - private static AtomicInteger getHintsInProgressFor(InetAddress destination) + private static AtomicInteger getHintsInProgressFor(InetAddressAndPort destination) { try { @@ -2755,22 +2749,22 @@ public class StorageProxy implements StorageProxyMBean } } - public static Future submitHint(Mutation mutation, InetAddress target, AbstractWriteResponseHandler responseHandler) + public static Future submitHint(Mutation mutation, InetAddressAndPort target, AbstractWriteResponseHandler responseHandler) { return submitHint(mutation, Collections.singleton(target), responseHandler); } public static Future submitHint(Mutation mutation, - Collection targets, + Collection targets, AbstractWriteResponseHandler responseHandler) { HintRunnable runnable = new HintRunnable(targets) { public void runMayThrow() { - Set validTargets = new HashSet<>(targets.size()); + Set validTargets = new HashSet<>(targets.size()); Set hostIds = new HashSet<>(targets.size()); - for (InetAddress target : targets) + for (InetAddressAndPort target : targets) { UUID hostId = StorageService.instance.getHostIdForEndpoint(target); if (hostId != null) @@ -2796,7 +2790,7 @@ public class StorageProxy implements StorageProxyMBean private static Future submitHint(HintRunnable runnable) { StorageMetrics.totalHintsInProgress.inc(runnable.targets.size()); - for (InetAddress target : runnable.targets) + for (InetAddressAndPort target : runnable.targets) getHintsInProgressFor(target).incrementAndGet(); return (Future) StageManager.getStage(Stage.MUTATION).submit(runnable); } diff --git a/src/java/org/apache/cassandra/service/StorageProxyMBean.java b/src/java/org/apache/cassandra/service/StorageProxyMBean.java index 173d43ff61..76a6617b45 100644 --- a/src/java/org/apache/cassandra/service/StorageProxyMBean.java +++ b/src/java/org/apache/cassandra/service/StorageProxyMBean.java @@ -65,7 +65,8 @@ public interface StorageProxyMBean public void setOtcBacklogExpirationInterval(int intervalInMillis); /** Returns each live node's schema version */ - public Map> getSchemaVersions(); + @Deprecated public Map> getSchemaVersions(); + public Map> getSchemaVersionsWithPort(); public int getNumberOfTables(); diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index 89d5358b3c..cda575aef9 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -158,23 +158,23 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public Collection> getLocalRanges(String keyspaceName) { - return getRangesForEndpoint(keyspaceName, FBUtilities.getBroadcastAddress()); + return getRangesForEndpoint(keyspaceName, FBUtilities.getBroadcastAddressAndPort()); } public Collection> getPrimaryRanges(String keyspace) { - return getPrimaryRangesForEndpoint(keyspace, FBUtilities.getBroadcastAddress()); + return getPrimaryRangesForEndpoint(keyspace, FBUtilities.getBroadcastAddressAndPort()); } public Collection> getPrimaryRangesWithinDC(String keyspace) { - return getPrimaryRangeForEndpointWithinDC(keyspace, FBUtilities.getBroadcastAddress()); + return getPrimaryRangeForEndpointWithinDC(keyspace, FBUtilities.getBroadcastAddressAndPort()); } - private final Set replicatingNodes = Collections.synchronizedSet(new HashSet()); + private final Set replicatingNodes = Collections.synchronizedSet(new HashSet()); private CassandraDaemon daemon; - private InetAddress removingNode; + private InetAddressAndPort removingNode; /* Are we starting this node in bootstrap mode? */ private volatile boolean isBootstrapMode; @@ -225,7 +225,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE SystemKeyspace.updateTokens(tokens); Collection localTokens = getLocalTokens(); setGossipTokens(localTokens); - tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddress()); + tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddressAndPort()); setMode(Mode.NORMAL, false); } @@ -233,6 +233,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { List> states = new ArrayList>(); states.add(Pair.create(ApplicationState.TOKENS, valueFactory.tokens(tokens))); + states.add(Pair.create(ApplicationState.STATUS_WITH_PORT, valueFactory.normal(tokens))); states.add(Pair.create(ApplicationState.STATUS, valueFactory.normal(tokens))); Gossiper.instance.addLocalApplicationStates(states); } @@ -407,7 +408,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * they get the Gossip shutdown message, so even if * we don't get time to broadcast this, it is not a problem. * - * See {@link Gossiper#markAsShutdown(InetAddress)} + * See {@link Gossiper#markAsShutdown(InetAddressAndPort)} */ private void shutdownClientServers() { @@ -463,9 +464,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE "To perform this operation, please restart with " + "-Dcassandra.allow_unsafe_replace=true"); - InetAddress replaceAddress = DatabaseDescriptor.getReplaceAddress(); + InetAddressAndPort replaceAddress = DatabaseDescriptor.getReplaceAddress(); logger.info("Gathering node replacement information for {}", replaceAddress); - Map epStates = Gossiper.instance.doShadowRound(); + Map epStates = Gossiper.instance.doShadowRound(); // as we've completed the shadow round of gossip, we should be able to find the node we're replacing if (epStates.get(replaceAddress) == null) throw new RuntimeException(String.format("Cannot replace_address %s because it doesn't exist in gossip", replaceAddress)); @@ -503,25 +504,32 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } logger.debug("Starting shadow gossip round to check for endpoint collision"); - Map epStates = Gossiper.instance.doShadowRound(); + Map epStates = Gossiper.instance.doShadowRound(); // If bootstrapping, check whether any previously known status for the endpoint makes it unsafe to do so. // If not bootstrapping, compare the host id for this endpoint learned from gossip (if any) with the local // one, which was either read from system.local or generated at startup. If a learned id is present & // doesn't match the local, then the node needs replacing - if (!Gossiper.instance.isSafeForStartup(FBUtilities.getBroadcastAddress(), localHostId, shouldBootstrap(), epStates)) + if (!Gossiper.instance.isSafeForStartup(FBUtilities.getBroadcastAddressAndPort(), localHostId, shouldBootstrap(), epStates)) { throw new RuntimeException(String.format("A node with address %s already exists, cancelling join. " + "Use cassandra.replace_address if you want to replace this node.", - FBUtilities.getBroadcastAddress())); + FBUtilities.getBroadcastAddressAndPort())); } if (shouldBootstrap() && useStrictConsistency && !allowSimultaneousMoves()) { - for (Map.Entry entry : epStates.entrySet()) + for (Map.Entry entry : epStates.entrySet()) { // ignore local node or empty status - if (entry.getKey().equals(FBUtilities.getBroadcastAddress()) || entry.getValue().getApplicationState(ApplicationState.STATUS) == null) + if (entry.getKey().equals(FBUtilities.getBroadcastAddressAndPort()) || (entry.getValue().getApplicationState(ApplicationState.STATUS_WITH_PORT) == null & entry.getValue().getApplicationState(ApplicationState.STATUS) == null)) continue; + + VersionedValue value = entry.getValue().getApplicationState(ApplicationState.STATUS_WITH_PORT); + if (value == null) + { + value = entry.getValue().getApplicationState(ApplicationState.STATUS); + } + String[] pieces = splitValue(entry.getValue().getApplicationState(ApplicationState.STATUS)); assert (pieces.length > 0); String state = pieces[0]; @@ -553,10 +561,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (Boolean.parseBoolean(System.getProperty("cassandra.load_ring_state", "true"))) { logger.info("Populating token metadata from system tables"); - Multimap loadedTokens = SystemKeyspace.loadTokens(); + Multimap loadedTokens = SystemKeyspace.loadTokens(); if (!shouldBootstrap()) // if we have not completed bootstrapping, we should not add ourselves as a normal token - loadedTokens.putAll(FBUtilities.getBroadcastAddress(), SystemKeyspace.getSavedTokens()); - for (InetAddress ep : loadedTokens.keySet()) + loadedTokens.putAll(FBUtilities.getBroadcastAddressAndPort(), SystemKeyspace.getSavedTokens()); + for (InetAddressAndPort ep : loadedTokens.keySet()) tokenMetadata.updateNormalTokens(loadedTokens.get(ep), ep); logger.info("Token metadata: {}", tokenMetadata); @@ -640,10 +648,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE Collection tokens = SystemKeyspace.getSavedTokens(); if (!tokens.isEmpty()) { - tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddress()); + tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddressAndPort()); // order is important here, the gossiper can fire in between adding these two states. It's ok to send TOKENS without STATUS, but *not* vice versa. List> states = new ArrayList>(); states.add(Pair.create(ApplicationState.TOKENS, valueFactory.tokens(tokens))); + states.add(Pair.create(ApplicationState.STATUS_WITH_PORT, valueFactory.hibernate(true))); states.add(Pair.create(ApplicationState.STATUS, valueFactory.hibernate(true))); Gossiper.instance.addLocalApplicationStates(states); } @@ -659,11 +668,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (Boolean.parseBoolean(System.getProperty("cassandra.load_ring_state", "true"))) { logger.info("Loading persisted ring state"); - Multimap loadedTokens = SystemKeyspace.loadTokens(); - Map loadedHostIds = SystemKeyspace.loadHostIds(); - for (InetAddress ep : loadedTokens.keySet()) + Multimap loadedTokens = SystemKeyspace.loadTokens(); + Map loadedHostIds = SystemKeyspace.loadHostIds(); + for (InetAddressAndPort ep : loadedTokens.keySet()) { - if (ep.equals(FBUtilities.getBroadcastAddress())) + if (ep.equals(FBUtilities.getBroadcastAddressAndPort())) { // entry has been mistakenly added, delete it SystemKeyspace.removeEndpoint(ep); @@ -707,7 +716,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public static boolean isSeed() { - return DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddress()); + return DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddressAndPort()); } private void prepareToJoin() throws ConfigurationException @@ -753,6 +762,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE "the node to be replaced ({}). If the previous node has been down for longer than max_hint_window_in_ms, " + "repair must be run after the replacement process in order to make this node consistent.", DatabaseDescriptor.getReplaceAddress()); + appStates.put(ApplicationState.STATUS_WITH_PORT, valueFactory.hibernate(true)); appStates.put(ApplicationState.STATUS, valueFactory.hibernate(true)); } } @@ -765,10 +775,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // for bootstrap to get the load info it needs. // (we won't be part of the storage ring though until we add a counterId to our state, below.) // Seed the host ID-to-endpoint map with our own ID. - getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddress()); + getTokenMetadata().updateHostId(localHostId, FBUtilities.getBroadcastAddressAndPort()); appStates.put(ApplicationState.NET_VERSION, valueFactory.networkVersion()); appStates.put(ApplicationState.HOST_ID, valueFactory.hostId(localHostId)); - appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getBroadcastRpcAddress())); + appStates.put(ApplicationState.NATIVE_ADDRESS_AND_PORT, valueFactory.nativeaddressAndPort(FBUtilities.getBroadcastNativeAddressAndPort())); + appStates.put(ApplicationState.RPC_ADDRESS, valueFactory.rpcaddress(FBUtilities.getJustBroadcastNativeAddress())); appStates.put(ApplicationState.RELEASE_VERSION, valueFactory.releaseVersion()); // load the persisted ring state. This used to be done earlier in the init process, @@ -826,16 +837,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // // We attempted to replace this with a schema-presence check, but you need a meaningful sleep // to get schema info from gossip which defeats the purpose. See CASSANDRA-4427 for the gory details. - Set current = new HashSet<>(); + Set current = new HashSet<>(); if (logger.isDebugEnabled()) { logger.debug("Bootstrap variables: {} {} {} {}", DatabaseDescriptor.isAutoBootstrap(), SystemKeyspace.bootstrapInProgress(), SystemKeyspace.bootstrapComplete(), - DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddress())); + DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddressAndPort())); } - if (DatabaseDescriptor.isAutoBootstrap() && !SystemKeyspace.bootstrapComplete() && DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddress())) + if (DatabaseDescriptor.isAutoBootstrap() && !SystemKeyspace.bootstrapComplete() && DatabaseDescriptor.getSeeds().contains(FBUtilities.getBroadcastAddressAndPort())) { logger.info("This node will not auto bootstrap because it is configured to be a seed node."); } @@ -873,13 +884,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // get bootstrap tokens if (!replacing) { - if (tokenMetadata.isMember(FBUtilities.getBroadcastAddress())) + if (tokenMetadata.isMember(FBUtilities.getBroadcastAddressAndPort())) { String s = "This node is already a member of the token ring; bootstrap aborted. (If replacing a dead node, remove the old one from the ring first.)"; throw new UnsupportedOperationException(s); } setMode(Mode.JOINING, "getting bootstrap token", true); - bootstrapTokens = BootStrapper.getBootstrapTokens(tokenMetadata, FBUtilities.getBroadcastAddress(), delay); + bootstrapTokens = BootStrapper.getBootstrapTokens(tokenMetadata, FBUtilities.getBroadcastAddressAndPort(), delay); } else { @@ -899,7 +910,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // check for operator errors... for (Token token : bootstrapTokens) { - InetAddress existing = tokenMetadata.getEndpoint(token); + InetAddressAndPort existing = tokenMetadata.getEndpoint(token); if (existing != null) { long nanoDelay = delay * 1000000L; @@ -935,7 +946,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE bootstrapTokens = SystemKeyspace.getSavedTokens(); if (bootstrapTokens.isEmpty()) { - bootstrapTokens = BootStrapper.getBootstrapTokens(tokenMetadata, FBUtilities.getBroadcastAddress(), delay); + bootstrapTokens = BootStrapper.getBootstrapTokens(tokenMetadata, FBUtilities.getBroadcastAddressAndPort(), delay); } else { @@ -958,7 +969,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // remove the existing info about the replaced node. if (!current.isEmpty()) { - for (InetAddress existing : current) + for (InetAddressAndPort existing : current) Gossiper.instance.replacedEndpoint(existing); } } @@ -975,15 +986,15 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public static boolean isReplacingSameAddress() { - InetAddress replaceAddress = DatabaseDescriptor.getReplaceAddress(); - return replaceAddress != null && replaceAddress.equals(FBUtilities.getBroadcastAddress()); + InetAddressAndPort replaceAddress = DatabaseDescriptor.getReplaceAddress(); + return replaceAddress != null && replaceAddress.equals(FBUtilities.getBroadcastAddressAndPort()); } public void gossipSnitchInfo() { IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); - String dc = snitch.getDatacenter(FBUtilities.getBroadcastAddress()); - String rack = snitch.getRack(FBUtilities.getBroadcastAddress()); + String dc = snitch.getDatacenter(FBUtilities.getBroadcastAddressAndPort()); + String rack = snitch.getRack(FBUtilities.getBroadcastAddressAndPort()); Gossiper.instance.addLocalApplicationState(ApplicationState.DC, StorageService.instance.valueFactory.datacenter(dc)); Gossiper.instance.addLocalApplicationState(ApplicationState.RACK, StorageService.instance.valueFactory.rack(rack)); } @@ -1111,7 +1122,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public boolean isJoined() { - return tokenMetadata.isMember(FBUtilities.getBroadcastAddress()) && !isSurveyMode; + return tokenMetadata.isMember(FBUtilities.getBroadcastAddressAndPort()) && !isSurveyMode; } public void rebuild(String sourceDc) @@ -1141,7 +1152,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { RangeStreamer streamer = new RangeStreamer(tokenMetadata, null, - FBUtilities.getBroadcastAddress(), + FBUtilities.getBroadcastAddressAndPort(), StreamOperation.REBUILD, useStrictConsistency && !replacing, DatabaseDescriptor.getEndpointSnitch(), @@ -1202,13 +1213,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (specificSources != null) { String[] stringHosts = specificSources.split(","); - Set sources = new HashSet<>(stringHosts.length); + Set sources = new HashSet<>(stringHosts.length); for (String stringHost : stringHosts) { try { - InetAddress endpoint = InetAddress.getByName(stringHost); - if (FBUtilities.getBroadcastAddress().equals(endpoint)) + InetAddressAndPort endpoint = InetAddressAndPort.getByName(stringHost); + if (FBUtilities.getBroadcastAddressAndPort().equals(endpoint)) { throw new IllegalArgumentException("This host was specified as a source for rebuilding. Sources for a rebuild can only be other nodes in the cluster."); } @@ -1449,8 +1460,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // if not an existing token then bootstrap List> states = new ArrayList<>(); states.add(Pair.create(ApplicationState.TOKENS, valueFactory.tokens(tokens))); + states.add(Pair.create(ApplicationState.STATUS_WITH_PORT, replacing? + valueFactory.bootReplacingWithPort(DatabaseDescriptor.getReplaceAddress()) : + valueFactory.bootstrapping(tokens))); states.add(Pair.create(ApplicationState.STATUS, replacing? - valueFactory.bootReplacing(DatabaseDescriptor.getReplaceAddress()) : + valueFactory.bootReplacing(DatabaseDescriptor.getReplaceAddress().address) : valueFactory.bootstrapping(tokens))); Gossiper.instance.addLocalApplicationStates(states); setMode(Mode.JOINING, "sleeping " + RING_DELAY + " ms for pending range setup", true); @@ -1459,7 +1473,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE else { // Dont set any state for the node which is bootstrapping the existing token... - tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddress()); + tokenMetadata.updateNormalTokens(tokens, FBUtilities.getBroadcastAddressAndPort()); SystemKeyspace.removeEndpoint(DatabaseDescriptor.getReplaceAddress()); } if (!Gossiper.instance.seenAnySeed()) @@ -1475,7 +1489,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE invalidateDiskBoundaries(); setMode(Mode.JOINING, "Starting to bootstrap...", true); - BootStrapper bootstrapper = new BootStrapper(FBUtilities.getBroadcastAddress(), tokens, tokenMetadata); + BootStrapper bootstrapper = new BootStrapper(FBUtilities.getBroadcastAddressAndPort(), tokens, tokenMetadata); bootstrapper.addProgressListener(progressSupport); ListenableFuture bootstrapStream = bootstrapper.bootstrap(streamStateStore, useStrictConsistency && !replacing); // handles token update Futures.addCallback(bootstrapStream, new FutureCallback() @@ -1547,7 +1561,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // get bootstrap tokens saved in system keyspace final Collection tokens = SystemKeyspace.getSavedTokens(); // already bootstrapped ranges are filtered during bootstrap - BootStrapper bootstrapper = new BootStrapper(FBUtilities.getBroadcastAddress(), tokens, tokenMetadata); + BootStrapper bootstrapper = new BootStrapper(FBUtilities.getBroadcastAddressAndPort(), tokens, tokenMetadata); bootstrapper.addProgressListener(progressSupport); ListenableFuture bootstrapStream = bootstrapper.bootstrap(streamStateStore, useStrictConsistency && !replacing); // handles token update Futures.addCallback(bootstrapStream, new FutureCallback() @@ -1608,35 +1622,67 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return tokenMetadata; } + public Map, List> getRangeToEndpointMap(String keyspace) + { + return getRangeToEndpointMap(keyspace, false); + } + + public Map, List> getRangeToEndpointWithPortMap(String keyspace) + { + return getRangeToEndpointMap(keyspace, true); + } + /** * for a keyspace, return the ranges and corresponding listen addresses. * @param keyspace * @return the endpoint map */ - public Map, List> getRangeToEndpointMap(String keyspace) + public Map, List> getRangeToEndpointMap(String keyspace, boolean withPort) { /* All the ranges for the tokens */ Map, List> map = new HashMap<>(); - for (Map.Entry,List> entry : getRangeToAddressMap(keyspace).entrySet()) + for (Map.Entry,List> entry : getRangeToAddressMap(keyspace).entrySet()) { - map.put(entry.getKey().asList(), stringify(entry.getValue())); + map.put(entry.getKey().asList(), stringify(entry.getValue(), withPort)); } return map; } /** - * Return the rpc address associated with an endpoint as a string. + * Return the native address associated with an endpoint as a string. * @param endpoint The endpoint to get rpc address for - * @return the rpc address + * @return the native address */ - public String getRpcaddress(InetAddress endpoint) + public String getNativeaddress(InetAddressAndPort endpoint, boolean withPort) { - if (endpoint.equals(FBUtilities.getBroadcastAddress())) - return FBUtilities.getBroadcastRpcAddress().getHostAddress(); + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) + return FBUtilities.getBroadcastNativeAddressAndPort().toString(withPort); + else if (Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.NATIVE_ADDRESS_AND_PORT) != null) + { + try + { + InetAddressAndPort address = InetAddressAndPort.getByName(Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS).value); + return address.getHostAddress(withPort); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + } else if (Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS) == null) - return endpoint.getHostAddress(); + return endpoint.address.getHostAddress() + ":" + DatabaseDescriptor.getNativeTransportPort(); else - return Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS).value; + return Gossiper.instance.getEndpointStateForEndpoint(endpoint).getApplicationState(ApplicationState.RPC_ADDRESS).value + ":" + DatabaseDescriptor.getNativeTransportPort(); + } + + public Map, List> getRangeToRpcaddressMap(String keyspace) + { + return getRangeToNativeaddressMap(keyspace, false); + } + + public Map, List> getRangeToNativeaddressWithPortMap(String keyspace) + { + return getRangeToNativeaddressMap(keyspace, true); } /** @@ -1644,16 +1690,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param keyspace * @return the endpoint map */ - public Map, List> getRangeToRpcaddressMap(String keyspace) + private Map, List> getRangeToNativeaddressMap(String keyspace, boolean withPort) { /* All the ranges for the tokens */ Map, List> map = new HashMap<>(); - for (Map.Entry, List> entry : getRangeToAddressMap(keyspace).entrySet()) + for (Map.Entry, List> entry : getRangeToAddressMap(keyspace).entrySet()) { List rpcaddrs = new ArrayList<>(entry.getValue().size()); - for (InetAddress endpoint: entry.getValue()) + for (InetAddressAndPort endpoint: entry.getValue()) { - rpcaddrs.add(getRpcaddress(endpoint)); + rpcaddrs.add(getNativeaddress(endpoint, withPort)); } map.put(entry.getKey().asList(), rpcaddrs); } @@ -1661,6 +1707,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } public Map, List> getPendingRangeToEndpointMap(String keyspace) + { + return getPendingRangeToEndpointMap(keyspace, false); + } + + public Map, List> getPendingRangeToEndpointWithPortMap(String keyspace) + { + return getPendingRangeToEndpointMap(keyspace, true); + } + + private Map, List> getPendingRangeToEndpointMap(String keyspace, boolean withPort) { // some people just want to get a visual representation of things. Allow null and set it to the first // non-system keyspace. @@ -1668,34 +1724,34 @@ public class StorageService extends NotificationBroadcasterSupport implements IE keyspace = Schema.instance.getNonLocalStrategyKeyspaces().get(0); Map, List> map = new HashMap<>(); - for (Map.Entry, Collection> entry : tokenMetadata.getPendingRangesMM(keyspace).asMap().entrySet()) + for (Map.Entry, Collection> entry : tokenMetadata.getPendingRangesMM(keyspace).asMap().entrySet()) { - List l = new ArrayList<>(entry.getValue()); - map.put(entry.getKey().asList(), stringify(l)); + List l = new ArrayList<>(entry.getValue()); + map.put(entry.getKey().asList(), stringify(l, withPort)); } return map; } - public Map, List> getRangeToAddressMap(String keyspace) + public Map, List> getRangeToAddressMap(String keyspace) { return getRangeToAddressMap(keyspace, tokenMetadata.sortedTokens()); } - public Map, List> getRangeToAddressMapInLocalDC(String keyspace) + public Map, List> getRangeToAddressMapInLocalDC(String keyspace) { - Predicate isLocalDC = new Predicate() + Predicate isLocalDC = new Predicate() { - public boolean apply(InetAddress address) + public boolean apply(InetAddressAndPort address) { return isLocalDC(address); } }; - Map, List> origMap = getRangeToAddressMap(keyspace, getTokensInLocalDC()); - Map, List> filteredMap = Maps.newHashMap(); - for (Map.Entry, List> entry : origMap.entrySet()) + Map, List> origMap = getRangeToAddressMap(keyspace, getTokensInLocalDC()); + Map, List> filteredMap = Maps.newHashMap(); + for (Map.Entry, List> entry : origMap.entrySet()) { - List endpointsInLocalDC = Lists.newArrayList(Collections2.filter(entry.getValue(), isLocalDC)); + List endpointsInLocalDC = Lists.newArrayList(Collections2.filter(entry.getValue(), isLocalDC)); filteredMap.put(entry.getKey(), endpointsInLocalDC); } @@ -1707,21 +1763,21 @@ public class StorageService extends NotificationBroadcasterSupport implements IE List filteredTokens = Lists.newArrayList(); for (Token token : tokenMetadata.sortedTokens()) { - InetAddress endpoint = tokenMetadata.getEndpoint(token); + InetAddressAndPort endpoint = tokenMetadata.getEndpoint(token); if (isLocalDC(endpoint)) filteredTokens.add(token); } return filteredTokens; } - private boolean isLocalDC(InetAddress targetHost) + private boolean isLocalDC(InetAddressAndPort targetHost) { String remoteDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(targetHost); - String localDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + String localDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); return remoteDC.equals(localDC); } - private Map, List> getRangeToAddressMap(String keyspace, List sortedTokens) + private Map, List> getRangeToAddressMap(String keyspace, List sortedTokens) { // some people just want to get a visual representation of things. Allow null and set it to the first // non-system keyspace. @@ -1733,6 +1789,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } + public List describeRingJMX(String keyspace) throws IOException + { + return describeRingJMX(keyspace, false); + } + + public List describeRingWithPortJMX(String keyspace) throws IOException + { + return describeRingJMX(keyspace,true); + } + /** * The same as {@code describeRing(String)} but converts TokenRange to the String for JMX compatibility * @@ -1740,12 +1806,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @return a List of TokenRange(s) converted to String for the given keyspace */ - public List describeRingJMX(String keyspace) throws IOException + private List describeRingJMX(String keyspace, boolean withPort) throws IOException { List tokenRanges; try { - tokenRanges = describeRing(keyspace); + tokenRanges = describeRing(keyspace, false, withPort); } catch (InvalidRequestException e) { @@ -1754,7 +1820,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE List result = new ArrayList<>(tokenRanges.size()); for (TokenRange tokenRange : tokenRanges) - result.add(tokenRange.toString()); + result.add(tokenRange.toString(withPort)); return result; } @@ -1770,7 +1836,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ public List describeRing(String keyspace) throws InvalidRequestException { - return describeRing(keyspace, false); + return describeRing(keyspace, false, false); } /** @@ -1778,10 +1844,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ public List describeLocalRing(String keyspace) throws InvalidRequestException { - return describeRing(keyspace, true); + return describeRing(keyspace, true, false); } - private List describeRing(String keyspace, boolean includeOnlyLocalDC) throws InvalidRequestException + private List describeRing(String keyspace, boolean includeOnlyLocalDC, boolean withPort) throws InvalidRequestException { if (!Schema.instance.getKeyspaces().contains(keyspace)) throw new InvalidRequestException("No such keyspace: " + keyspace); @@ -1792,39 +1858,49 @@ public class StorageService extends NotificationBroadcasterSupport implements IE List ranges = new ArrayList<>(); Token.TokenFactory tf = getTokenFactory(); - Map, List> rangeToAddressMap = + Map, List> rangeToAddressMap = includeOnlyLocalDC ? getRangeToAddressMapInLocalDC(keyspace) : getRangeToAddressMap(keyspace); - for (Map.Entry, List> entry : rangeToAddressMap.entrySet()) - ranges.add(TokenRange.create(tf, entry.getKey(), entry.getValue())); + for (Map.Entry, List> entry : rangeToAddressMap.entrySet()) + ranges.add(TokenRange.create(tf, entry.getKey(), entry.getValue(), withPort)); return ranges; } public Map getTokenToEndpointMap() { - Map mapInetAddress = tokenMetadata.getNormalAndBootstrappingTokenToEndpointMap(); + return getTokenToEndpointMap(false); + } + + public Map getTokenToEndpointWithPortMap() + { + return getTokenToEndpointMap(true); + } + + private Map getTokenToEndpointMap(boolean withPort) + { + Map mapInetAddress = tokenMetadata.getNormalAndBootstrappingTokenToEndpointMap(); // in order to preserve tokens in ascending order, we use LinkedHashMap here Map mapString = new LinkedHashMap<>(mapInetAddress.size()); List tokens = new ArrayList<>(mapInetAddress.keySet()); Collections.sort(tokens); for (Token token : tokens) { - mapString.put(token.toString(), mapInetAddress.get(token).getHostAddress()); + mapString.put(token.toString(), mapInetAddress.get(token).getHostAddress(withPort)); } return mapString; } public String getLocalHostId() { - return getTokenMetadata().getHostId(FBUtilities.getBroadcastAddress()).toString(); + return getTokenMetadata().getHostId(FBUtilities.getBroadcastAddressAndPort()).toString(); } public UUID getLocalHostUUID() { - return getTokenMetadata().getHostId(FBUtilities.getBroadcastAddress()); + return getTokenMetadata().getHostId(FBUtilities.getBroadcastAddressAndPort()); } public Map getHostIdMap() @@ -1832,19 +1908,40 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return getEndpointToHostId(); } + public Map getEndpointToHostId() + { + return getEndpointToHostId(false); + } + + public Map getEndpointWithPortToHostId() + { + return getEndpointToHostId(true); + } + + private Map getEndpointToHostId(boolean withPort) { Map mapOut = new HashMap<>(); - for (Map.Entry entry : getTokenMetadata().getEndpointToHostIdMapForReading().entrySet()) - mapOut.put(entry.getKey().getHostAddress(), entry.getValue().toString()); + for (Map.Entry entry : getTokenMetadata().getEndpointToHostIdMapForReading().entrySet()) + mapOut.put(entry.getKey().getHostAddress(withPort), entry.getValue().toString()); return mapOut; } public Map getHostIdToEndpoint() + { + return getHostIdToEndpoint(false); + } + + public Map getHostIdToEndpointWithPort() + { + return getHostIdToEndpoint(true); + } + + private Map getHostIdToEndpoint(boolean withPort) { Map mapOut = new HashMap<>(); - for (Map.Entry entry : getTokenMetadata().getEndpointToHostIdMapForReading().entrySet()) - mapOut.put(entry.getValue().toString(), entry.getKey().getHostAddress()); + for (Map.Entry entry : getTokenMetadata().getEndpointToHostIdMapForReading().entrySet()) + mapOut.put(entry.getValue().toString(), entry.getKey().getHostAddress(withPort)); return mapOut; } @@ -1854,9 +1951,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param ranges * @return mapping of ranges to the replicas responsible for them. */ - private Map, List> constructRangeToEndpointMap(String keyspace, List> ranges) + private Map, List> constructRangeToEndpointMap(String keyspace, List> ranges) { - Map, List> rangeToEndpointMap = new HashMap<>(ranges.size()); + Map, List> rangeToEndpointMap = new HashMap<>(ranges.size()); for (Range range : ranges) { rangeToEndpointMap.put(range, Keyspace.open(keyspace).getReplicationStrategy().getNaturalEndpoints(range.right)); @@ -1864,7 +1961,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return rangeToEndpointMap; } - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) { // no-op } @@ -1901,9 +1998,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * Note: Any time a node state changes from STATUS_NORMAL, it will not be visible to new nodes. So it follows that * you should never bootstrap a new node during a removenode, decommission or move. */ - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) { - if (state == ApplicationState.STATUS) + if (state == ApplicationState.STATUS | state == ApplicationState.STATUS_WITH_PORT) { String[] pieces = splitValue(value); assert (pieces.length > 0); @@ -1973,6 +2070,17 @@ public class StorageService extends NotificationBroadcasterSupport implements IE throw new RuntimeException(e); } break; + case NATIVE_ADDRESS_AND_PORT: + try + { + InetAddressAndPort address = InetAddressAndPort.getByName(value.value); + SystemKeyspace.updatePeerNativeAddress(endpoint, address); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + break; case SCHEMA: SystemKeyspace.updatePeerInfo(endpoint, "schema_version", UUID.fromString(value.value)); MigrationManager.instance.scheduleSchemaPull(endpoint, epState); @@ -1996,7 +2104,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return value.value.split(VersionedValue.DELIMITER_STR, -1); } - private void updateNetVersion(InetAddress endpoint, VersionedValue value) + private void updateNetVersion(InetAddressAndPort endpoint, VersionedValue value) { try { @@ -2008,7 +2116,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } } - public void updateTopology(InetAddress endpoint) + public void updateTopology(InetAddressAndPort endpoint) { if (getTokenMetadata().isMember(endpoint)) { @@ -2021,9 +2129,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE getTokenMetadata().updateTopology(); } - private void updatePeerInfo(InetAddress endpoint) + private void updatePeerInfo(InetAddressAndPort endpoint) { EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint); + InetAddress native_address = null; + int native_port = DatabaseDescriptor.getNativeTransportPort(); + for (Map.Entry entry : epState.states()) { switch (entry.getKey()) @@ -2040,7 +2151,19 @@ public class StorageService extends NotificationBroadcasterSupport implements IE case RPC_ADDRESS: try { - SystemKeyspace.updatePeerInfo(endpoint, "rpc_address", InetAddress.getByName(entry.getValue().value)); + native_address = InetAddress.getByName(entry.getValue().value); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + break; + case NATIVE_ADDRESS_AND_PORT: + try + { + InetAddressAndPort address = InetAddressAndPort.getByName(entry.getValue().value); + native_address = address.address; + native_port = address.port; } catch (UnknownHostException e) { @@ -2055,9 +2178,17 @@ public class StorageService extends NotificationBroadcasterSupport implements IE break; } } + + //Some tests won't set all the states + if (native_address != null) + { + SystemKeyspace.updatePeerNativeAddress(endpoint, + InetAddressAndPort.getByAddressOverrideDefaults(native_address, + native_port)); + } } - private void notifyRpcChange(InetAddress endpoint, boolean ready) + private void notifyRpcChange(InetAddressAndPort endpoint, boolean ready) { if (ready) notifyUp(endpoint); @@ -2065,7 +2196,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE notifyDown(endpoint); } - private void notifyUp(InetAddress endpoint) + private void notifyUp(InetAddressAndPort endpoint) { if (!isRpcReady(endpoint) || !Gossiper.instance.isAlive(endpoint)) return; @@ -2074,13 +2205,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE subscriber.onUp(endpoint); } - private void notifyDown(InetAddress endpoint) + private void notifyDown(InetAddressAndPort endpoint) { for (IEndpointLifecycleSubscriber subscriber : lifecycleSubscribers) subscriber.onDown(endpoint); } - private void notifyJoined(InetAddress endpoint) + private void notifyJoined(InetAddressAndPort endpoint) { if (!isStatus(endpoint, VersionedValue.STATUS_NORMAL)) return; @@ -2089,25 +2220,25 @@ public class StorageService extends NotificationBroadcasterSupport implements IE subscriber.onJoinCluster(endpoint); } - private void notifyMoved(InetAddress endpoint) + private void notifyMoved(InetAddressAndPort endpoint) { for (IEndpointLifecycleSubscriber subscriber : lifecycleSubscribers) subscriber.onMove(endpoint); } - private void notifyLeft(InetAddress endpoint) + private void notifyLeft(InetAddressAndPort endpoint) { for (IEndpointLifecycleSubscriber subscriber : lifecycleSubscribers) subscriber.onLeaveCluster(endpoint); } - private boolean isStatus(InetAddress endpoint, String status) + private boolean isStatus(InetAddressAndPort endpoint, String status) { EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); return state != null && state.getStatus().equals(status); } - public boolean isRpcReady(InetAddress endpoint) + public boolean isRpcReady(InetAddressAndPort endpoint) { EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint); return state != null && state.isRpcReady(); @@ -2123,7 +2254,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ public void setRpcReady(boolean value) { - EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(FBUtilities.getBroadcastAddress()); + EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(FBUtilities.getBroadcastAddressAndPort()); // if value is false we're OK with a null state, if it is true we are not. assert !value || state != null; @@ -2131,7 +2262,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE Gossiper.instance.addLocalApplicationState(ApplicationState.RPC_READY, valueFactory.rpcReady(value)); } - private Collection getTokensFor(InetAddress endpoint) + private Collection getTokensFor(InetAddressAndPort endpoint) { try { @@ -2156,7 +2287,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @param endpoint bootstrapping node */ - private void handleStateBootstrap(InetAddress endpoint) + private void handleStateBootstrap(InetAddressAndPort endpoint) { Collection tokens; // explicitly check for TOKENS, because a bootstrapping node might be bootstrapping in legacy mode; that is, not using vnodes and no token specified @@ -2186,12 +2317,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE tokenMetadata.updateHostId(Gossiper.instance.getHostId(endpoint), endpoint); } - private void handleStateBootreplacing(InetAddress newNode, String[] pieces) + private void handleStateBootreplacing(InetAddressAndPort newNode, String[] pieces) { - InetAddress oldNode; + InetAddressAndPort oldNode; try { - oldNode = InetAddress.getByName(pieces[1]); + oldNode = InetAddressAndPort.getByName(pieces[1]); } catch (Exception e) { @@ -2204,7 +2335,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE throw new RuntimeException(String.format("Node %s is trying to replace alive node %s.", newNode, oldNode)); } - Optional replacingNode = tokenMetadata.getReplacingNode(newNode); + Optional replacingNode = tokenMetadata.getReplacingNode(newNode); if (replacingNode.isPresent() && !replacingNode.get().equals(oldNode)) { throw new RuntimeException(String.format("Node %s is already replacing %s but is trying to replace %s.", @@ -2228,12 +2359,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @param endpoint node */ - private void handleStateNormal(final InetAddress endpoint, final String status) + private void handleStateNormal(final InetAddressAndPort endpoint, final String status) { Collection tokens = getTokensFor(endpoint); Set tokensToUpdateInMetadata = new HashSet<>(); Set tokensToUpdateInSystemKeyspace = new HashSet<>(); - Set endpointsToRemove = new HashSet<>(); + Set endpointsToRemove = new HashSet<>(); if (logger.isDebugEnabled()) logger.debug("Node {} state {}, token {}", endpoint, status, tokens); @@ -2246,7 +2377,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE endpoint, Gossiper.instance.getEndpointStateForEndpoint(endpoint)); - Optional replacingNode = tokenMetadata.getReplacingNode(endpoint); + Optional replacingNode = tokenMetadata.getReplacingNode(endpoint); if (replacingNode.isPresent()) { assert !endpoint.equals(replacingNode.get()) : "Pending replacement endpoint with same address is not supported"; @@ -2259,7 +2390,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE endpointsToRemove.add(replacingNode.get()); } - Optional replacementNode = tokenMetadata.getReplacementNode(endpoint); + Optional replacementNode = tokenMetadata.getReplacementNode(endpoint); if (replacementNode.isPresent()) { logger.warn("Node {} is currently being replaced by node {}.", endpoint, replacementNode.get()); @@ -2268,7 +2399,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE updatePeerInfo(endpoint); // Order Matters, TM.updateHostID() should be called before TM.updateNormalToken(), (see CASSANDRA-4300). UUID hostId = Gossiper.instance.getHostId(endpoint); - InetAddress existing = tokenMetadata.getEndpointForHostId(hostId); + InetAddressAndPort existing = tokenMetadata.getEndpointForHostId(hostId); if (replacing && isReplacingSameAddress() && Gossiper.instance.getEndpointStateForEndpoint(DatabaseDescriptor.getReplaceAddress()) != null && (hostId.equals(Gossiper.instance.getHostId(DatabaseDescriptor.getReplaceAddress())))) logger.warn("Not updating token metadata for {} because I am replacing it", endpoint); @@ -2276,7 +2407,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { if (existing != null && !existing.equals(endpoint)) { - if (existing.equals(FBUtilities.getBroadcastAddress())) + if (existing.equals(FBUtilities.getBroadcastAddressAndPort())) { logger.warn("Not updating host ID {} for {} because it's mine", hostId, endpoint); tokenMetadata.removeEndpoint(endpoint); @@ -2303,7 +2434,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE for (final Token token : tokens) { // we don't want to update if this node is responsible for the token and it has a later startup time than endpoint. - InetAddress currentOwner = tokenMetadata.getEndpoint(token); + InetAddressAndPort currentOwner = tokenMetadata.getEndpoint(token); if (currentOwner == null) { logger.debug("New node {} at token {}", endpoint, token); @@ -2323,7 +2454,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // currentOwner is no longer current, endpoint is. Keep track of these moves, because when // a host no longer has any tokens, we'll want to remove it. - Multimap epToTokenCopy = getTokenMetadata().getEndpointToTokenMapForReading(); + Multimap epToTokenCopy = getTokenMetadata().getEndpointToTokenMapForReading(); epToTokenCopy.get(currentOwner).remove(token); if (epToTokenCopy.get(currentOwner).size() < 1) endpointsToRemove.add(currentOwner); @@ -2348,7 +2479,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE boolean isMember = tokenMetadata.isMember(endpoint); boolean isMoving = tokenMetadata.isMoving(endpoint); tokenMetadata.updateNormalTokens(tokensToUpdateInMetadata, endpoint); - for (InetAddress ep : endpointsToRemove) + for (InetAddressAndPort ep : endpointsToRemove) { removeEndpoint(ep); if (replacing && DatabaseDescriptor.getReplaceAddress().equals(ep)) @@ -2375,7 +2506,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @param endpoint node */ - private void handleStateLeaving(InetAddress endpoint) + private void handleStateLeaving(InetAddressAndPort endpoint) { Collection tokens = getTokensFor(endpoint); @@ -2408,7 +2539,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param endpoint If reason for leaving is decommission, endpoint is the leaving node. * @param pieces STATE_LEFT,token */ - private void handleStateLeft(InetAddress endpoint, String[] pieces) + private void handleStateLeft(InetAddressAndPort endpoint, String[] pieces) { assert pieces.length >= 2; Collection tokens = getTokensFor(endpoint); @@ -2425,7 +2556,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param endpoint moving endpoint address * @param pieces STATE_MOVING, token */ - private void handleStateMoving(InetAddress endpoint, String[] pieces) + private void handleStateMoving(InetAddressAndPort endpoint, String[] pieces) { assert pieces.length >= 2; Token token = getTokenFactory().fromString(pieces[1]); @@ -2444,11 +2575,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param endpoint node * @param pieces either REMOVED_TOKEN (node is gone) or REMOVING_TOKEN (replicas need to be restored) */ - private void handleStateRemoving(InetAddress endpoint, String[] pieces) + private void handleStateRemoving(InetAddressAndPort endpoint, String[] pieces) { assert (pieces.length > 0); - if (endpoint.equals(FBUtilities.getBroadcastAddress())) + if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort())) { logger.info("Received removenode gossip about myself. Is this node rejoining after an explicit removenode?"); try @@ -2494,7 +2625,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } } - private void excise(Collection tokens, InetAddress endpoint) + private void excise(Collection tokens, InetAddressAndPort endpoint) { logger.info("Removing tokens {} for {}", tokens, endpoint); @@ -2510,20 +2641,20 @@ public class StorageService extends NotificationBroadcasterSupport implements IE PendingRangeCalculatorService.instance.update(); } - private void excise(Collection tokens, InetAddress endpoint, long expireTime) + private void excise(Collection tokens, InetAddressAndPort endpoint, long expireTime) { addExpireTimeIfFound(endpoint, expireTime); excise(tokens, endpoint); } /** unlike excise we just need this endpoint gone without going through any notifications **/ - private void removeEndpoint(InetAddress endpoint) + private void removeEndpoint(InetAddressAndPort endpoint) { Gossiper.instance.removeEndpoint(endpoint); SystemKeyspace.removeEndpoint(endpoint); } - protected void addExpireTimeIfFound(InetAddress endpoint, long expireTime) + protected void addExpireTimeIfFound(InetAddressAndPort endpoint, long expireTime) { if (expireTime != 0L) { @@ -2543,23 +2674,23 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param ranges the ranges to find sources for * @return multimap of addresses to ranges the address is responsible for */ - private Multimap> getNewSourceRanges(String keyspaceName, Set> ranges) + private Multimap> getNewSourceRanges(String keyspaceName, Set> ranges) { - InetAddress myAddress = FBUtilities.getBroadcastAddress(); - Multimap, InetAddress> rangeAddresses = Keyspace.open(keyspaceName).getReplicationStrategy().getRangeAddresses(tokenMetadata.cloneOnlyTokenMap()); - Multimap> sourceRanges = HashMultimap.create(); + InetAddressAndPort myAddress = FBUtilities.getBroadcastAddressAndPort(); + Multimap, InetAddressAndPort> rangeAddresses = Keyspace.open(keyspaceName).getReplicationStrategy().getRangeAddresses(tokenMetadata.cloneOnlyTokenMap()); + Multimap> sourceRanges = HashMultimap.create(); IFailureDetector failureDetector = FailureDetector.instance; // find alive sources for our new ranges for (Range range : ranges) { - Collection possibleRanges = rangeAddresses.get(range); + Collection possibleRanges = rangeAddresses.get(range); IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); - List sources = snitch.getSortedListByProximity(myAddress, possibleRanges); + List sources = snitch.getSortedListByProximity(myAddress, possibleRanges); assert (!sources.contains(myAddress)); - for (InetAddress source : sources) + for (InetAddressAndPort source : sources) { if (failureDetector.isAlive(source)) { @@ -2576,7 +2707,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @param remote node to send notification to */ - private void sendReplicationNotification(InetAddress remote) + private void sendReplicationNotification(InetAddressAndPort remote) { // notify the remote token MessageOut msg = new MessageOut(MessagingService.Verb.REPLICATION_FINISHED); @@ -2608,23 +2739,23 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @param endpoint the node that left */ - private void restoreReplicaCount(InetAddress endpoint, final InetAddress notifyEndpoint) + private void restoreReplicaCount(InetAddressAndPort endpoint, final InetAddressAndPort notifyEndpoint) { - Multimap>>> rangesToFetch = HashMultimap.create(); + Multimap>>> rangesToFetch = HashMultimap.create(); - InetAddress myAddress = FBUtilities.getBroadcastAddress(); + InetAddressAndPort myAddress = FBUtilities.getBroadcastAddressAndPort(); for (String keyspaceName : Schema.instance.getNonLocalStrategyKeyspaces()) { - Multimap, InetAddress> changedRanges = getChangedRangesForLeaving(keyspaceName, endpoint); + Multimap, InetAddressAndPort> changedRanges = getChangedRangesForLeaving(keyspaceName, endpoint); Set> myNewRanges = new HashSet<>(); - for (Map.Entry, InetAddress> entry : changedRanges.entries()) + for (Map.Entry, InetAddressAndPort> entry : changedRanges.entries()) { if (entry.getValue().equals(myAddress)) myNewRanges.add(entry.getKey()); } - Multimap> sourceRanges = getNewSourceRanges(keyspaceName, myNewRanges); - for (Map.Entry>> entry : sourceRanges.asMap().entrySet()) + Multimap> sourceRanges = getNewSourceRanges(keyspaceName, myNewRanges); + for (Map.Entry>> entry : sourceRanges.asMap().entrySet()) { rangesToFetch.put(keyspaceName, entry); } @@ -2633,10 +2764,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE StreamPlan stream = new StreamPlan(StreamOperation.RESTORE_REPLICA_COUNT); for (String keyspaceName : rangesToFetch.keySet()) { - for (Map.Entry>> entry : rangesToFetch.get(keyspaceName)) + for (Map.Entry>> entry : rangesToFetch.get(keyspaceName)) { - InetAddress source = entry.getKey(); - InetAddress preferred = SystemKeyspace.getPreferredIP(source); + InetAddressAndPort source = entry.getKey(); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(source); Collection> ranges = entry.getValue(); if (logger.isDebugEnabled()) logger.debug("Requesting from {} ranges {}", source, StringUtils.join(ranges, ", ")); @@ -2661,7 +2792,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } // needs to be modified to accept either a keyspace or ARS. - private Multimap, InetAddress> getChangedRangesForLeaving(String keyspaceName, InetAddress endpoint) + private Multimap, InetAddressAndPort> getChangedRangesForLeaving(String keyspaceName, InetAddressAndPort endpoint) { // First get all ranges the leaving endpoint is responsible for Collection> ranges = getRangesForEndpoint(keyspaceName, endpoint); @@ -2669,7 +2800,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (logger.isDebugEnabled()) logger.debug("Node {} ranges [{}]", endpoint, StringUtils.join(ranges, ", ")); - Map, List> currentReplicaEndpoints = new HashMap<>(ranges.size()); + Map, List> currentReplicaEndpoints = new HashMap<>(ranges.size()); // Find (for each range) all nodes that store replicas for these ranges as well TokenMetadata metadata = tokenMetadata.cloneOnlyTokenMap(); // don't do this in the loop! #7758 @@ -2683,7 +2814,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (temp.isMember(endpoint)) temp.removeEndpoint(endpoint); - Multimap, InetAddress> changedRanges = HashMultimap.create(); + Multimap, InetAddressAndPort> changedRanges = HashMultimap.create(); // Go through the ranges and for each range check who will be // storing replicas for these ranges when the leaving endpoint @@ -2692,7 +2823,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // range. for (Range range : ranges) { - Collection newReplicaEndpoints = Keyspace.open(keyspaceName).getReplicationStrategy().calculateNaturalEndpoints(range.right, temp); + Collection newReplicaEndpoints = Keyspace.open(keyspaceName).getReplicationStrategy().calculateNaturalEndpoints(range.right, temp); newReplicaEndpoints.removeAll(currentReplicaEndpoints.get(range)); if (logger.isDebugEnabled()) if (newReplicaEndpoints.isEmpty()) @@ -2705,7 +2836,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return changedRanges; } - public void onJoin(InetAddress endpoint, EndpointState epState) + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) { for (Map.Entry entry : epState.states()) { @@ -2714,7 +2845,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE MigrationManager.instance.scheduleSchemaPull(endpoint, epState); } - public void onAlive(InetAddress endpoint, EndpointState state) + public void onAlive(InetAddressAndPort endpoint, EndpointState state) { MigrationManager.instance.scheduleSchemaPull(endpoint, state); @@ -2722,19 +2853,19 @@ public class StorageService extends NotificationBroadcasterSupport implements IE notifyUp(endpoint); } - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { tokenMetadata.removeEndpoint(endpoint); PendingRangeCalculatorService.instance.update(); } - public void onDead(InetAddress endpoint, EndpointState state) + public void onDead(InetAddressAndPort endpoint, EndpointState state) { MessagingService.instance().convict(endpoint); notifyDown(endpoint); } - public void onRestart(InetAddress endpoint, EndpointState state) + public void onRestart(InetAddressAndPort endpoint, EndpointState state) { // If we have restarted before the node was even marked down, we need to reset the connection pool if (state.isAlive()) @@ -2753,15 +2884,25 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return FileUtils.stringifyFileSize(StorageMetrics.load.getCount()); } + public Map getLoadMapWithPort() + { + return getLoadMap(true); + } + public Map getLoadMap() + { + return getLoadMap(false); + } + + private Map getLoadMap(boolean withPort) { Map map = new HashMap<>(); - for (Map.Entry entry : LoadBroadcaster.instance.getLoadInfo().entrySet()) + for (Map.Entry entry : LoadBroadcaster.instance.getLoadInfo().entrySet()) { - map.put(entry.getKey().getHostAddress(), FileUtils.stringifyFileSize(entry.getValue())); + map.put(entry.getKey().getHostAddress(withPort), FileUtils.stringifyFileSize(entry.getValue())); } // gossiper doesn't see its own updates, so we need to special-case the local node - map.put(FBUtilities.getBroadcastAddress().getHostAddress(), getLoadString()); + map.put(withPort ? FBUtilities.getJustBroadcastAddress().getHostAddress() : FBUtilities.getBroadcastAddressAndPort().toString(), getLoadString()); return map; } @@ -2779,13 +2920,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } @Nullable - public InetAddress getEndpointForHostId(UUID hostId) + public InetAddressAndPort getEndpointForHostId(UUID hostId) { return tokenMetadata.getEndpointForHostId(hostId); } @Nullable - public UUID getHostIdForEndpoint(InetAddress address) + public UUID getHostIdForEndpoint(InetAddressAndPort address) { return tokenMetadata.getHostId(address); } @@ -2794,15 +2935,15 @@ public class StorageService extends NotificationBroadcasterSupport implements IE public List getTokens() { - return getTokens(FBUtilities.getBroadcastAddress()); + return getTokens(FBUtilities.getBroadcastAddressAndPort()); } public List getTokens(String endpoint) throws UnknownHostException { - return getTokens(InetAddress.getByName(endpoint)); + return getTokens(InetAddressAndPort.getByName(endpoint)); } - private List getTokens(InetAddress endpoint) + private List getTokens(InetAddressAndPort endpoint) { List strTokens = new ArrayList<>(); for (Token tok : getTokenMetadata().getTokens(endpoint)) @@ -2820,42 +2961,74 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return Schema.instance.getVersion().toString(); } + @Deprecated public List getLeavingNodes() { - return stringify(tokenMetadata.getLeavingEndpoints()); + return stringify(tokenMetadata.getLeavingEndpoints(), false); } + public List getLeavingNodesWithPort() + { + return stringify(tokenMetadata.getLeavingEndpoints(), true); + } + + @Deprecated public List getMovingNodes() { List endpoints = new ArrayList<>(); - for (Pair node : tokenMetadata.getMovingEndpoints()) + for (Pair node : tokenMetadata.getMovingEndpoints()) { - endpoints.add(node.right.getHostAddress()); + endpoints.add(node.right.address.getHostAddress()); } return endpoints; } + public List getMovingNodesWithPort() + { + List endpoints = new ArrayList<>(); + + for (Pair node : tokenMetadata.getMovingEndpoints()) + { + endpoints.add(node.right.toString()); + } + + return endpoints; + } + + public List getJoiningNodes() { - return stringify(tokenMetadata.getBootstrapTokens().valueSet()); + return stringify(tokenMetadata.getBootstrapTokens().valueSet(), false); + } + + @Deprecated + public List getJoiningNodesWithPort() + { + return stringify(tokenMetadata.getBootstrapTokens().valueSet(), true); } public List getLiveNodes() { - return stringify(Gossiper.instance.getLiveMembers()); + return stringify(Gossiper.instance.getLiveMembers(), false); } - public Set getLiveRingMembers() + @Deprecated + public List getLiveNodesWithPort() + { + return stringify(Gossiper.instance.getLiveMembers(), true); + } + + public Set getLiveRingMembers() { return getLiveRingMembers(false); } - public Set getLiveRingMembers(boolean excludeDeadStates) + public Set getLiveRingMembers(boolean excludeDeadStates) { - Set ret = new HashSet<>(); - for (InetAddress ep : Gossiper.instance.getLiveMembers()) + Set ret = new HashSet<>(); + for (InetAddressAndPort ep : Gossiper.instance.getLiveMembers()) { if (excludeDeadStates) { @@ -2871,9 +3044,15 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } + @Deprecated public List getUnreachableNodes() { - return stringify(Gossiper.instance.getUnreachableMembers()); + return stringify(Gossiper.instance.getUnreachableMembers(), false); + } + + public List getUnreachableNodesWithPort() + { + return stringify(Gossiper.instance.getUnreachableMembers(), true); } public String[] getAllDataFileLocations() @@ -2894,19 +3073,19 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return FileUtils.getCanonicalPath(DatabaseDescriptor.getSavedCachesLocation()); } - private List stringify(Iterable endpoints) + private List stringify(Iterable endpoints, boolean withPort) { List stringEndpoints = new ArrayList<>(); - for (InetAddress ep : endpoints) + for (InetAddressAndPort ep : endpoints) { - stringEndpoints.add(ep.getHostAddress()); + stringEndpoints.add(ep.getHostAddress(withPort)); } return stringEndpoints; } public int getCurrentGenerationNumber() { - return Gossiper.instance.getCurrentGenerationNumber(FBUtilities.getBroadcastAddress()); + return Gossiper.instance.getCurrentGenerationNumber(FBUtilities.getBroadcastAddressAndPort()); } public int forceKeyspaceCleanup(String keyspaceName, String... tables) throws IOException, ExecutionException, InterruptedException @@ -3436,14 +3615,14 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param ep endpoint we are interested in. * @return primary ranges for the specified endpoint. */ - public Collection> getPrimaryRangesForEndpoint(String keyspace, InetAddress ep) + public Collection> getPrimaryRangesForEndpoint(String keyspace, InetAddressAndPort ep) { AbstractReplicationStrategy strategy = Keyspace.open(keyspace).getReplicationStrategy(); Collection> primaryRanges = new HashSet<>(); TokenMetadata metadata = tokenMetadata.cloneOnlyTokenMap(); for (Token token : metadata.sortedTokens()) { - List endpoints = strategy.calculateNaturalEndpoints(token, metadata); + List endpoints = strategy.calculateNaturalEndpoints(token, metadata); if (endpoints.size() > 0 && endpoints.get(0).equals(ep)) primaryRanges.add(new Range<>(metadata.getPredecessor(token), token)); } @@ -3453,23 +3632,23 @@ public class StorageService extends NotificationBroadcasterSupport implements IE /** * Get the "primary ranges" within local DC for the specified keyspace and endpoint. * - * @see #getPrimaryRangesForEndpoint(String, java.net.InetAddress) + * @see #getPrimaryRangesForEndpoint(String, InetAddressAndPort) * @param keyspace Keyspace name to check primary ranges * @param referenceEndpoint endpoint we are interested in. * @return primary ranges within local DC for the specified endpoint. */ - public Collection> getPrimaryRangeForEndpointWithinDC(String keyspace, InetAddress referenceEndpoint) + public Collection> getPrimaryRangeForEndpointWithinDC(String keyspace, InetAddressAndPort referenceEndpoint) { TokenMetadata metadata = tokenMetadata.cloneOnlyTokenMap(); String localDC = DatabaseDescriptor.getEndpointSnitch().getDatacenter(referenceEndpoint); - Collection localDcNodes = metadata.getTopology().getDatacenterEndpoints().get(localDC); + Collection localDcNodes = metadata.getTopology().getDatacenterEndpoints().get(localDC); AbstractReplicationStrategy strategy = Keyspace.open(keyspace).getReplicationStrategy(); Collection> localDCPrimaryRanges = new HashSet<>(); for (Token token : metadata.sortedTokens()) { - List endpoints = strategy.calculateNaturalEndpoints(token, metadata); - for (InetAddress endpoint : endpoints) + List endpoints = strategy.calculateNaturalEndpoints(token, metadata); + for (InetAddressAndPort endpoint : endpoints) { if (localDcNodes.contains(endpoint)) { @@ -3490,7 +3669,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param ep endpoint we are interested in. * @return ranges for the specified endpoint. */ - Collection> getRangesForEndpoint(String keyspaceName, InetAddress ep) + Collection> getRangesForEndpoint(String keyspaceName, InetAddressAndPort ep) { return Keyspace.open(keyspaceName).getReplicationStrategy().getAddressRanges().get(ep); } @@ -3530,6 +3709,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param key key for which we need to find the endpoint * @return the endpoint responsible for this key */ + @Deprecated public List getNaturalEndpoints(String keyspaceName, String cf, String key) { KeyspaceMetadata ksMetaData = Schema.instance.getKeyspaceMetadata(keyspaceName); @@ -3540,12 +3720,32 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (metadata == null) throw new IllegalArgumentException("Unknown table '" + cf + "' in keyspace '" + keyspaceName + "'"); - return getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(metadata.partitionKeyType.fromString(key))); + return getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(metadata.partitionKeyType.fromString(key))).stream().map(i -> i.address).collect(toList()); } + public List getNaturalEndpointsWithPort(String keyspaceName, String cf, String key) + { + KeyspaceMetadata ksMetaData = Schema.instance.getKeyspaceMetadata(keyspaceName); + if (ksMetaData == null) + throw new IllegalArgumentException("Unknown keyspace '" + keyspaceName + "'"); + + TableMetadata metadata = ksMetaData.getTableOrViewNullable(cf); + if (metadata == null) + throw new IllegalArgumentException("Unknown table '" + cf + "' in keyspace '" + keyspaceName + "'"); + + return stringify(getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(metadata.partitionKeyType.fromString(key))), true); + } + + + @Deprecated public List getNaturalEndpoints(String keyspaceName, ByteBuffer key) { - return getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(key)); + return getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(key)).stream().map(i -> i.address).collect(toList()); + } + + public List getNaturalEndpointsWithPort(String keyspaceName, ByteBuffer key) + { + return stringify(getNaturalEndpoints(keyspaceName, tokenMetadata.partitioner.getToken(key)), true); } /** @@ -3556,7 +3756,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param pos position for which we need to find the endpoint * @return the endpoint responsible for this token */ - public List getNaturalEndpoints(String keyspaceName, RingPosition pos) + public List getNaturalEndpoints(String keyspaceName, RingPosition pos) { return Keyspace.open(keyspaceName).getReplicationStrategy().getNaturalEndpoints(pos); } @@ -3564,7 +3764,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE /** * Returns the endpoints currently responsible for storing the token plus pending ones */ - public Iterable getNaturalAndPendingEndpoints(String keyspaceName, Token token) + public Iterable getNaturalAndPendingEndpoints(String keyspaceName, Token token) { return Iterables.concat(getNaturalEndpoints(keyspaceName, token), tokenMetadata.pendingEndpointsFor(token, keyspaceName)); } @@ -3577,14 +3777,14 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param key key for which we need to find the endpoint * @return the endpoint responsible for this key */ - public List getLiveNaturalEndpoints(Keyspace keyspace, ByteBuffer key) + public List getLiveNaturalEndpoints(Keyspace keyspace, ByteBuffer key) { return getLiveNaturalEndpoints(keyspace, tokenMetadata.decorateKey(key)); } - public List getLiveNaturalEndpoints(Keyspace keyspace, RingPosition pos) + public List getLiveNaturalEndpoints(Keyspace keyspace, RingPosition pos) { - List liveEps = new ArrayList<>(); + List liveEps = new ArrayList<>(); getLiveNaturalEndpoints(keyspace, pos, liveEps); return liveEps; } @@ -3597,11 +3797,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param pos position for which we need to find the endpoint * @param liveEps the list of endpoints to mutate */ - public void getLiveNaturalEndpoints(Keyspace keyspace, RingPosition pos, List liveEps) + public void getLiveNaturalEndpoints(Keyspace keyspace, RingPosition pos, List liveEps) { - List endpoints = keyspace.getReplicationStrategy().getNaturalEndpoints(pos); + List endpoints = keyspace.getReplicationStrategy().getNaturalEndpoints(pos); - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { if (FailureDetector.instance.isAlive(endpoint)) liveEps.add(endpoint); @@ -3718,8 +3918,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ private void startLeaving() { + Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS_WITH_PORT, valueFactory.leaving(getLocalTokens())); Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS, valueFactory.leaving(getLocalTokens())); - tokenMetadata.addLeavingEndpoint(FBUtilities.getBroadcastAddress()); + tokenMetadata.addLeavingEndpoint(FBUtilities.getBroadcastAddressAndPort()); PendingRangeCalculatorService.instance.update(); } @@ -3728,7 +3929,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE TokenMetadata metadata = tokenMetadata.cloneAfterAllLeft(); if (operationMode != Mode.LEAVING) { - if (!tokenMetadata.isMember(FBUtilities.getBroadcastAddress())) + if (!tokenMetadata.isMember(FBUtilities.getBroadcastAddressAndPort())) throw new UnsupportedOperationException("local node is not a member of the token ring yet"); if (metadata.getAllEndpoints().size() < 2) throw new UnsupportedOperationException("no other normal nodes in the ring; decommission would be pointless"); @@ -3745,7 +3946,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { PendingRangeCalculatorService.instance.blockUntilFinished(); - String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress()); + String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddressAndPort()); if (operationMode != Mode.LEAVING) // If we're already decommissioning there is no point checking RF/pending ranges { @@ -3772,7 +3973,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE + keyspaceName + " (RF = " + rf + ", N = " + numNodes + ")." + " Perform a forceful decommission to ignore."); } - if (tokenMetadata.getPendingRanges(keyspaceName, FBUtilities.getBroadcastAddress()).size() > 0) + if (tokenMetadata.getPendingRanges(keyspaceName, FBUtilities.getBroadcastAddressAndPort()).size() > 0) throw new UnsupportedOperationException("data is currently moving to this node; unable to leave the ring"); } } @@ -3822,9 +4023,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE private void leaveRing() { SystemKeyspace.setBootstrapState(SystemKeyspace.BootstrapState.NEEDS_BOOTSTRAP); - tokenMetadata.removeEndpoint(FBUtilities.getBroadcastAddress()); + tokenMetadata.removeEndpoint(FBUtilities.getBroadcastAddressAndPort()); PendingRangeCalculatorService.instance.update(); + Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS_WITH_PORT, valueFactory.left(getLocalTokens(),Gossiper.computeExpireTime())); Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS, valueFactory.left(getLocalTokens(),Gossiper.computeExpireTime())); int delay = Math.max(RING_DELAY, Gossiper.intervalInMillis * 2); logger.info("Announcing that I have left the ring for {}ms", delay); @@ -3833,11 +4035,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE private void unbootstrap(Runnable onFinish) throws ExecutionException, InterruptedException { - Map, InetAddress>> rangesToStream = new HashMap<>(); + Map, InetAddressAndPort>> rangesToStream = new HashMap<>(); for (String keyspaceName : Schema.instance.getNonLocalStrategyKeyspaces()) { - Multimap, InetAddress> rangesMM = getChangedRangesForLeaving(keyspaceName, FBUtilities.getBroadcastAddress()); + Multimap, InetAddressAndPort> rangesMM = getChangedRangesForLeaving(keyspaceName, FBUtilities.getBroadcastAddressAndPort()); if (logger.isDebugEnabled()) logger.debug("Ranges needing transfer are [{}]", StringUtils.join(rangesMM.keySet(), ",")); @@ -3878,11 +4080,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ private UUID getPreferredHintsStreamTarget() { - List candidates = new ArrayList<>(StorageService.instance.getTokenMetadata().cloneAfterAllLeft().getAllEndpoints()); - candidates.remove(FBUtilities.getBroadcastAddress()); - for (Iterator iter = candidates.iterator(); iter.hasNext(); ) + List candidates = new ArrayList<>(StorageService.instance.getTokenMetadata().cloneAfterAllLeft().getAllEndpoints()); + candidates.remove(FBUtilities.getBroadcastAddressAndPort()); + for (Iterator iter = candidates.iterator(); iter.hasNext(); ) { - InetAddress address = iter.next(); + InetAddressAndPort address = iter.next(); if (!FailureDetector.instance.isAlive(address)) iter.remove(); } @@ -3895,8 +4097,8 @@ public class StorageService extends NotificationBroadcasterSupport implements IE else { // stream to the closest peer as chosen by the snitch - DatabaseDescriptor.getEndpointSnitch().sortByProximity(FBUtilities.getBroadcastAddress(), candidates); - InetAddress hintsDestinationHost = candidates.get(0); + DatabaseDescriptor.getEndpointSnitch().sortByProximity(FBUtilities.getBroadcastAddressAndPort(), candidates); + InetAddressAndPort hintsDestinationHost = candidates.get(0); return tokenMetadata.getHostId(hintsDestinationHost); } } @@ -3930,7 +4132,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE throw new IOException("target token " + newToken + " is already owned by another node."); // address of the current node - InetAddress localAddress = FBUtilities.getBroadcastAddress(); + InetAddressAndPort localAddress = FBUtilities.getBroadcastAddressAndPort(); // This doesn't make any sense in a vnodes environment. if (getTokenMetadata().getTokens(localAddress).size() > 1) @@ -3949,6 +4151,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE throw new UnsupportedOperationException("data is currently moving to this node; unable to leave the ring"); } + Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS_WITH_PORT, valueFactory.moving(newToken)); Gossiper.instance.addLocalApplicationState(ApplicationState.STATUS, valueFactory.moving(newToken)); setMode(Mode.MOVING, String.format("Moving %s from %s to %s.", localAddress, getLocalTokens().iterator().next(), newToken), true); @@ -3991,7 +4194,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE private void calculateToFromStreams(Collection newTokens, List keyspaceNames) { - InetAddress localAddress = FBUtilities.getBroadcastAddress(); + InetAddressAndPort localAddress = FBUtilities.getBroadcastAddressAndPort(); IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); TokenMetadata tokenMetaCloneAllSettled = tokenMetadata.cloneAfterAllSettled(); // clone to avoid concurrent modification in calculateNaturalEndpoints @@ -4001,7 +4204,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { // replication strategy of the current keyspace AbstractReplicationStrategy strategy = Keyspace.open(keyspace).getReplicationStrategy(); - Multimap> endpointToRanges = strategy.getAddressRanges(); + Multimap> endpointToRanges = strategy.getAddressRanges(); logger.debug("Calculating ranges to stream and request for keyspace {}", keyspace); for (Token newToken : newTokens) @@ -4013,7 +4216,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // ring ranges and endpoints associated with them // this used to determine what nodes should we ping about range data - Multimap, InetAddress> rangeAddresses = strategy.getRangeAddresses(tokenMetaClone); + Multimap, InetAddressAndPort> rangeAddresses = strategy.getRangeAddresses(tokenMetaClone); // calculated parts of the ranges to request/stream from/to nodes in the ring Pair>, Set>> rangesPerKeyspace = calculateStreamAndFetchRanges(currentRanges, updatedRanges); @@ -4022,19 +4225,19 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * In this loop we are going through all ranges "to fetch" and determining * nodes in the ring responsible for data we are interested in */ - Multimap, InetAddress> rangesToFetchWithPreferredEndpoints = ArrayListMultimap.create(); + Multimap, InetAddressAndPort> rangesToFetchWithPreferredEndpoints = ArrayListMultimap.create(); for (Range toFetch : rangesPerKeyspace.right) { for (Range range : rangeAddresses.keySet()) { if (range.contains(toFetch)) { - List endpoints = null; + List endpoints = null; if (useStrictConsistency) { - Set oldEndpoints = Sets.newHashSet(rangeAddresses.get(range)); - Set newEndpoints = Sets.newHashSet(strategy.calculateNaturalEndpoints(toFetch.right, tokenMetaCloneAllSettled)); + Set oldEndpoints = Sets.newHashSet(rangeAddresses.get(range)); + Set newEndpoints = Sets.newHashSet(strategy.calculateNaturalEndpoints(toFetch.right, tokenMetaCloneAllSettled)); //Due to CASSANDRA-5953 we can have a higher RF then we have endpoints. //So we need to be careful to only be strict when endpoints == RF @@ -4061,7 +4264,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } } - Collection addressList = rangesToFetchWithPreferredEndpoints.get(toFetch); + Collection addressList = rangesToFetchWithPreferredEndpoints.get(toFetch); if (addressList == null || addressList.isEmpty()) continue; @@ -4070,7 +4273,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (addressList.size() > 1) throw new IllegalStateException("Multiple strict sources found for " + toFetch); - InetAddress sourceIp = addressList.iterator().next(); + InetAddressAndPort sourceIp = addressList.iterator().next(); if (Gossiper.instance.isEnabled() && !Gossiper.instance.getEndpointStateForEndpoint(sourceIp).isAlive()) throw new RuntimeException("A node required to move the data consistently is down ("+sourceIp+"). If you wish to move the data from a potentially inconsistent replica, restart the node with -Dcassandra.consistent.rangemovement=false"); } @@ -4078,13 +4281,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // calculating endpoints to stream current ranges to if needed // in some situations node will handle current ranges as part of the new ranges - Multimap> endpointRanges = HashMultimap.create(); + Multimap> endpointRanges = HashMultimap.create(); for (Range toStream : rangesPerKeyspace.left) { - Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(toStream.right, tokenMetaClone)); - Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(toStream.right, tokenMetaCloneAllSettled)); + Set currentEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(toStream.right, tokenMetaClone)); + Set newEndpoints = ImmutableSet.copyOf(strategy.calculateNaturalEndpoints(toStream.right, tokenMetaCloneAllSettled)); logger.debug("Range: {} Current endpoints: {} New endpoints: {}", toStream, currentEndpoints, newEndpoints); - for (InetAddress address : Sets.difference(newEndpoints, currentEndpoints)) + for (InetAddressAndPort address : Sets.difference(newEndpoints, currentEndpoints)) { logger.debug("Range {} has new owner {}", toStream, address); endpointRanges.put(address, toStream); @@ -4092,19 +4295,19 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } // stream ranges - for (InetAddress address : endpointRanges.keySet()) + for (InetAddressAndPort address : endpointRanges.keySet()) { logger.debug("Will stream range {} of keyspace {} to endpoint {}", endpointRanges.get(address), keyspace, address); - InetAddress preferred = SystemKeyspace.getPreferredIP(address); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(address); streamPlan.transferRanges(address, preferred, keyspace, endpointRanges.get(address)); } // stream requests - Multimap> workMap = RangeStreamer.getWorkMap(rangesToFetchWithPreferredEndpoints, keyspace, FailureDetector.instance, useStrictConsistency); - for (InetAddress address : workMap.keySet()) + Multimap> workMap = RangeStreamer.getWorkMap(rangesToFetchWithPreferredEndpoints, keyspace, FailureDetector.instance, useStrictConsistency); + for (InetAddressAndPort address : workMap.keySet()) { logger.debug("Will request range {} of keyspace {} from endpoint {}", workMap.get(address), keyspace, address); - InetAddress preferred = SystemKeyspace.getPreferredIP(address); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(address); streamPlan.requestRanges(address, preferred, keyspace, workMap.get(address)); } @@ -4124,18 +4327,39 @@ public class StorageService extends NotificationBroadcasterSupport implements IE } } + public String getRemovalStatus() + { + return getRemovalStatus(false); + } + + public String getRemovalStatusWithPort() + { + return getRemovalStatus(true); + } + /** * Get the status of a token removal. */ - public String getRemovalStatus() + private String getRemovalStatus(boolean withPort) { if (removingNode == null) { return "No token removals in process."; } + + Collection toFormat = replicatingNodes; + if (!withPort) + { + toFormat = new ArrayList(replicatingNodes.size()); + for (InetAddressAndPort node : replicatingNodes) + { + toFormat.add(node.toString(false)); + } + } + return String.format("Removing token (%s). Waiting for replication confirmation from [%s].", tokenMetadata.getToken(removingNode), - StringUtils.join(replicatingNodes, ",")); + StringUtils.join(toFormat, ",")); } /** @@ -4148,7 +4372,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (!replicatingNodes.isEmpty() || tokenMetadata.getSizeOfLeavingEndpoints() > 0) { logger.warn("Removal not confirmed for for {}", StringUtils.join(this.replicatingNodes, ",")); - for (InetAddress endpoint : tokenMetadata.getLeavingEndpoints()) + for (InetAddressAndPort endpoint : tokenMetadata.getLeavingEndpoints()) { UUID hostId = tokenMetadata.getHostId(endpoint); Gossiper.instance.advertiseTokenRemoved(endpoint, hostId); @@ -4174,10 +4398,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE */ public void removeNode(String hostIdString) { - InetAddress myAddress = FBUtilities.getBroadcastAddress(); + InetAddressAndPort myAddress = FBUtilities.getBroadcastAddressAndPort(); UUID localHostId = tokenMetadata.getHostId(myAddress); UUID hostId = UUID.fromString(hostIdString); - InetAddress endpoint = tokenMetadata.getEndpointForHostId(hostId); + InetAddressAndPort endpoint = tokenMetadata.getEndpointForHostId(hostId); if (endpoint == null) throw new UnsupportedOperationException("Host ID not found."); @@ -4209,9 +4433,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // get all ranges that change ownership (that is, a node needs // to take responsibility for new range) - Multimap, InetAddress> changedRanges = getChangedRangesForLeaving(keyspaceName, endpoint); + Multimap, InetAddressAndPort> changedRanges = getChangedRangesForLeaving(keyspaceName, endpoint); IFailureDetector failureDetector = FailureDetector.instance; - for (InetAddress ep : changedRanges.values()) + for (InetAddressAndPort ep : changedRanges.values()) { if (failureDetector.isAlive(ep)) replicatingNodes.add(ep); @@ -4246,7 +4470,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE removingNode = null; } - public void confirmReplication(InetAddress node) + public void confirmReplication(InetAddressAndPort node) { // replicatingNodes can be empty in the case where this node used to be a removal coordinator, // but restarted before all 'replication finished' messages arrived. In that case, we'll @@ -4540,12 +4764,30 @@ public class StorageService extends NotificationBroadcasterSupport implements IE Map nodeMap = new LinkedHashMap<>(); for (Map.Entry entry : tokenMap.entrySet()) { - InetAddress endpoint = tokenMetadata.getEndpoint(entry.getKey()); + InetAddressAndPort endpoint = tokenMetadata.getEndpoint(entry.getKey()); Float tokenOwnership = entry.getValue(); if (nodeMap.containsKey(endpoint)) - nodeMap.put(endpoint, nodeMap.get(endpoint) + tokenOwnership); + nodeMap.put(endpoint.address, nodeMap.get(endpoint) + tokenOwnership); else - nodeMap.put(endpoint, tokenOwnership); + nodeMap.put(endpoint.address, tokenOwnership); + } + return nodeMap; + } + + public Map getOwnershipWithPort() + { + List sortedTokens = tokenMetadata.sortedTokens(); + // describeOwnership returns tokens in an unspecified order, let's re-order them + Map tokenMap = new TreeMap(tokenMetadata.partitioner.describeOwnership(sortedTokens)); + Map nodeMap = new LinkedHashMap<>(); + for (Map.Entry entry : tokenMap.entrySet()) + { + InetAddressAndPort endpoint = tokenMetadata.getEndpoint(entry.getKey()); + Float tokenOwnership = entry.getValue(); + if (nodeMap.containsKey(endpoint)) + nodeMap.put(endpoint.toString(), nodeMap.get(endpoint) + tokenOwnership); + else + nodeMap.put(endpoint.toString(), tokenOwnership); } return nodeMap; } @@ -4559,7 +4801,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * * @throws IllegalStateException when node is not configured properly. */ - public LinkedHashMap effectiveOwnership(String keyspace) throws IllegalStateException + private LinkedHashMap getEffectiveOwnership(String keyspace) { AbstractReplicationStrategy strategy; if (keyspace != null) @@ -4599,22 +4841,22 @@ public class StorageService extends NotificationBroadcasterSupport implements IE TokenMetadata metadata = tokenMetadata.cloneOnlyTokenMap(); - Collection> endpointsGroupedByDc = new ArrayList<>(); + Collection> endpointsGroupedByDc = new ArrayList<>(); // mapping of dc's to nodes, use sorted map so that we get dcs sorted - SortedMap> sortedDcsToEndpoints = new TreeMap<>(); + SortedMap> sortedDcsToEndpoints = new TreeMap<>(); sortedDcsToEndpoints.putAll(metadata.getTopology().getDatacenterEndpoints().asMap()); - for (Collection endpoints : sortedDcsToEndpoints.values()) + for (Collection endpoints : sortedDcsToEndpoints.values()) endpointsGroupedByDc.add(endpoints); Map tokenOwnership = tokenMetadata.partitioner.describeOwnership(tokenMetadata.sortedTokens()); - LinkedHashMap finalOwnership = Maps.newLinkedHashMap(); + LinkedHashMap finalOwnership = Maps.newLinkedHashMap(); - Multimap> endpointToRanges = strategy.getAddressRanges(); + Multimap> endpointToRanges = strategy.getAddressRanges(); // calculate ownership per dc - for (Collection endpoints : endpointsGroupedByDc) + for (Collection endpoints : endpointsGroupedByDc) { // calculate the ownership with replication and add the endpoint to the final ownership map - for (InetAddress endpoint : endpoints) + for (InetAddressAndPort endpoint : endpoints) { float ownership = 0.0f; for (Range range : endpointToRanges.get(endpoint)) @@ -4628,6 +4870,22 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return finalOwnership; } + public LinkedHashMap effectiveOwnership(String keyspace) throws IllegalStateException + { + LinkedHashMap result = getEffectiveOwnership(keyspace); + LinkedHashMap asInets = new LinkedHashMap<>(); + result.entrySet().stream().forEachOrdered(entry -> asInets.put(entry.getKey().address, entry.getValue())); + return asInets; + } + + public LinkedHashMap effectiveOwnershipWithPort(String keyspace) throws IllegalStateException + { + LinkedHashMap result = getEffectiveOwnership(keyspace); + LinkedHashMap asStrings = new LinkedHashMap<>(); + result.entrySet().stream().forEachOrdered(entry -> asStrings.put(entry.getKey().toString(), entry.getValue())); + return asStrings; + } + public List getKeyspaces() { List keyspaceNamesList = new ArrayList<>(Schema.instance.getKeyspaces()); @@ -4644,17 +4902,17 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return Collections.unmodifiableList(Schema.instance.getNonLocalStrategyKeyspaces()); } - public Map getViewBuildStatuses(String keyspace, String view) + public Map getViewBuildStatuses(String keyspace, String view, boolean withPort) { Map coreViewStatus = SystemDistributedKeyspace.viewStatus(keyspace, view); - Map hostIdToEndpoint = tokenMetadata.getEndpointToHostIdMapForReading(); + Map hostIdToEndpoint = tokenMetadata.getEndpointToHostIdMapForReading(); Map result = new HashMap<>(); - for (Map.Entry entry : hostIdToEndpoint.entrySet()) + for (Map.Entry entry : hostIdToEndpoint.entrySet()) { UUID hostId = entry.getValue(); - InetAddress endpoint = entry.getKey(); - result.put(endpoint.toString(), + InetAddressAndPort endpoint = entry.getKey(); + result.put(endpoint.toString(withPort), coreViewStatus.containsKey(hostId) ? coreViewStatus.get(hostId) : "UNKNOWN"); @@ -4663,6 +4921,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return Collections.unmodifiableMap(result); } + public Map getViewBuildStatuses(String keyspace, String view) + { + return getViewBuildStatuses(keyspace, view, false); + } + + public Map getViewBuildStatusesWithPort(String keyspace, String view) + { + return getViewBuildStatuses(keyspace, view, true); + } + public void setDynamicUpdateInterval(int dynamicUpdateInterval) { if (DatabaseDescriptor.getEndpointSnitch() instanceof DynamicEndpointSnitch) @@ -4755,27 +5023,27 @@ public class StorageService extends NotificationBroadcasterSupport implements IE * @param rangesToStreamByKeyspace keyspaces and data ranges with endpoints included for each * @return async Future for whether stream was success */ - private Future streamRanges(Map, InetAddress>> rangesToStreamByKeyspace) + private Future streamRanges(Map, InetAddressAndPort>> rangesToStreamByKeyspace) { // First, we build a list of ranges to stream to each host, per table - Map>>> sessionsToStreamByKeyspace = new HashMap<>(); + Map>>> sessionsToStreamByKeyspace = new HashMap<>(); - for (Map.Entry, InetAddress>> entry : rangesToStreamByKeyspace.entrySet()) + for (Map.Entry, InetAddressAndPort>> entry : rangesToStreamByKeyspace.entrySet()) { String keyspace = entry.getKey(); - Multimap, InetAddress> rangesWithEndpoints = entry.getValue(); + Multimap, InetAddressAndPort> rangesWithEndpoints = entry.getValue(); if (rangesWithEndpoints.isEmpty()) continue; - Map>> transferredRangePerKeyspace = SystemKeyspace.getTransferredRanges("Unbootstrap", - keyspace, - StorageService.instance.getTokenMetadata().partitioner); - Map>> rangesPerEndpoint = new HashMap<>(); - for (Map.Entry, InetAddress> endPointEntry : rangesWithEndpoints.entries()) + Map>> transferredRangePerKeyspace = SystemKeyspace.getTransferredRanges("Unbootstrap", + keyspace, + StorageService.instance.getTokenMetadata().partitioner); + Map>> rangesPerEndpoint = new HashMap<>(); + for (Map.Entry, InetAddressAndPort> endPointEntry : rangesWithEndpoints.entries()) { Range range = endPointEntry.getKey(); - InetAddress endpoint = endPointEntry.getValue(); + InetAddressAndPort endpoint = endPointEntry.getValue(); Set> transferredRanges = transferredRangePerKeyspace.get(endpoint); if (transferredRanges != null && transferredRanges.contains(range)) @@ -4801,16 +5069,16 @@ public class StorageService extends NotificationBroadcasterSupport implements IE // Vinculate StreamStateStore to current StreamPlan to update transferred ranges per StreamSession streamPlan.listeners(streamStateStore); - for (Map.Entry>>> entry : sessionsToStreamByKeyspace.entrySet()) + for (Map.Entry>>> entry : sessionsToStreamByKeyspace.entrySet()) { String keyspaceName = entry.getKey(); - Map>> rangesPerEndpoint = entry.getValue(); + Map>> rangesPerEndpoint = entry.getValue(); - for (Map.Entry>> rangesEntry : rangesPerEndpoint.entrySet()) + for (Map.Entry>> rangesEntry : rangesPerEndpoint.entrySet()) { List> ranges = rangesEntry.getValue(); - InetAddress newEndpoint = rangesEntry.getKey(); - InetAddress preferred = SystemKeyspace.getPreferredIP(newEndpoint); + InetAddressAndPort newEndpoint = rangesEntry.getKey(); + InetAddressAndPort preferred = SystemKeyspace.getPreferredIP(newEndpoint); // TODO each call to transferRanges re-flushes, this is potentially a lot of waste streamPlan.transferRanges(newEndpoint, preferred, keyspaceName, ranges); @@ -4905,10 +5173,10 @@ public class StorageService extends NotificationBroadcasterSupport implements IE this.keyspace = keyspace; try { - for (Map.Entry, List> entry : StorageService.instance.getRangeToAddressMap(keyspace).entrySet()) + for (Map.Entry, List> entry : StorageService.instance.getRangeToAddressMap(keyspace).entrySet()) { Range range = entry.getKey(); - for (InetAddress endpoint : entry.getValue()) + for (InetAddressAndPort endpoint : entry.getValue()) addRangeForEndpoint(range, endpoint); } } @@ -4948,7 +5216,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE List keys = new ArrayList<>(); for (Keyspace keyspace : Keyspace.nonLocalStrategy()) { - for (Range range : getPrimaryRangesForEndpoint(keyspace.getName(), FBUtilities.getBroadcastAddress())) + for (Range range : getPrimaryRangesForEndpoint(keyspace.getName(), FBUtilities.getBroadcastAddressAndPort())) keys.addAll(keySamples(keyspace.getColumnFamilyStores(), range)); } diff --git a/src/java/org/apache/cassandra/service/StorageServiceMBean.java b/src/java/org/apache/cassandra/service/StorageServiceMBean.java index 48e1b2fc78..29b45b4bc4 100644 --- a/src/java/org/apache/cassandra/service/StorageServiceMBean.java +++ b/src/java/org/apache/cassandra/service/StorageServiceMBean.java @@ -37,7 +37,8 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return set of IP addresses, as Strings */ - public List getLiveNodes(); + @Deprecated public List getLiveNodes(); + public List getLiveNodesWithPort(); /** * Retrieve the list of unreachable nodes in the cluster, as determined @@ -45,28 +46,32 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return set of IP addresses, as Strings */ - public List getUnreachableNodes(); + @Deprecated public List getUnreachableNodes(); + public List getUnreachableNodesWithPort(); /** * Retrieve the list of nodes currently bootstrapping into the ring. * * @return set of IP addresses, as Strings */ - public List getJoiningNodes(); + @Deprecated public List getJoiningNodes(); + public List getJoiningNodesWithPort(); /** * Retrieve the list of nodes currently leaving the ring. * * @return set of IP addresses, as Strings */ - public List getLeavingNodes(); + @Deprecated public List getLeavingNodes(); + public List getLeavingNodesWithPort(); /** * Retrieve the list of nodes currently moving in the ring. * * @return set of IP addresses, as Strings */ - public List getMovingNodes(); + @Deprecated public List getMovingNodes(); + public List getMovingNodesWithPort(); /** * Fetch string representations of the tokens for this node. @@ -120,7 +125,8 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return mapping of ranges to end points */ - public Map, List> getRangeToEndpointMap(String keyspace); + @Deprecated public Map, List> getRangeToEndpointMap(String keyspace); + public Map, List> getRangeToEndpointWithPortMap(String keyspace); /** * Retrieve a map of range to rpc addresses that describe the ring topology @@ -128,7 +134,8 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return mapping of ranges to rpc addresses */ - public Map, List> getRangeToRpcaddressMap(String keyspace); + @Deprecated public Map, List> getRangeToRpcaddressMap(String keyspace); + public Map, List> getRangeToNativeaddressWithPortMap(String keyspace); /** * The same as {@code describeRing(String)} but converts TokenRange to the String for JMX compatibility @@ -137,14 +144,16 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return a List of TokenRange(s) converted to String for the given keyspace */ - public List describeRingJMX(String keyspace) throws IOException; + @Deprecated public List describeRingJMX(String keyspace) throws IOException; + public List describeRingWithPortJMX(String keyspace) throws IOException; /** * Retrieve a map of pending ranges to endpoints that describe the ring topology * @param keyspace the keyspace to get the pending range map for. * @return a map of pending ranges to endpoints */ - public Map, List> getPendingRangeToEndpointMap(String keyspace); + @Deprecated public Map, List> getPendingRangeToEndpointMap(String keyspace); + public Map, List> getPendingRangeToEndpointWithPortMap(String keyspace); /** * Retrieve a map of tokens to endpoints, including the bootstrapping @@ -152,7 +161,8 @@ public interface StorageServiceMBean extends NotificationEmitter * * @return a map of tokens to endpoints in ascending order */ - public Map getTokenToEndpointMap(); + @Deprecated public Map getTokenToEndpointMap(); + public Map getTokenToEndpointWithPortMap(); /** Retrieve this hosts unique ID */ public String getLocalHostId(); @@ -162,16 +172,19 @@ public interface StorageServiceMBean extends NotificationEmitter public Map getHostIdMap(); /** Retrieve the mapping of endpoint to host ID */ - public Map getEndpointToHostId(); + @Deprecated public Map getEndpointToHostId(); + public Map getEndpointWithPortToHostId(); /** Retrieve the mapping of host ID to endpoint */ - public Map getHostIdToEndpoint(); + @Deprecated public Map getHostIdToEndpoint(); + public Map getHostIdToEndpointWithPort(); /** Human-readable load value */ public String getLoadString(); /** Human-readable load value. Keys are IP addresses. */ - public Map getLoadMap(); + @Deprecated public Map getLoadMap(); + public Map getLoadMapWithPort(); /** * Return the generation value for this node. @@ -189,8 +202,10 @@ public interface StorageServiceMBean extends NotificationEmitter * @param key - key for which we need to find the endpoint return value - * the endpoint responsible for this key */ - public List getNaturalEndpoints(String keyspaceName, String cf, String key); - public List getNaturalEndpoints(String keyspaceName, ByteBuffer key); + @Deprecated public List getNaturalEndpoints(String keyspaceName, String cf, String key); + public List getNaturalEndpointsWithPort(String keyspaceName, String cf, String key); + @Deprecated public List getNaturalEndpoints(String keyspaceName, ByteBuffer key); + public List getNaturalEndpointsWithPort(String keysapceName, ByteBuffer key); /** * @deprecated use {@link #takeSnapshot(String tag, Map options, String... entities)} instead. @@ -353,7 +368,8 @@ public interface StorageServiceMBean extends NotificationEmitter /** * Get the status of a token removal. */ - public String getRemovalStatus(); + @Deprecated public String getRemovalStatus(); + public String getRemovalStatusWithPort(); /** * Force a remove operation to finish. @@ -408,7 +424,8 @@ public interface StorageServiceMBean extends NotificationEmitter * given a list of tokens (representing the nodes in the cluster), returns * a mapping from {@code "token -> %age of cluster owned by that token"} */ - public Map getOwnership(); + @Deprecated public Map getOwnership(); + public Map getOwnershipWithPort(); /** * Effective ownership is % of the data each node owns given the keyspace @@ -417,7 +434,8 @@ public interface StorageServiceMBean extends NotificationEmitter * in the cluster have the same replication strategies and if yes then we will * use the first else a empty Map is returned. */ - public Map effectiveOwnership(String keyspace) throws IllegalStateException; + @Deprecated public Map effectiveOwnership(String keyspace) throws IllegalStateException; + public Map effectiveOwnershipWithPort(String keyspace) throws IllegalStateException; public List getKeyspaces(); @@ -425,7 +443,8 @@ public interface StorageServiceMBean extends NotificationEmitter public List getNonLocalStrategyKeyspaces(); - public Map getViewBuildStatuses(String keyspace, String view); + @Deprecated public Map getViewBuildStatuses(String keyspace, String view); + public Map getViewBuildStatusesWithPort(String keyspace, String view); /** * Change endpointsnitch class and dynamic-ness (and dynamic attributes) at runtime. diff --git a/src/java/org/apache/cassandra/service/TokenRange.java b/src/java/org/apache/cassandra/service/TokenRange.java index 0e46910294..a1f9aee1d6 100644 --- a/src/java/org/apache/cassandra/service/TokenRange.java +++ b/src/java/org/apache/cassandra/service/TokenRange.java @@ -17,13 +17,13 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.*; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Holds token range informations for the sake of {@link StorageService#describeRing}. @@ -54,13 +54,13 @@ public class TokenRange return tokenFactory.toString(tk); } - public static TokenRange create(Token.TokenFactory tokenFactory, Range range, List endpoints) + public static TokenRange create(Token.TokenFactory tokenFactory, Range range, List endpoints, boolean withPorts) { List details = new ArrayList<>(endpoints.size()); IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch(); - for (InetAddress ep : endpoints) + for (InetAddressAndPort ep : endpoints) details.add(new EndpointDetails(ep, - StorageService.instance.getRpcaddress(ep), + StorageService.instance.getNativeaddress(ep, withPorts), snitch.getDatacenter(ep), snitch.getRack(ep))); return new TokenRange(tokenFactory, range, details); @@ -68,6 +68,11 @@ public class TokenRange @Override public String toString() + { + return toString(false); + } + + public String toString(boolean withPorts) { StringBuilder sb = new StringBuilder("TokenRange("); @@ -76,44 +81,59 @@ public class TokenRange List hosts = new ArrayList<>(endpoints.size()); List rpcs = new ArrayList<>(endpoints.size()); + List endpointDetails = new ArrayList<>(endpoints.size()); for (EndpointDetails ep : endpoints) { - hosts.add(ep.host.getHostAddress()); - rpcs.add(ep.rpcAddress); + hosts.add(ep.host.getHostAddress(withPorts)); + rpcs.add(ep.nativeAddress); + endpointDetails.add(ep.toString(withPorts)); } - sb.append("endpoints:").append(hosts); - sb.append("rpc_endpoints:").append(rpcs); - sb.append("endpoint_details:").append(endpoints); - + if (withPorts) + { + sb.append(", endpoints:").append(hosts); + sb.append(", rpc_endpoints:").append(rpcs); + sb.append(", endpoint_details:").append(endpointDetails); + } + else + { + sb.append("endpoints:").append(hosts); + sb.append("rpc_endpoints:").append(rpcs); + sb.append("endpoint_details:").append(endpointDetails); + } sb.append(")"); return sb.toString(); } public static class EndpointDetails { - public final InetAddress host; - public final String rpcAddress; + public final InetAddressAndPort host; + public final String nativeAddress; public final String datacenter; public final String rack; - private EndpointDetails(InetAddress host, String rpcAddress, String datacenter, String rack) + private EndpointDetails(InetAddressAndPort host, String nativeAddress, String datacenter, String rack) { // dc and rack can be null, but host shouldn't assert host != null; this.host = host; - this.rpcAddress = rpcAddress; + this.nativeAddress = nativeAddress; this.datacenter = datacenter; this.rack = rack; } @Override public String toString() + { + return toString(false); + } + + public String toString(boolean withPorts) { // Format matters for backward compatibility with describeRing() String dcStr = datacenter == null ? "" : String.format(", datacenter:%s", datacenter); String rackStr = rack == null ? "" : String.format(", rack:%s", rack); - return String.format("EndpointDetails(host:%s%s%s)", host.getHostAddress(), dcStr, rackStr); + return String.format("EndpointDetails(host:%s%s%s)", host.getHostAddress(withPorts), dcStr, rackStr); } } } diff --git a/src/java/org/apache/cassandra/service/WriteResponseHandler.java b/src/java/org/apache/cassandra/service/WriteResponseHandler.java index 55ca5aaa35..65efeff4cc 100644 --- a/src/java/org/apache/cassandra/service/WriteResponseHandler.java +++ b/src/java/org/apache/cassandra/service/WriteResponseHandler.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -27,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.WriteType; @@ -42,8 +42,8 @@ public class WriteResponseHandler extends AbstractWriteResponseHandler private static final AtomicIntegerFieldUpdater responsesUpdater = AtomicIntegerFieldUpdater.newUpdater(WriteResponseHandler.class, "responses"); - public WriteResponseHandler(Collection writeEndpoints, - Collection pendingEndpoints, + public WriteResponseHandler(Collection writeEndpoints, + Collection pendingEndpoints, ConsistencyLevel consistencyLevel, Keyspace keyspace, Runnable callback, @@ -54,12 +54,12 @@ public class WriteResponseHandler extends AbstractWriteResponseHandler responses = totalBlockFor(); } - public WriteResponseHandler(InetAddress endpoint, WriteType writeType, Runnable callback, long queryStartNanoTime) + public WriteResponseHandler(InetAddressAndPort endpoint, WriteType writeType, Runnable callback, long queryStartNanoTime) { - this(Arrays.asList(endpoint), Collections.emptyList(), ConsistencyLevel.ONE, null, callback, writeType, queryStartNanoTime); + this(Arrays.asList(endpoint), Collections.emptyList(), ConsistencyLevel.ONE, null, callback, writeType, queryStartNanoTime); } - public WriteResponseHandler(InetAddress endpoint, WriteType writeType, long queryStartNanoTime) + public WriteResponseHandler(InetAddressAndPort endpoint, WriteType writeType, long queryStartNanoTime) { this(endpoint, writeType, null, queryStartNanoTime); } diff --git a/src/java/org/apache/cassandra/service/paxos/PrepareCallback.java b/src/java/org/apache/cassandra/service/paxos/PrepareCallback.java index 381c498025..ed70e964c0 100644 --- a/src/java/org/apache/cassandra/service/paxos/PrepareCallback.java +++ b/src/java/org/apache/cassandra/service/paxos/PrepareCallback.java @@ -21,7 +21,6 @@ package org.apache.cassandra.service.paxos; */ -import java.net.InetAddress; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -29,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.DecoratedKey; @@ -48,7 +48,7 @@ public class PrepareCallback extends AbstractPaxosCallback public Commit mostRecentInProgressCommit; public Commit mostRecentInProgressCommitWithUpdate; - private final Map commitsByReplica = new ConcurrentHashMap(); + private final Map commitsByReplica = new ConcurrentHashMap<>(); public PrepareCallback(DecoratedKey key, TableMetadata metadata, int targets, ConsistencyLevel consistency, long queryStartNanoTime) { @@ -90,7 +90,7 @@ public class PrepareCallback extends AbstractPaxosCallback latch.countDown(); } - public Iterable replicasMissingMostRecentCommit(TableMetadata metadata, int nowInSec) + public Iterable replicasMissingMostRecentCommit(TableMetadata metadata, int nowInSec) { // In general, we need every replicas that have answered to the prepare (a quorum) to agree on the MRC (see // coment in StorageProxy.beginAndRepairPaxos(), but basically we need to make sure at least a quorum of nodes @@ -105,9 +105,9 @@ public class PrepareCallback extends AbstractPaxosCallback if (UUIDGen.unixTimestampInSec(mostRecentCommit.ballot) + paxosTtlSec < nowInSec) return Collections.emptySet(); - return Iterables.filter(commitsByReplica.keySet(), new Predicate() + return Iterables.filter(commitsByReplica.keySet(), new Predicate() { - public boolean apply(InetAddress inetAddress) + public boolean apply(InetAddressAndPort inetAddress) { return (!commitsByReplica.get(inetAddress).ballot.equals(mostRecentCommit.ballot)); } diff --git a/src/java/org/apache/cassandra/streaming/ProgressInfo.java b/src/java/org/apache/cassandra/streaming/ProgressInfo.java index fdd3e97bb8..2334599404 100644 --- a/src/java/org/apache/cassandra/streaming/ProgressInfo.java +++ b/src/java/org/apache/cassandra/streaming/ProgressInfo.java @@ -18,10 +18,11 @@ package org.apache.cassandra.streaming; import java.io.Serializable; -import java.net.InetAddress; import com.google.common.base.Objects; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * ProgressInfo contains file transfer progress. */ @@ -48,14 +49,14 @@ public class ProgressInfo implements Serializable } } - public final InetAddress peer; + public final InetAddressAndPort peer; public final int sessionIndex; public final String fileName; public final Direction direction; public final long currentBytes; public final long totalBytes; - public ProgressInfo(InetAddress peer, int sessionIndex, String fileName, Direction direction, long currentBytes, long totalBytes) + public ProgressInfo(InetAddressAndPort peer, int sessionIndex, String fileName, Direction direction, long currentBytes, long totalBytes) { assert totalBytes > 0; @@ -101,6 +102,11 @@ public class ProgressInfo implements Serializable @Override public String toString() + { + return toString(false); + } + + public String toString(boolean withPorts) { StringBuilder sb = new StringBuilder(fileName); sb.append(" ").append(currentBytes); @@ -108,7 +114,7 @@ public class ProgressInfo implements Serializable sb.append("(").append(currentBytes*100/totalBytes).append("%) "); sb.append(direction == Direction.OUT ? "sent to " : "received from "); sb.append("idx:").append(sessionIndex); - sb.append(peer); + sb.append(peer.toString(withPorts)); return sb.toString(); } } diff --git a/src/java/org/apache/cassandra/streaming/SessionInfo.java b/src/java/org/apache/cassandra/streaming/SessionInfo.java index 1521614e0b..bbca7531c2 100644 --- a/src/java/org/apache/cassandra/streaming/SessionInfo.java +++ b/src/java/org/apache/cassandra/streaming/SessionInfo.java @@ -18,7 +18,6 @@ package org.apache.cassandra.streaming; import java.io.Serializable; -import java.net.InetAddress; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -27,6 +26,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.FBUtilities; /** @@ -34,9 +34,9 @@ import org.apache.cassandra.utils.FBUtilities; */ public final class SessionInfo implements Serializable { - public final InetAddress peer; + public final InetAddressAndPort peer; public final int sessionIndex; - public final InetAddress connecting; + public final InetAddressAndPort connecting; /** Immutable collection of receiving summaries */ public final Collection receivingSummaries; /** Immutable collection of sending summaries*/ @@ -47,9 +47,9 @@ public final class SessionInfo implements Serializable private final Map receivingFiles; private final Map sendingFiles; - public SessionInfo(InetAddress peer, + public SessionInfo(InetAddressAndPort peer, int sessionIndex, - InetAddress connecting, + InetAddressAndPort connecting, Collection receivingSummaries, Collection sendingSummaries, StreamSession.State state) @@ -195,6 +195,6 @@ public final class SessionInfo implements Serializable public SessionSummary createSummary() { - return new SessionSummary(FBUtilities.getBroadcastAddress(), peer, receivingSummaries, sendingSummaries); + return new SessionSummary(FBUtilities.getBroadcastAddressAndPort(), peer, receivingSummaries, sendingSummaries); } } diff --git a/src/java/org/apache/cassandra/streaming/SessionSummary.java b/src/java/org/apache/cassandra/streaming/SessionSummary.java index d52c2ca360..cf63a572cd 100644 --- a/src/java/org/apache/cassandra/streaming/SessionSummary.java +++ b/src/java/org/apache/cassandra/streaming/SessionSummary.java @@ -19,7 +19,6 @@ package org.apache.cassandra.streaming; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -28,19 +27,19 @@ import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.serializers.InetAddressSerializer; -import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.net.CompactEndpointSerializationHelper; public class SessionSummary { - public final InetAddress coordinator; - public final InetAddress peer; + public final InetAddressAndPort coordinator; + public final InetAddressAndPort peer; /** Immutable collection of receiving summaries */ public final Collection receivingSummaries; /** Immutable collection of sending summaries*/ public final Collection sendingSummaries; - public SessionSummary(InetAddress coordinator, InetAddress peer, + public SessionSummary(InetAddressAndPort coordinator, InetAddressAndPort peer, Collection receivingSummaries, Collection sendingSummaries) { @@ -81,8 +80,8 @@ public class SessionSummary { public void serialize(SessionSummary summary, DataOutputPlus out, int version) throws IOException { - ByteBufferUtil.writeWithLength(InetAddressSerializer.instance.serialize(summary.coordinator), out); - ByteBufferUtil.writeWithLength(InetAddressSerializer.instance.serialize(summary.peer), out); + CompactEndpointSerializationHelper.instance.serialize(summary.coordinator, out, version); + CompactEndpointSerializationHelper.instance.serialize(summary.peer, out, version); out.writeInt(summary.receivingSummaries.size()); for (StreamSummary streamSummary: summary.receivingSummaries) @@ -99,8 +98,8 @@ public class SessionSummary public SessionSummary deserialize(DataInputPlus in, int version) throws IOException { - InetAddress coordinator = InetAddressSerializer.instance.deserialize(ByteBufferUtil.readWithLength(in)); - InetAddress peer = InetAddressSerializer.instance.deserialize(ByteBufferUtil.readWithLength(in)); + InetAddressAndPort coordinator = CompactEndpointSerializationHelper.instance.deserialize(in, version); + InetAddressAndPort peer = CompactEndpointSerializationHelper.instance.deserialize(in, version); int numRcvd = in.readInt(); List receivingSummaries = new ArrayList<>(numRcvd); @@ -122,8 +121,8 @@ public class SessionSummary public long serializedSize(SessionSummary summary, int version) { long size = 0; - size += ByteBufferUtil.serializedSizeWithLength(InetAddressSerializer.instance.serialize(summary.coordinator)); - size += ByteBufferUtil.serializedSizeWithLength(InetAddressSerializer.instance.serialize(summary.peer)); + size += CompactEndpointSerializationHelper.instance.serializedSize(summary.coordinator, version); + size += CompactEndpointSerializationHelper.instance.serializedSize(summary.peer, version); size += TypeSizes.sizeof(summary.receivingSummaries.size()); for (StreamSummary streamSummary: summary.receivingSummaries) diff --git a/src/java/org/apache/cassandra/streaming/StreamCoordinator.java b/src/java/org/apache/cassandra/streaming/StreamCoordinator.java index bb8c702bb1..a22e07d5d6 100644 --- a/src/java/org/apache/cassandra/streaming/StreamCoordinator.java +++ b/src/java/org/apache/cassandra/streaming/StreamCoordinator.java @@ -17,13 +17,13 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.concurrent.DebuggableThreadPoolExecutor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.FBUtilities; /** @@ -45,7 +45,7 @@ public class StreamCoordinator FBUtilities.getAvailableProcessors()); private final boolean connectSequentially; - private Map peerSessions = new HashMap<>(); + private Map peerSessions = new HashMap<>(); private final int connectionsPerHost; private StreamConnectionFactory factory; private final boolean keepSSTableLevel; @@ -143,29 +143,29 @@ public class StreamCoordinator if (sessionsToConnect.hasNext()) { StreamSession next = sessionsToConnect.next(); - logger.debug("Connecting next session {} with {}.", next.planId(), next.peer.getHostAddress()); + logger.debug("Connecting next session {} with {}.", next.planId(), next.peer.toString()); streamExecutor.execute(new StreamSessionConnector(next)); } else logger.debug("Finished connecting all sessions"); } - public synchronized Set getPeers() + public synchronized Set getPeers() { return new HashSet<>(peerSessions.keySet()); } - public synchronized StreamSession getOrCreateNextSession(InetAddress peer, InetAddress connecting) + public synchronized StreamSession getOrCreateNextSession(InetAddressAndPort peer, InetAddressAndPort connecting) { return getOrCreateHostData(peer).getOrCreateNextSession(peer, connecting); } - public synchronized StreamSession getOrCreateSessionById(InetAddress peer, int id, InetAddress connecting) + public synchronized StreamSession getOrCreateSessionById(InetAddressAndPort peer, int id, InetAddressAndPort connecting) { return getOrCreateHostData(peer).getOrCreateSessionById(peer, id, connecting); } - public StreamSession getSessionById(InetAddress peer, int id) + public StreamSession getSessionById(InetAddressAndPort peer, int id) { return getHostData(peer).getSessionById(id); } @@ -191,7 +191,7 @@ public class StreamCoordinator return result; } - public synchronized void transferFiles(InetAddress to, Collection sstableDetails) + public synchronized void transferFiles(InetAddressAndPort to, Collection sstableDetails) { HostStreamingData sessionList = getOrCreateHostData(to); @@ -239,7 +239,7 @@ public class StreamCoordinator return result; } - private HostStreamingData getHostData(InetAddress peer) + private HostStreamingData getHostData(InetAddressAndPort peer) { HostStreamingData data = peerSessions.get(peer); if (data == null) @@ -247,7 +247,7 @@ public class StreamCoordinator return data; } - private HostStreamingData getOrCreateHostData(InetAddress peer) + private HostStreamingData getOrCreateHostData(InetAddressAndPort peer) { HostStreamingData data = peerSessions.get(peer); if (data == null) @@ -297,7 +297,7 @@ public class StreamCoordinator return false; } - public StreamSession getOrCreateNextSession(InetAddress peer, InetAddress connecting) + public StreamSession getOrCreateNextSession(InetAddressAndPort peer, InetAddressAndPort connecting) { // create if (streamSessions.size() < connectionsPerHost) @@ -329,7 +329,7 @@ public class StreamCoordinator return Collections.unmodifiableCollection(streamSessions.values()); } - public StreamSession getOrCreateSessionById(InetAddress peer, int id, InetAddress connecting) + public StreamSession getOrCreateSessionById(InetAddressAndPort peer, int id, InetAddressAndPort connecting) { StreamSession session = streamSessions.get(id); if (session == null) diff --git a/src/java/org/apache/cassandra/streaming/StreamEvent.java b/src/java/org/apache/cassandra/streaming/StreamEvent.java index 6ea2814f56..7ecd08179e 100644 --- a/src/java/org/apache/cassandra/streaming/StreamEvent.java +++ b/src/java/org/apache/cassandra/streaming/StreamEvent.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -27,6 +26,7 @@ import com.google.common.collect.ImmutableSet; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; public abstract class StreamEvent { @@ -48,7 +48,7 @@ public abstract class StreamEvent public static class SessionCompleteEvent extends StreamEvent { - public final InetAddress peer; + public final InetAddressAndPort peer; public final boolean success; public final int sessionIndex; public final Set requests; diff --git a/src/java/org/apache/cassandra/streaming/StreamManager.java b/src/java/org/apache/cassandra/streaming/StreamManager.java index a44f02eae5..81c65c5261 100644 --- a/src/java/org/apache/cassandra/streaming/StreamManager.java +++ b/src/java/org/apache/cassandra/streaming/StreamManager.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -35,6 +34,7 @@ import com.google.common.util.concurrent.RateLimiter; import org.cliffc.high_scale_lib.NonBlockingHashMap; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.management.StreamEventJMXNotifier; import org.apache.cassandra.streaming.management.StreamStateCompositeData; @@ -55,7 +55,7 @@ public class StreamManager implements StreamManagerMBean * * @return StreamRateLimiter with rate limit set based on peer location. */ - public static StreamRateLimiter getRateLimiter(InetAddress peer) + public static StreamRateLimiter getRateLimiter(InetAddressAndPort peer) { return new StreamRateLimiter(peer); } @@ -67,7 +67,7 @@ public class StreamManager implements StreamManagerMBean private static final RateLimiter interDCLimiter = RateLimiter.create(Double.MAX_VALUE); private final boolean isLocalDC; - public StreamRateLimiter(InetAddress peer) + public StreamRateLimiter(InetAddressAndPort peer) { double throughput = DatabaseDescriptor.getStreamThroughputOutboundMegabitsPerSec() * BYTES_PER_MEGABIT; mayUpdateThroughput(throughput, limiter); @@ -176,7 +176,7 @@ public class StreamManager implements StreamManagerMBean return notifier.getNotificationInfo(); } - public StreamSession findSession(InetAddress peer, UUID planId, int sessionIndex) + public StreamSession findSession(InetAddressAndPort peer, UUID planId, int sessionIndex) { StreamSession session = findSession(initiatedStreams, peer, planId, sessionIndex); if (session != null) @@ -185,7 +185,7 @@ public class StreamManager implements StreamManagerMBean return findSession(receivingStreams, peer, planId, sessionIndex); } - private StreamSession findSession(Map streams, InetAddress peer, UUID planId, int sessionIndex) + private StreamSession findSession(Map streams, InetAddressAndPort peer, UUID planId, int sessionIndex) { StreamResultFuture streamResultFuture = streams.get(planId); if (streamResultFuture == null) diff --git a/src/java/org/apache/cassandra/streaming/StreamPlan.java b/src/java/org/apache/cassandra/streaming/StreamPlan.java index 213f74bff6..43e9068363 100644 --- a/src/java/org/apache/cassandra/streaming/StreamPlan.java +++ b/src/java/org/apache/cassandra/streaming/StreamPlan.java @@ -17,11 +17,11 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.*; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.UUIDGen; import static org.apache.cassandra.service.ActiveRepairService.NO_PENDING_REPAIR; @@ -73,7 +73,7 @@ public class StreamPlan * @param ranges ranges to fetch * @return this object for chaining */ - public StreamPlan requestRanges(InetAddress from, InetAddress connecting, String keyspace, Collection> ranges) + public StreamPlan requestRanges(InetAddressAndPort from, InetAddressAndPort connecting, String keyspace, Collection> ranges) { return requestRanges(from, connecting, keyspace, ranges, EMPTY_COLUMN_FAMILIES); } @@ -88,7 +88,7 @@ public class StreamPlan * @param columnFamilies specific column families * @return this object for chaining */ - public StreamPlan requestRanges(InetAddress from, InetAddress connecting, String keyspace, Collection> ranges, String... columnFamilies) + public StreamPlan requestRanges(InetAddressAndPort from, InetAddressAndPort connecting, String keyspace, Collection> ranges, String... columnFamilies) { StreamSession session = coordinator.getOrCreateNextSession(from, connecting); session.addStreamRequest(keyspace, ranges, Arrays.asList(columnFamilies)); @@ -98,9 +98,9 @@ public class StreamPlan /** * Add transfer task to send data of specific {@code columnFamilies} under {@code keyspace} and {@code ranges}. * - * @see #transferRanges(java.net.InetAddress, java.net.InetAddress, String, java.util.Collection, String...) + * @see #transferRanges(InetAddressAndPort, InetAddressAndPort, String, java.util.Collection, String...) */ - public StreamPlan transferRanges(InetAddress to, String keyspace, Collection> ranges, String... columnFamilies) + public StreamPlan transferRanges(InetAddressAndPort to, String keyspace, Collection> ranges, String... columnFamilies) { return transferRanges(to, to, keyspace, ranges, columnFamilies); } @@ -114,7 +114,7 @@ public class StreamPlan * @param ranges ranges to send * @return this object for chaining */ - public StreamPlan transferRanges(InetAddress to, InetAddress connecting, String keyspace, Collection> ranges) + public StreamPlan transferRanges(InetAddressAndPort to, InetAddressAndPort connecting, String keyspace, Collection> ranges) { return transferRanges(to, connecting, keyspace, ranges, EMPTY_COLUMN_FAMILIES); } @@ -129,7 +129,7 @@ public class StreamPlan * @param columnFamilies specific column families * @return this object for chaining */ - public StreamPlan transferRanges(InetAddress to, InetAddress connecting, String keyspace, Collection> ranges, String... columnFamilies) + public StreamPlan transferRanges(InetAddressAndPort to, InetAddressAndPort connecting, String keyspace, Collection> ranges, String... columnFamilies) { StreamSession session = coordinator.getOrCreateNextSession(to, connecting); session.addTransferRanges(keyspace, ranges, Arrays.asList(columnFamilies), flushBeforeTransfer); @@ -144,7 +144,7 @@ public class StreamPlan * this collection will be modified to remove those files that are successfully handed off * @return this object for chaining */ - public StreamPlan transferFiles(InetAddress to, Collection sstableDetails) + public StreamPlan transferFiles(InetAddressAndPort to, Collection sstableDetails) { coordinator.transferFiles(to, sstableDetails); return this; diff --git a/src/java/org/apache/cassandra/streaming/StreamResultFuture.java b/src/java/org/apache/cassandra/streaming/StreamResultFuture.java index 0f74c7f3f7..544f37fa95 100644 --- a/src/java/org/apache/cassandra/streaming/StreamResultFuture.java +++ b/src/java/org/apache/cassandra/streaming/StreamResultFuture.java @@ -29,6 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.Channel; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.FBUtilities; /** @@ -103,7 +104,7 @@ public final class StreamResultFuture extends AbstractFuture public static synchronized StreamResultFuture initReceivingSide(int sessionIndex, UUID planId, StreamOperation streamOperation, - InetAddress from, + InetAddressAndPort from, Channel channel, boolean keepSSTableLevel, UUID pendingRepair, @@ -135,11 +136,15 @@ public final class StreamResultFuture extends AbstractFuture return coordinator; } - private void attachConnection(InetAddress from, int sessionIndex, Channel channel) + private void attachConnection(InetAddressAndPort from, int sessionIndex, Channel channel) { SocketAddress addr = channel.remoteAddress(); - InetAddress connecting = (addr instanceof InetSocketAddress ? ((InetSocketAddress) addr).getAddress() : from); - StreamSession session = coordinator.getOrCreateSessionById(from, sessionIndex, connecting); + //In the case of unit tests, if you use the EmbeddedChannel, channel.remoteAddress() + //does not return an InetSocketAddress, but an EmbeddedSocketAddress. Hence why we need the type check here + InetAddress connecting = (addr instanceof InetSocketAddress ? ((InetSocketAddress) addr).getAddress() : from.address); + //Need to turn connecting into a InetAddressAndPort with the correct port. I think getting the port from "from" + //Will work since we don't actually have ports diverge across network interfaces + StreamSession session = coordinator.getOrCreateSessionById(from, sessionIndex, InetAddressAndPort.getByAddressOverrideDefaults(connecting, from.port)); session.init(this); session.attach(channel); } @@ -228,7 +233,7 @@ public final class StreamResultFuture extends AbstractFuture } } - StreamSession getSession(InetAddress peer, int sessionIndex) + StreamSession getSession(InetAddressAndPort peer, int sessionIndex) { return coordinator.getSessionById(peer, sessionIndex); } diff --git a/src/java/org/apache/cassandra/streaming/StreamSession.java b/src/java/org/apache/cassandra/streaming/StreamSession.java index b6351f9ed0..4085c43a38 100644 --- a/src/java/org/apache/cassandra/streaming/StreamSession.java +++ b/src/java/org/apache/cassandra/streaming/StreamSession.java @@ -49,6 +49,7 @@ import org.apache.cassandra.db.PartitionPosition; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.gms.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.StreamingMetrics; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.OutboundConnectionIdentifier; @@ -142,14 +143,14 @@ public class StreamSession implements IEndpointStateChangeSubscriber /** * Streaming endpoint. * - * Each {@code StreamSession} is identified by this InetAddress which is broadcast address of the node streaming. + * Each {@code StreamSession} is identified by this InetAddressAndPort which is broadcast address of the node streaming. */ - public final InetAddress peer; + public final InetAddressAndPort peer; private final int index; /** Actual connecting address. Can be the same as {@linkplain #peer}. */ - public final InetAddress connecting; + public final InetAddressAndPort connecting; // should not be null when session is started private StreamResultFuture streamResult; @@ -191,14 +192,14 @@ public class StreamSession implements IEndpointStateChangeSubscriber * @param peer Address of streaming peer * @param connecting Actual connecting address */ - public StreamSession(InetAddress peer, InetAddress connecting, StreamConnectionFactory factory, int index, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind) + public StreamSession(InetAddressAndPort peer, InetAddressAndPort connecting, StreamConnectionFactory factory, int index, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind) { this.peer = peer; this.connecting = connecting; this.index = index; - OutboundConnectionIdentifier id = OutboundConnectionIdentifier.stream(new InetSocketAddress(FBUtilities.getBroadcastAddress(), 0), - new InetSocketAddress(connecting, MessagingService.instance().portFor(connecting))); + OutboundConnectionIdentifier id = OutboundConnectionIdentifier.stream(InetAddressAndPort.getByAddressOverrideDefaults(FBUtilities.getBroadcastAddressAndPort().address, 0), + InetAddressAndPort.getByAddressOverrideDefaults(connecting.address, MessagingService.instance().portFor(connecting))); this.messageSender = new NettyStreamingMessageSender(this, id, factory, StreamMessage.CURRENT_VERSION, previewKind.isPreview()); this.metrics = StreamingMetrics.get(connecting); this.keepSSTableLevel = keepSSTableLevel; @@ -607,16 +608,16 @@ public class StreamSession implements IEndpointStateChangeSubscriber { logger.error("[Stream #{}] Did not receive response from peer {}{} for {} secs. Is peer down? " + "If not, maybe try increasing streaming_keep_alive_period_in_secs.", planId(), - peer.getHostAddress(), - peer.equals(connecting) ? "" : " through " + connecting.getHostAddress(), + peer.getHostAddress(true), + peer.equals(connecting) ? "" : " through " + connecting.getHostAddress(true), 2 * DatabaseDescriptor.getStreamingKeepAlivePeriod(), e); } else { logger.error("[Stream #{}] Streaming error occurred on session with peer {}{}", planId(), - peer.getHostAddress(), - peer.equals(connecting) ? "" : " through " + connecting.getHostAddress(), + peer.getHostAddress(true), + peer.equals(connecting) ? "" : " through " + connecting.getHostAddress(true), e); } } @@ -644,7 +645,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber prepareReceiving(summary); PrepareSynAckMessage prepareSynAck = new PrepareSynAckMessage(); - if (!peer.equals(FBUtilities.getBroadcastAddress())) + if (!peer.equals(FBUtilities.getBroadcastAddressAndPort())) for (StreamTransferTask task : transfers.values()) prepareSynAck.summaries.add(task.getSummary()); messageSender.sendMessage(prepareSynAck); @@ -754,7 +755,7 @@ public class StreamSession implements IEndpointStateChangeSubscriber */ public synchronized void sessionFailed() { - logger.error("[Stream #{}] Remote peer {} failed stream session.", planId(), peer.getHostAddress()); + logger.error("[Stream #{}] Remote peer {} failed stream session.", planId(), peer.toString()); closeSession(State.FAILED); } @@ -784,21 +785,21 @@ public class StreamSession implements IEndpointStateChangeSubscriber maybeCompleted(); } - public void onJoin(InetAddress endpoint, EndpointState epState) {} - public void beforeChange(InetAddress endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} - public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue value) {} - public void onAlive(InetAddress endpoint, EndpointState state) {} - public void onDead(InetAddress endpoint, EndpointState state) {} + public void onJoin(InetAddressAndPort endpoint, EndpointState epState) {} + public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue) {} + public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value) {} + public void onAlive(InetAddressAndPort endpoint, EndpointState state) {} + public void onDead(InetAddressAndPort endpoint, EndpointState state) {} - public void onRemove(InetAddress endpoint) + public void onRemove(InetAddressAndPort endpoint) { - logger.error("[Stream #{}] Session failed because remote peer {} has left.", planId(), peer.getHostAddress()); + logger.error("[Stream #{}] Session failed because remote peer {} has left.", planId(), peer.toString()); closeSession(State.FAILED); } - public void onRestart(InetAddress endpoint, EndpointState epState) + public void onRestart(InetAddressAndPort endpoint, EndpointState epState) { - logger.error("[Stream #{}] Session failed because remote peer {} was restarted.", planId(), peer.getHostAddress()); + logger.error("[Stream #{}] Session failed because remote peer {} was restarted.", planId(), peer.toString()); closeSession(State.FAILED); } diff --git a/src/java/org/apache/cassandra/streaming/async/NettyStreamingMessageSender.java b/src/java/org/apache/cassandra/streaming/async/NettyStreamingMessageSender.java index 0b3876019e..20b7c87a31 100644 --- a/src/java/org/apache/cassandra/streaming/async/NettyStreamingMessageSender.java +++ b/src/java/org/apache/cassandra/streaming/async/NettyStreamingMessageSender.java @@ -137,7 +137,7 @@ public class NettyStreamingMessageSender implements StreamingMessageSender @Override public void initialize() throws IOException { - StreamInitMessage message = new StreamInitMessage(FBUtilities.getBroadcastAddress(), + StreamInitMessage message = new StreamInitMessage(FBUtilities.getBroadcastAddressAndPort(), session.sessionIndex(), session.planId(), session.streamOperation(), @@ -183,7 +183,7 @@ public class NettyStreamingMessageSender implements StreamingMessageSender { Channel channel = factory.createConnection(connectionId, protocolVersion); ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast(NettyFactory.instance.streamingGroup, NettyFactory.INBOUND_STREAM_HANDLER_NAME, new StreamingInboundHandler(connectionId.remoteAddress(), protocolVersion, session)); + pipeline.addLast(NettyFactory.instance.streamingGroup, NettyFactory.INBOUND_STREAM_HANDLER_NAME, new StreamingInboundHandler(connectionId.remote(), protocolVersion, session)); channel.attr(TRANSFERRING_FILE_ATTR).set(Boolean.FALSE); return channel; } diff --git a/src/java/org/apache/cassandra/streaming/async/StreamingInboundHandler.java b/src/java/org/apache/cassandra/streaming/async/StreamingInboundHandler.java index cc6f9e0797..907572b210 100644 --- a/src/java/org/apache/cassandra/streaming/async/StreamingInboundHandler.java +++ b/src/java/org/apache/cassandra/streaming/async/StreamingInboundHandler.java @@ -21,7 +21,6 @@ package org.apache.cassandra.streaming.async; import java.io.EOFException; import java.io.IOException; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -38,6 +37,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.FastThreadLocalThread; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.async.RebufferingByteBufDataInputPlus; import org.apache.cassandra.streaming.StreamManager; import org.apache.cassandra.streaming.StreamReceiveException; @@ -65,7 +65,7 @@ public class StreamingInboundHandler extends ChannelInboundHandlerAdapter private static final int AUTO_READ_LOW_WATER_MARK = 1 << 15; private static final int AUTO_READ_HIGH_WATER_MARK = 1 << 16; - private final InetSocketAddress remoteAddress; + private final InetAddressAndPort remoteAddress; private final int protocolVersion; private final StreamSession session; @@ -82,7 +82,7 @@ public class StreamingInboundHandler extends ChannelInboundHandlerAdapter private volatile boolean closed; - public StreamingInboundHandler(InetSocketAddress remoteAddress, int protocolVersion, @Nullable StreamSession session) + public StreamingInboundHandler(InetAddressAndPort remoteAddress, int protocolVersion, @Nullable StreamSession session) { this.remoteAddress = remoteAddress; this.protocolVersion = protocolVersion; @@ -254,11 +254,11 @@ public class StreamingInboundHandler extends ChannelInboundHandlerAdapter */ static class SessionIdentifier { - final InetAddress from; + final InetAddressAndPort from; final UUID planId; final int sessionIndex; - SessionIdentifier(InetAddress from, UUID planId, int sessionIndex) + SessionIdentifier(InetAddressAndPort from, UUID planId, int sessionIndex) { this.from = from; this.planId = planId; diff --git a/src/java/org/apache/cassandra/streaming/management/ProgressInfoCompositeData.java b/src/java/org/apache/cassandra/streaming/management/ProgressInfoCompositeData.java index b9e6951e9e..964fe107a2 100644 --- a/src/java/org/apache/cassandra/streaming/management/ProgressInfoCompositeData.java +++ b/src/java/org/apache/cassandra/streaming/management/ProgressInfoCompositeData.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming.management; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; @@ -26,12 +25,14 @@ import javax.management.openmbean.*; import com.google.common.base.Throwables; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.ProgressInfo; public class ProgressInfoCompositeData { private static final String[] ITEM_NAMES = new String[]{"planId", "peer", + "peer storage port", "sessionIndex", "fileName", "direction", @@ -39,6 +40,7 @@ public class ProgressInfoCompositeData "totalBytes"}; private static final String[] ITEM_DESCS = new String[]{"String representation of Plan ID", "Session peer", + "Session peer storage port", "Index of session", "Name of the file", "Direction('IN' or 'OUT')", @@ -47,6 +49,7 @@ public class ProgressInfoCompositeData private static final OpenType[] ITEM_TYPES = new OpenType[]{SimpleType.STRING, SimpleType.STRING, SimpleType.INTEGER, + SimpleType.INTEGER, SimpleType.STRING, SimpleType.STRING, SimpleType.LONG, @@ -73,12 +76,13 @@ public class ProgressInfoCompositeData { Map valueMap = new HashMap<>(); valueMap.put(ITEM_NAMES[0], planId.toString()); - valueMap.put(ITEM_NAMES[1], progressInfo.peer.getHostAddress()); - valueMap.put(ITEM_NAMES[2], progressInfo.sessionIndex); - valueMap.put(ITEM_NAMES[3], progressInfo.fileName); - valueMap.put(ITEM_NAMES[4], progressInfo.direction.name()); - valueMap.put(ITEM_NAMES[5], progressInfo.currentBytes); - valueMap.put(ITEM_NAMES[6], progressInfo.totalBytes); + valueMap.put(ITEM_NAMES[1], progressInfo.peer.address.getHostAddress()); + valueMap.put(ITEM_NAMES[2], progressInfo.peer.port); + valueMap.put(ITEM_NAMES[3], progressInfo.sessionIndex); + valueMap.put(ITEM_NAMES[4], progressInfo.fileName); + valueMap.put(ITEM_NAMES[5], progressInfo.direction.name()); + valueMap.put(ITEM_NAMES[6], progressInfo.currentBytes); + valueMap.put(ITEM_NAMES[7], progressInfo.totalBytes); try { return new CompositeDataSupport(COMPOSITE_TYPE, valueMap); @@ -94,12 +98,12 @@ public class ProgressInfoCompositeData Object[] values = cd.getAll(ITEM_NAMES); try { - return new ProgressInfo(InetAddress.getByName((String) values[1]), - (int) values[2], - (String) values[3], - ProgressInfo.Direction.valueOf((String)values[4]), - (long) values[5], - (long) values[6]); + return new ProgressInfo(InetAddressAndPort.getByNameOverrideDefaults((String) values[1], (Integer)values[2]), + (int) values[3], + (String) values[4], + ProgressInfo.Direction.valueOf((String)values[5]), + (long) values[6], + (long) values[7]); } catch (UnknownHostException e) { diff --git a/src/java/org/apache/cassandra/streaming/management/SessionCompleteEventCompositeData.java b/src/java/org/apache/cassandra/streaming/management/SessionCompleteEventCompositeData.java index 516582a389..1c0d8c5247 100644 --- a/src/java/org/apache/cassandra/streaming/management/SessionCompleteEventCompositeData.java +++ b/src/java/org/apache/cassandra/streaming/management/SessionCompleteEventCompositeData.java @@ -29,12 +29,15 @@ public class SessionCompleteEventCompositeData { private static final String[] ITEM_NAMES = new String[]{"planId", "peer", + "peer storage port", "success"}; private static final String[] ITEM_DESCS = new String[]{"Plan ID", "Session peer", + "Session peer storage port", "Indicates whether session was successful"}; private static final OpenType[] ITEM_TYPES = new OpenType[]{SimpleType.STRING, SimpleType.STRING, + SimpleType.INTEGER, SimpleType.BOOLEAN}; public static final CompositeType COMPOSITE_TYPE; @@ -58,8 +61,9 @@ public class SessionCompleteEventCompositeData { Map valueMap = new HashMap<>(); valueMap.put(ITEM_NAMES[0], event.planId.toString()); - valueMap.put(ITEM_NAMES[1], event.peer.getHostAddress()); - valueMap.put(ITEM_NAMES[2], event.success); + valueMap.put(ITEM_NAMES[1], event.peer.address.getHostAddress()); + valueMap.put(ITEM_NAMES[2], event.peer.port); + valueMap.put(ITEM_NAMES[3], event.success); try { return new CompositeDataSupport(COMPOSITE_TYPE, valueMap); diff --git a/src/java/org/apache/cassandra/streaming/management/SessionInfoCompositeData.java b/src/java/org/apache/cassandra/streaming/management/SessionInfoCompositeData.java index a6762a8832..d20eaf57cd 100644 --- a/src/java/org/apache/cassandra/streaming/management/SessionInfoCompositeData.java +++ b/src/java/org/apache/cassandra/streaming/management/SessionInfoCompositeData.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming.management; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import javax.management.openmbean.*; @@ -27,6 +26,7 @@ import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.ProgressInfo; import org.apache.cassandra.streaming.SessionInfo; import org.apache.cassandra.streaming.StreamSession; @@ -36,7 +36,9 @@ public class SessionInfoCompositeData { private static final String[] ITEM_NAMES = new String[]{"planId", "peer", + "peer_port", "connecting", + "connecting_port", "receivingSummaries", "sendingSummaries", "state", @@ -45,7 +47,9 @@ public class SessionInfoCompositeData "sessionIndex"}; private static final String[] ITEM_DESCS = new String[]{"Plan ID", "Session peer", + "Session peer storage port", "Connecting address", + "Connecting storage port", "Summaries of receiving data", "Summaries of sending data", "Current session state", @@ -61,7 +65,9 @@ public class SessionInfoCompositeData { ITEM_TYPES = new OpenType[]{SimpleType.STRING, SimpleType.STRING, + SimpleType.INTEGER, SimpleType.STRING, + SimpleType.INTEGER, ArrayType.getArrayType(StreamSummaryCompositeData.COMPOSITE_TYPE), ArrayType.getArrayType(StreamSummaryCompositeData.COMPOSITE_TYPE), SimpleType.STRING, @@ -84,8 +90,10 @@ public class SessionInfoCompositeData { Map valueMap = new HashMap<>(); valueMap.put(ITEM_NAMES[0], planId.toString()); - valueMap.put(ITEM_NAMES[1], sessionInfo.peer.getHostAddress()); - valueMap.put(ITEM_NAMES[2], sessionInfo.connecting.getHostAddress()); + valueMap.put(ITEM_NAMES[1], sessionInfo.peer.address.getHostAddress()); + valueMap.put(ITEM_NAMES[2], sessionInfo.peer.port); + valueMap.put(ITEM_NAMES[3], sessionInfo.connecting.address.getHostAddress()); + valueMap.put(ITEM_NAMES[4], sessionInfo.connecting.port); Function fromStreamSummary = new Function() { public CompositeData apply(StreamSummary input) @@ -93,9 +101,9 @@ public class SessionInfoCompositeData return StreamSummaryCompositeData.toCompositeData(input); } }; - valueMap.put(ITEM_NAMES[3], toArrayOfCompositeData(sessionInfo.receivingSummaries, fromStreamSummary)); - valueMap.put(ITEM_NAMES[4], toArrayOfCompositeData(sessionInfo.sendingSummaries, fromStreamSummary)); - valueMap.put(ITEM_NAMES[5], sessionInfo.state.name()); + valueMap.put(ITEM_NAMES[5], toArrayOfCompositeData(sessionInfo.receivingSummaries, fromStreamSummary)); + valueMap.put(ITEM_NAMES[6], toArrayOfCompositeData(sessionInfo.sendingSummaries, fromStreamSummary)); + valueMap.put(ITEM_NAMES[7], sessionInfo.state.name()); Function fromProgressInfo = new Function() { public CompositeData apply(ProgressInfo input) @@ -103,9 +111,9 @@ public class SessionInfoCompositeData return ProgressInfoCompositeData.toCompositeData(planId, input); } }; - valueMap.put(ITEM_NAMES[6], toArrayOfCompositeData(sessionInfo.getReceivingFiles(), fromProgressInfo)); - valueMap.put(ITEM_NAMES[7], toArrayOfCompositeData(sessionInfo.getSendingFiles(), fromProgressInfo)); - valueMap.put(ITEM_NAMES[8], sessionInfo.sessionIndex); + valueMap.put(ITEM_NAMES[8], toArrayOfCompositeData(sessionInfo.getReceivingFiles(), fromProgressInfo)); + valueMap.put(ITEM_NAMES[9], toArrayOfCompositeData(sessionInfo.getSendingFiles(), fromProgressInfo)); + valueMap.put(ITEM_NAMES[10], sessionInfo.sessionIndex); try { return new CompositeDataSupport(COMPOSITE_TYPE, valueMap); @@ -121,11 +129,11 @@ public class SessionInfoCompositeData assert cd.getCompositeType().equals(COMPOSITE_TYPE); Object[] values = cd.getAll(ITEM_NAMES); - InetAddress peer, connecting; + InetAddressAndPort peer, connecting; try { - peer = InetAddress.getByName((String) values[1]); - connecting = InetAddress.getByName((String) values[2]); + peer = InetAddressAndPort.getByNameOverrideDefaults((String) values[1], (Integer)values[2]); + connecting = InetAddressAndPort.getByNameOverrideDefaults((String) values[3], (Integer)values[4]); } catch (UnknownHostException e) { @@ -139,11 +147,11 @@ public class SessionInfoCompositeData } }; SessionInfo info = new SessionInfo(peer, - (int)values[8], + (int)values[10], connecting, - fromArrayOfCompositeData((CompositeData[]) values[3], toStreamSummary), - fromArrayOfCompositeData((CompositeData[]) values[4], toStreamSummary), - StreamSession.State.valueOf((String) values[5])); + fromArrayOfCompositeData((CompositeData[]) values[5], toStreamSummary), + fromArrayOfCompositeData((CompositeData[]) values[6], toStreamSummary), + StreamSession.State.valueOf((String) values[7])); Function toProgressInfo = new Function() { public ProgressInfo apply(CompositeData input) @@ -151,11 +159,11 @@ public class SessionInfoCompositeData return ProgressInfoCompositeData.fromCompositeData(input); } }; - for (ProgressInfo progress : fromArrayOfCompositeData((CompositeData[]) values[6], toProgressInfo)) + for (ProgressInfo progress : fromArrayOfCompositeData((CompositeData[]) values[8], toProgressInfo)) { info.updateProgress(progress); } - for (ProgressInfo progress : fromArrayOfCompositeData((CompositeData[]) values[7], toProgressInfo)) + for (ProgressInfo progress : fromArrayOfCompositeData((CompositeData[]) values[9], toProgressInfo)) { info.updateProgress(progress); } diff --git a/src/java/org/apache/cassandra/streaming/messages/FileMessageHeader.java b/src/java/org/apache/cassandra/streaming/messages/FileMessageHeader.java index fedb971995..13a3358c0a 100644 --- a/src/java/org/apache/cassandra/streaming/messages/FileMessageHeader.java +++ b/src/java/org/apache/cassandra/streaming/messages/FileMessageHeader.java @@ -18,7 +18,6 @@ package org.apache.cassandra.streaming.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -30,6 +29,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.io.sstable.format.Version; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.TableId; @@ -67,13 +67,13 @@ public class FileMessageHeader public final UUID pendingRepair; public final int sstableLevel; public final SerializationHeader.Component header; - public final InetAddress sender; + public final InetAddressAndPort sender; /* cached size value */ private transient final long size; private FileMessageHeader(TableId tableId, - InetAddress sender, + InetAddressAndPort sender, UUID planId, int sessionIndex, int sequenceNumber, @@ -106,7 +106,7 @@ public class FileMessageHeader } public FileMessageHeader(TableId tableId, - InetAddress sender, + InetAddressAndPort sender, UUID planId, int sessionIndex, int sequenceNumber, @@ -218,7 +218,7 @@ public class FileMessageHeader public CompressionInfo serialize(FileMessageHeader header, DataOutputPlus out, int version) throws IOException { header.tableId.serialize(out); - CompactEndpointSerializationHelper.serialize(header.sender, out); + CompactEndpointSerializationHelper.streamingInstance.serialize(header.sender, out, version); UUIDSerializer.serializer.serialize(header.planId, out, version); out.writeInt(header.sessionIndex); out.writeInt(header.sequenceNumber); @@ -252,7 +252,7 @@ public class FileMessageHeader public FileMessageHeader deserialize(DataInputPlus in, int version) throws IOException { TableId tableId = TableId.deserialize(in); - InetAddress sender = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort sender = CompactEndpointSerializationHelper.streamingInstance.deserialize(in, version); UUID planId = UUIDSerializer.serializer.deserialize(in, MessagingService.current_version); int sessionIndex = in.readInt(); int sequenceNumber = in.readInt(); @@ -276,7 +276,7 @@ public class FileMessageHeader public long serializedSize(FileMessageHeader header, int version) { long size = header.tableId.serializedSize(); - size += CompactEndpointSerializationHelper.serializedSize(header.sender); + size += CompactEndpointSerializationHelper.streamingInstance.serializedSize(header.sender, version); size += UUIDSerializer.serializer.serializedSize(header.planId, version); size += TypeSizes.sizeof(header.sessionIndex); size += TypeSizes.sizeof(header.sequenceNumber); diff --git a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java index f44b41ce4a..8bbcc05bce 100644 --- a/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java +++ b/src/java/org/apache/cassandra/streaming/messages/OutgoingFileMessage.java @@ -79,7 +79,7 @@ public class OutgoingFileMessage extends StreamMessage SSTableReader sstable = ref.get(); filename = sstable.getFilename(); this.header = new FileMessageHeader(sstable.metadata().id, - FBUtilities.getBroadcastAddress(), + FBUtilities.getBroadcastAddressAndPort(), session.planId(), session.sessionIndex(), sequenceNumber, diff --git a/src/java/org/apache/cassandra/streaming/messages/StreamInitMessage.java b/src/java/org/apache/cassandra/streaming/messages/StreamInitMessage.java index 68c6034fd8..fced1332a3 100644 --- a/src/java/org/apache/cassandra/streaming/messages/StreamInitMessage.java +++ b/src/java/org/apache/cassandra/streaming/messages/StreamInitMessage.java @@ -18,12 +18,12 @@ package org.apache.cassandra.streaming.messages; import java.io.IOException; -import java.net.InetAddress; import java.util.UUID; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputStreamPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.streaming.StreamOperation; @@ -39,7 +39,7 @@ public class StreamInitMessage extends StreamMessage { public static Serializer serializer = new StreamInitMessageSerializer(); - public final InetAddress from; + public final InetAddressAndPort from; public final int sessionIndex; public final UUID planId; public final StreamOperation streamOperation; @@ -48,7 +48,7 @@ public class StreamInitMessage extends StreamMessage public final UUID pendingRepair; public final PreviewKind previewKind; - public StreamInitMessage(InetAddress from, int sessionIndex, UUID planId, StreamOperation streamOperation, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind) + public StreamInitMessage(InetAddressAndPort from, int sessionIndex, UUID planId, StreamOperation streamOperation, boolean keepSSTableLevel, UUID pendingRepair, PreviewKind previewKind) { super(Type.STREAM_INIT); this.from = from; @@ -73,7 +73,7 @@ public class StreamInitMessage extends StreamMessage { public void serialize(StreamInitMessage message, DataOutputStreamPlus out, int version, StreamSession session) throws IOException { - CompactEndpointSerializationHelper.serialize(message.from, out); + CompactEndpointSerializationHelper.streamingInstance.serialize(message.from, out, version); out.writeInt(message.sessionIndex); UUIDSerializer.serializer.serialize(message.planId, out, MessagingService.current_version); out.writeUTF(message.streamOperation.getDescription()); @@ -89,7 +89,7 @@ public class StreamInitMessage extends StreamMessage public StreamInitMessage deserialize(DataInputPlus in, int version, StreamSession session) throws IOException { - InetAddress from = CompactEndpointSerializationHelper.deserialize(in); + InetAddressAndPort from = CompactEndpointSerializationHelper.streamingInstance.deserialize(in, version); int sessionIndex = in.readInt(); UUID planId = UUIDSerializer.serializer.deserialize(in, MessagingService.current_version); String description = in.readUTF(); @@ -102,7 +102,7 @@ public class StreamInitMessage extends StreamMessage public long serializedSize(StreamInitMessage message, int version) { - long size = CompactEndpointSerializationHelper.serializedSize(message.from); + long size = CompactEndpointSerializationHelper.streamingInstance.serializedSize(message.from, version); size += TypeSizes.sizeof(message.sessionIndex); size += UUIDSerializer.serializer.serializedSize(message.planId, MessagingService.current_version); size += TypeSizes.sizeof(message.streamOperation.getDescription()); diff --git a/src/java/org/apache/cassandra/tools/BulkLoadConnectionFactory.java b/src/java/org/apache/cassandra/tools/BulkLoadConnectionFactory.java index b56d2921fb..cce686fb01 100644 --- a/src/java/org/apache/cassandra/tools/BulkLoadConnectionFactory.java +++ b/src/java/org/apache/cassandra/tools/BulkLoadConnectionFactory.java @@ -22,21 +22,21 @@ import java.io.IOException; import java.net.InetSocketAddress; import io.netty.channel.Channel; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.net.async.OutboundConnectionIdentifier; import org.apache.cassandra.streaming.DefaultConnectionFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.StreamConnectionFactory; public class BulkLoadConnectionFactory extends DefaultConnectionFactory implements StreamConnectionFactory { private final boolean outboundBindAny; - private final int storagePort; private final int secureStoragePort; private final EncryptionOptions.ServerEncryptionOptions encryptionOptions; - public BulkLoadConnectionFactory(int storagePort, int secureStoragePort, EncryptionOptions.ServerEncryptionOptions encryptionOptions, boolean outboundBindAny) + public BulkLoadConnectionFactory(int secureStoragePort, EncryptionOptions.ServerEncryptionOptions encryptionOptions, boolean outboundBindAny) { - this.storagePort = storagePort; this.secureStoragePort = secureStoragePort; this.encryptionOptions = encryptionOptions != null && encryptionOptions.internode_encryption == EncryptionOptions.ServerEncryptionOptions.InternodeEncryption.none ? null @@ -50,9 +50,9 @@ public class BulkLoadConnectionFactory extends DefaultConnectionFactory implemen // When 'all', 'dc' and 'rack', server nodes always have SSL port open, and since thin client like sstableloader // does not know which node is in which dc/rack, connecting to SSL port is always the option. int port = encryptionOptions != null && encryptionOptions.internode_encryption != EncryptionOptions.ServerEncryptionOptions.InternodeEncryption.none ? - secureStoragePort : storagePort; + secureStoragePort : connectionId.remote().port; - connectionId = connectionId.withNewConnectionAddress(new InetSocketAddress(connectionId.remote(), port)); + connectionId = connectionId.withNewConnectionAddress(InetAddressAndPort.getByAddressOverrideDefaults(connectionId.remote().address, port)); return createConnection(connectionId, protocolVersion, encryptionOptions); } } diff --git a/src/java/org/apache/cassandra/tools/BulkLoader.java b/src/java/org/apache/cassandra/tools/BulkLoader.java index 0812e53b5a..545d1f71e0 100644 --- a/src/java/org/apache/cassandra/tools/BulkLoader.java +++ b/src/java/org/apache/cassandra/tools/BulkLoader.java @@ -18,7 +18,7 @@ package org.apache.cassandra.tools; import java.io.IOException; -import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.Set; import javax.net.ssl.SSLContext; @@ -33,6 +33,7 @@ import org.apache.commons.cli.Options; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.io.sstable.SSTableLoader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.security.SSLFactory; import org.apache.cassandra.streaming.*; import org.apache.cassandra.utils.FBUtilities; @@ -57,11 +58,12 @@ public class BulkLoader new ExternalClient( options.hosts, options.nativePort, - options.authProvider, options.storagePort, + options.authProvider, options.sslStoragePort, options.serverEncOptions, - buildSSLOptions(options.clientEncOptions)), + buildSSLOptions(options.clientEncOptions), + options.allowServerPortDiscovery), handler, options.connectionsPerHost); DatabaseDescriptor.setStreamThroughputOutboundMegabitsPerSec(options.throttle); @@ -124,7 +126,7 @@ public class BulkLoader private long peak = 0; private int totalFiles = 0; - private final Multimap sessionsByHost = HashMultimap.create(); + private final Multimap sessionsByHost = HashMultimap.create(); public ProgressIndicator() { @@ -165,7 +167,7 @@ public class BulkLoader boolean updateTotalFiles = totalFiles == 0; // recalculate progress across all sessions in all hosts and display - for (InetAddress peer : sessionsByHost.keySet()) + for (InetAddressAndPort peer : sessionsByHost.keySet()) { sb.append("[").append(peer).append("]"); @@ -268,20 +270,19 @@ public class BulkLoader static class ExternalClient extends NativeSSTableLoaderClient { - private final int storagePort; private final int sslStoragePort; private final EncryptionOptions.ServerEncryptionOptions serverEncOptions; - public ExternalClient(Set hosts, - int port, - AuthProvider authProvider, + public ExternalClient(Set hosts, + int nativePort, int storagePort, + AuthProvider authProvider, int sslStoragePort, EncryptionOptions.ServerEncryptionOptions serverEncryptionOptions, - SSLOptions sslOptions) + SSLOptions sslOptions, + boolean allowServerPortDiscovery) { - super(hosts, port, authProvider, sslOptions); - this.storagePort = storagePort; + super(hosts, nativePort, storagePort, authProvider, sslOptions, allowServerPortDiscovery); this.sslStoragePort = sslStoragePort; serverEncOptions = serverEncryptionOptions; } @@ -289,7 +290,7 @@ public class BulkLoader @Override public StreamConnectionFactory getConnectionFactory() { - return new BulkLoadConnectionFactory(storagePort, sslStoragePort, serverEncOptions, false); + return new BulkLoadConnectionFactory(sslStoragePort, serverEncOptions, false); } } diff --git a/src/java/org/apache/cassandra/tools/LoaderOptions.java b/src/java/org/apache/cassandra/tools/LoaderOptions.java index c821e6a498..4646ba4b83 100644 --- a/src/java/org/apache/cassandra/tools/LoaderOptions.java +++ b/src/java/org/apache/cassandra/tools/LoaderOptions.java @@ -27,8 +27,12 @@ import java.net.*; import java.util.HashSet; import java.util.Set; +import com.google.common.base.Throwables; +import com.google.common.net.HostAndPort; + import org.apache.cassandra.config.*; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.tools.BulkLoader.CmdLineOptions; import com.datastax.driver.core.AuthProvider; @@ -54,6 +58,7 @@ public class LoaderOptions public static final String THROTTLE_MBITS = "throttle"; public static final String INTER_DC_THROTTLE_MBITS = "inter-dc-throttle"; public static final String TOOL_NAME = "sstableloader"; + public static final String ALLOW_SERVER_PORT_DISCOVERY_OPTION = "server-port-discovery"; /* client encryption options */ public static final String SSL_TRUSTSTORE = "truststore"; @@ -80,8 +85,9 @@ public class LoaderOptions public final EncryptionOptions clientEncOptions; public final int connectionsPerHost; public final EncryptionOptions.ServerEncryptionOptions serverEncOptions; - public final Set hosts; - public final Set ignores; + public final Set hosts; + public final Set ignores; + public final boolean allowServerPortDiscovery; LoaderOptions(Builder builder) { @@ -101,6 +107,7 @@ public class LoaderOptions connectionsPerHost = builder.connectionsPerHost; serverEncOptions = builder.serverEncOptions; hosts = builder.hosts; + allowServerPortDiscovery = builder.allowServerPortDiscovery; ignores = builder.ignores; } @@ -122,8 +129,11 @@ public class LoaderOptions EncryptionOptions clientEncOptions = new EncryptionOptions(); int connectionsPerHost = 1; EncryptionOptions.ServerEncryptionOptions serverEncOptions = new EncryptionOptions.ServerEncryptionOptions(); - Set hosts = new HashSet<>(); - Set ignores = new HashSet<>(); + Set hostsArg = new HashSet<>(); + Set ignoresArg = new HashSet<>(); + Set hosts = new HashSet<>(); + Set ignores = new HashSet<>(); + boolean allowServerPortDiscovery; Builder() { @@ -133,6 +143,23 @@ public class LoaderOptions public LoaderOptions build() { constructAuthProvider(); + + try + { + for (InetAddress host : hostsArg) + { + hosts.add(new InetSocketAddress(host, nativePort)); + } + for (InetAddress host : ignoresArg) + { + ignores.add(InetAddressAndPort.getByNameOverrideDefaults(host.getHostAddress(), storagePort)); + } + } + catch (UnknownHostException e) + { + Throwables.propagate(e); + } + return new LoaderOptions(this); } @@ -226,13 +253,26 @@ public class LoaderOptions return this; } + @Deprecated public Builder hosts(Set hosts) { - this.hosts = hosts; + this.hostsArg.addAll(hosts); + return this; + } + + public Builder hostsAndNativePort(Set hosts) + { + this.hosts.addAll(hosts); return this; } public Builder host(InetAddress host) + { + hostsArg.add(host); + return this; + } + + public Builder hostAndNativePort(InetSocketAddress host) { hosts.add(host); return this; @@ -240,16 +280,34 @@ public class LoaderOptions public Builder ignore(Set ignores) { - this.ignores = ignores; + this.ignoresArg.addAll(ignores); + return this; + } + + public Builder ignoresAndInternalPorts(Set ignores) + { + this.ignores.addAll(ignores); return this; } public Builder ignore(InetAddress ignore) + { + ignoresArg.add(ignore); + return this; + } + + public Builder ignoreAndInternalPorts(InetAddressAndPort ignore) { ignores.add(ignore); return this; } + public Builder allowServerPortDiscovery(boolean allowServerPortDiscovery) + { + this.allowServerPortDiscovery = allowServerPortDiscovery; + return this; + } + public Builder parseArgs(String cmdArgs[]) { CommandLineParser parser = new GnuParser(); @@ -296,6 +354,7 @@ public class LoaderOptions verbose = cmd.hasOption(VERBOSE_OPTION); noProgress = cmd.hasOption(NOPROGRESS_OPTION); + allowServerPortDiscovery = cmd.hasOption(ALLOW_SERVER_PORT_DISCOVERY_OPTION); if (cmd.hasOption(USER_OPTION)) { @@ -319,7 +378,8 @@ public class LoaderOptions { for (String node : nodes) { - hosts.add(InetAddress.getByName(node.trim())); + HostAndPort hap = HostAndPort.fromString(node); + hosts.add(new InetSocketAddress(InetAddress.getByName(hap.getHost()), hap.getPortOrDefault(nativePort))); } } catch (UnknownHostException e) { @@ -340,7 +400,7 @@ public class LoaderOptions { for (String node : nodes) { - ignores.add(InetAddress.getByName(node.trim())); + ignores.add(InetAddressAndPort.getByNameOverrideDefaults(node.trim(), storagePort)); } } catch (UnknownHostException e) { @@ -554,6 +614,7 @@ public class LoaderOptions options.addOption("st", SSL_STORE_TYPE, "STORE-TYPE", "Client SSL: type of store"); options.addOption("ciphers", SSL_CIPHER_SUITES, "CIPHER-SUITES", "Client SSL: comma-separated list of encryption suites to use"); options.addOption("f", CONFIG_PATH, "path to config file", "cassandra.yaml file path for streaming throughput and client/server SSL."); + options.addOption("spd", ALLOW_SERVER_PORT_DISCOVERY_OPTION, "allow server port discovery", "Use ports published by server to decide how to connect. With SSL requires StartTLS to be used."); return options; } diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java b/src/java/org/apache/cassandra/tools/NodeProbe.java index 8acb3c107f..d330ed40c6 100644 --- a/src/java/org/apache/cassandra/tools/NodeProbe.java +++ b/src/java/org/apache/cassandra/tools/NodeProbe.java @@ -450,39 +450,39 @@ public class NodeProbe implements AutoCloseable ssProxy.drain(); } - public Map getTokenToEndpointMap() + public Map getTokenToEndpointMap(boolean withPort) { - return ssProxy.getTokenToEndpointMap(); + return withPort ? ssProxy.getTokenToEndpointWithPortMap() : ssProxy.getTokenToEndpointMap(); } - public List getLiveNodes() + public List getLiveNodes(boolean withPort) { - return ssProxy.getLiveNodes(); + return withPort ? ssProxy.getLiveNodesWithPort() : ssProxy.getLiveNodes(); } - public List getJoiningNodes() + public List getJoiningNodes(boolean withPort) { - return ssProxy.getJoiningNodes(); + return withPort ? ssProxy.getJoiningNodesWithPort() : ssProxy.getJoiningNodes(); } - public List getLeavingNodes() + public List getLeavingNodes(boolean withPort) { - return ssProxy.getLeavingNodes(); + return withPort ? ssProxy.getLeavingNodesWithPort() : ssProxy.getLeavingNodes(); } - public List getMovingNodes() + public List getMovingNodes(boolean withPort) { - return ssProxy.getMovingNodes(); + return withPort ? ssProxy.getMovingNodesWithPort() : ssProxy.getMovingNodes(); } - public List getUnreachableNodes() + public List getUnreachableNodes(boolean withPort) { - return ssProxy.getUnreachableNodes(); + return withPort ? ssProxy.getUnreachableNodesWithPort() : ssProxy.getUnreachableNodes(); } - public Map getLoadMap() + public Map getLoadMap(boolean withPort) { - return ssProxy.getLoadMap(); + return withPort ? ssProxy.getLoadMapWithPort() : ssProxy.getLoadMap(); } public Map getOwnership() @@ -490,11 +490,21 @@ public class NodeProbe implements AutoCloseable return ssProxy.getOwnership(); } + public Map getOwnershipWithPort() + { + return ssProxy.getOwnershipWithPort(); + } + public Map effectiveOwnership(String keyspace) throws IllegalStateException { return ssProxy.effectiveOwnership(keyspace); } + public Map effectiveOwnershipWithPort(String keyspace) throws IllegalStateException + { + return ssProxy.effectiveOwnershipWithPort(keyspace); + } + public CacheServiceMBean getCacheServiceMBean() { String cachePath = "org.apache.cassandra.db:type=Caches"; @@ -557,9 +567,9 @@ public class NodeProbe implements AutoCloseable return ssProxy.getLocalHostId(); } - public Map getHostIdMap() + public Map getHostIdMap(boolean withPort) { - return ssProxy.getEndpointToHostId(); + return withPort ? ssProxy.getEndpointWithPortToHostId() : ssProxy.getEndpointToHostId(); } public String getLoadString() @@ -686,9 +696,9 @@ public class NodeProbe implements AutoCloseable ssProxy.removeNode(token); } - public String getRemovalStatus() + public String getRemovalStatus(boolean withPort) { - return ssProxy.getRemovalStatus(); + return withPort ? ssProxy.getRemovalStatusWithPort() : ssProxy.getRemovalStatus(); } public void forceRemoveCompletion() @@ -775,6 +785,11 @@ public class NodeProbe implements AutoCloseable ssProxy.setHintedHandoffThrottleInKB(throttleInKB); } + public List getEndpointsWithPort(String keyspace, String cf, String key) + { + return ssProxy.getNaturalEndpointsWithPort(keyspace, cf, key); + } + public List getEndpoints(String keyspace, String cf, String key) { return ssProxy.getNaturalEndpoints(keyspace, cf, key); @@ -1144,9 +1159,9 @@ public class NodeProbe implements AutoCloseable ssProxy.rebuildSecondaryIndex(ksName, cfName, idxNames); } - public String getGossipInfo() + public String getGossipInfo(boolean withPort) { - return fdProxy.getAllEndpointStates(); + return withPort ? fdProxy.getAllEndpointStatesWithPort() : fdProxy.getAllEndpointStates(); } public void stop(String string) @@ -1212,9 +1227,9 @@ public class NodeProbe implements AutoCloseable return ssProxy.getSchemaVersion(); } - public List describeRing(String keyspaceName) throws IOException + public List describeRing(String keyspaceName, boolean withPort) throws IOException { - return ssProxy.describeRingJMX(keyspaceName); + return withPort ? ssProxy.describeRingWithPortJMX(keyspaceName) : ssProxy.describeRingJMX(keyspaceName); } public void rebuild(String sourceDc, String keyspace, String tokens, String specificSources) @@ -1580,11 +1595,11 @@ public class NodeProbe implements AutoCloseable } } - public TabularData getFailureDetectorPhilValues() + public TabularData getFailureDetectorPhilValues(boolean withPort) { try { - return fdProxy.getPhiValues(); + return withPort ? fdProxy.getPhiValuesWithPort() : fdProxy.getPhiValues(); } catch (OpenDataException e) { diff --git a/src/java/org/apache/cassandra/tools/NodeTool.java b/src/java/org/apache/cassandra/tools/NodeTool.java index 59d4eadbc6..d707499f6b 100644 --- a/src/java/org/apache/cassandra/tools/NodeTool.java +++ b/src/java/org/apache/cassandra/tools/NodeTool.java @@ -248,6 +248,9 @@ public class NodeTool @Option(type = OptionType.GLOBAL, name = {"-pwf", "--password-file"}, description = "Path to the JMX password file") private String passwordFilePath = EMPTY; + @Option(type = OptionType.GLOBAL, name = { "-wp", "--with-port"}, description = "Operate in 4.0 mode with hosts disambiguated by port number", arity = 0) + protected boolean withPort = false; + @Override public void run() { @@ -398,4 +401,27 @@ public class NodeTool } return ownershipByDc; } + + public static SortedMap getOwnershipByDcWithPort(NodeProbe probe, boolean resolveIp, + Map tokenToEndpoint, + Map ownerships) + { + SortedMap ownershipByDc = Maps.newTreeMap(); + EndpointSnitchInfoMBean epSnitchInfo = probe.getEndpointSnitchInfoProxy(); + try + { + for (Entry tokenAndEndPoint : tokenToEndpoint.entrySet()) + { + String dc = epSnitchInfo.getDatacenter(tokenAndEndPoint.getValue()); + if (!ownershipByDc.containsKey(dc)) + ownershipByDc.put(dc, new SetHostStatWithPort(resolveIp)); + ownershipByDc.get(dc).add(tokenAndEndPoint.getKey(), tokenAndEndPoint.getValue(), ownerships); + } + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + return ownershipByDc; + } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java b/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java index 5228468d09..ed91b8b460 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java +++ b/src/java/org/apache/cassandra/tools/nodetool/DescribeCluster.java @@ -49,10 +49,10 @@ public class DescribeCluster extends NodeToolCmd // display schema version for each node System.out.println("\tSchema versions:"); - Map> schemaVersions = probe.getSpProxy().getSchemaVersions(); + Map> schemaVersions = withPort ? probe.getSpProxy().getSchemaVersionsWithPort() : probe.getSpProxy().getSchemaVersions(); for (String version : schemaVersions.keySet()) { System.out.println(format("\t\t%s: %s%n", version, schemaVersions.get(version))); } } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java b/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java index 2a73c2a1e8..c57e54dbe6 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java +++ b/src/java/org/apache/cassandra/tools/nodetool/DescribeRing.java @@ -39,7 +39,7 @@ public class DescribeRing extends NodeToolCmd System.out.println("TokenRange: "); try { - for (String tokenRangeString : probe.describeRing(keyspace)) + for (String tokenRangeString : probe.describeRing(keyspace, withPort)) { System.out.println("\t" + tokenRangeString); } @@ -48,4 +48,4 @@ public class DescribeRing extends NodeToolCmd throw new RuntimeException(e); } } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java b/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java index b3ffb6d39a..896663deda 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java +++ b/src/java/org/apache/cassandra/tools/nodetool/FailureDetectorInfo.java @@ -33,7 +33,7 @@ public class FailureDetectorInfo extends NodeToolCmd @Override public void execute(NodeProbe probe) { - TabularData data = probe.getFailureDetectorPhilValues(); + TabularData data = probe.getFailureDetectorPhilValues(withPort); System.out.printf("%10s,%16s%n", "Endpoint", "Phi"); for (Object o : data.keySet()) { diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java b/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java index 922ae262ab..c0adb2a72f 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java +++ b/src/java/org/apache/cassandra/tools/nodetool/GetEndpoints.java @@ -42,10 +42,20 @@ public class GetEndpoints extends NodeToolCmd String table = args.get(1); String key = args.get(2); - List endpoints = probe.getEndpoints(ks, table, key); - for (InetAddress endpoint : endpoints) + if (withPort) { - System.out.println(endpoint.getHostAddress()); + for (String endpoint : probe.getEndpointsWithPort(ks, table, key)) + { + System.out.println(endpoint); + } + } + else + { + List endpoints = probe.getEndpoints(ks, table, key); + for (InetAddress endpoint : endpoints) + { + System.out.println(endpoint.getHostAddress()); + } } } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java b/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java index 1b4b9793dd..182c3952d6 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java +++ b/src/java/org/apache/cassandra/tools/nodetool/GossipInfo.java @@ -28,6 +28,6 @@ public class GossipInfo extends NodeToolCmd @Override public void execute(NodeProbe probe) { - System.out.println(probe.getGossipInfo()); + System.out.println(probe.getGossipInfo(withPort)); } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java b/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java new file mode 100644 index 0000000000..54cda17c42 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/HostStatWithPort.java @@ -0,0 +1,44 @@ +/* + * 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.cassandra.tools.nodetool; + +import org.apache.cassandra.locator.InetAddressAndPort; + +public class HostStatWithPort +{ + public final InetAddressAndPort endpoint; + public final boolean resolveIp; + public final Float owns; + public final String token; + + public HostStatWithPort(String token, InetAddressAndPort endpoint, boolean resolveIp, Float owns) + { + this.token = token; + this.endpoint = endpoint; + this.resolveIp = resolveIp; + this.owns = owns; + } + + public String ipOrDns() + { + return resolveIp ? + endpoint.address.getHostName() + ":" + endpoint.port : + endpoint.toString(); + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/NetStats.java b/src/java/org/apache/cassandra/tools/nodetool/NetStats.java index 2312097b8b..2702d9e57b 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/NetStats.java +++ b/src/java/org/apache/cassandra/tools/nodetool/NetStats.java @@ -50,11 +50,11 @@ public class NetStats extends NodeToolCmd System.out.printf("%s %s%n", status.streamOperation.getDescription(), status.planId.toString()); for (SessionInfo info : status.sessions) { - System.out.printf(" %s", info.peer.toString()); + System.out.printf(" %s", info.peer.toString(withPort)); // print private IP when it is used if (!info.peer.equals(info.connecting)) { - System.out.printf(" (using %s)", info.connecting.toString()); + System.out.printf(" (using %s)", info.connecting.toString(withPort)); } System.out.printf("%n"); if (!info.receivingSummaries.isEmpty()) @@ -65,7 +65,7 @@ public class NetStats extends NodeToolCmd System.out.printf(" Receiving %d files, %d bytes total. Already received %d files, %d bytes total%n", info.getTotalFilesToReceive(), info.getTotalSizeToReceive(), info.getTotalFilesReceived(), info.getTotalSizeReceived()); for (ProgressInfo progress : info.getReceivingFiles()) { - System.out.printf(" %s%n", progress.toString()); + System.out.printf(" %s%n", progress.toString(withPort)); } } if (!info.sendingSummaries.isEmpty()) @@ -76,7 +76,7 @@ public class NetStats extends NodeToolCmd System.out.printf(" Sending %d files, %d bytes total. Already sent %d files, %d bytes total%n", info.getTotalFilesToSend(), info.getTotalSizeToSend(), info.getTotalFilesSent(), info.getTotalSizeSent()); for (ProgressInfo progress : info.getSendingFiles()) { - System.out.printf(" %s%n", progress.toString()); + System.out.printf(" %s%n", progress.toString(withPort)); } } } @@ -131,4 +131,4 @@ public class NetStats extends NodeToolCmd System.out.printf("%-25s%10s%10s%15s%10s%n", "Gossip messages", "n/a", pending, completed, dropped); } } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java b/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java index 7312597497..bd40abaa1a 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RemoveNode.java @@ -36,10 +36,10 @@ public class RemoveNode extends NodeToolCmd switch (removeOperation) { case "status": - System.out.println("RemovalStatus: " + probe.getRemovalStatus()); + System.out.println("RemovalStatus: " + probe.getRemovalStatus(withPort)); break; case "force": - System.out.println("RemovalStatus: " + probe.getRemovalStatus()); + System.out.println("RemovalStatus: " + probe.getRemovalStatus(withPort)); probe.forceRemoveCompletion(); break; default: @@ -47,4 +47,4 @@ public class RemoveNode extends NodeToolCmd break; } } -} \ No newline at end of file +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java b/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java index 0b4f76719a..ba3cf624e5 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java +++ b/src/java/org/apache/cassandra/tools/nodetool/RepairAdmin.java @@ -57,7 +57,8 @@ public class RepairAdmin extends NodeTool.NodeToolCmd "state", "last activity", "coordinator", - "participants"); + "participants", + "participants_wp"); private List sessionValues(Map session, int now) @@ -67,7 +68,8 @@ public class RepairAdmin extends NodeTool.NodeToolCmd session.get(LocalSessionInfo.STATE), Integer.toString(now - updated) + " (s)", session.get(LocalSessionInfo.COORDINATOR), - session.get(LocalSessionInfo.PARTICIPANTS)); + session.get(LocalSessionInfo.PARTICIPANTS), + session.get(LocalSessionInfo.PARTICIPANTS_WP)); } private void listSessions(ActiveRepairServiceMBean repairServiceProxy) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Ring.java b/src/java/org/apache/cassandra/tools/nodetool/Ring.java index 9c389c2515..105726cc29 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Ring.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Ring.java @@ -51,59 +51,103 @@ public class Ring extends NodeToolCmd @Override public void execute(NodeProbe probe) { - Map tokensToEndpoints = probe.getTokenToEndpointMap(); - LinkedHashMultimap endpointsToTokens = LinkedHashMultimap.create(); - boolean haveVnodes = false; - for (Map.Entry entry : tokensToEndpoints.entrySet()) - { - haveVnodes |= endpointsToTokens.containsKey(entry.getValue()); - endpointsToTokens.put(entry.getValue(), entry.getKey()); - } - - int maxAddressLength = Collections.max(endpointsToTokens.keys(), new Comparator() - { - @Override - public int compare(String first, String second) - { - return Integer.compare(first.length(), second.length()); - } - }).length(); - - String formatPlaceholder = "%%-%ds %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n"; - String format = format(formatPlaceholder, maxAddressLength); - - StringBuilder errors = new StringBuilder(); - boolean showEffectiveOwnership = true; - // Calculate per-token ownership of the ring - Map ownerships; try { - ownerships = probe.effectiveOwnership(keyspace); - } - catch (IllegalStateException ex) + Map tokensToEndpoints = probe.getTokenToEndpointMap(withPort); + LinkedHashMultimap endpointsToTokens = LinkedHashMultimap.create(); + boolean haveVnodes = false; + for (Map.Entry entry : tokensToEndpoints.entrySet()) + { + haveVnodes |= endpointsToTokens.containsKey(entry.getValue()); + endpointsToTokens.put(entry.getValue(), entry.getKey()); + } + + int maxAddressLength = Collections.max(endpointsToTokens.keys(), new Comparator() + { + @Override + public int compare(String first, String second) + { + return Integer.compare(first.length(), second.length()); + } + }).length(); + + String formatPlaceholder = "%%-%ds %%-12s%%-7s%%-8s%%-16s%%-20s%%-44s%%n"; + String format = format(formatPlaceholder, maxAddressLength); + + StringBuilder errors = new StringBuilder(); + boolean showEffectiveOwnership = true; + + if (withPort) + { + // Calculate per-token ownership of the ring + Map ownerships; + try + { + ownerships = probe.effectiveOwnershipWithPort(keyspace); + } + catch (IllegalStateException ex) + { + ownerships = probe.getOwnershipWithPort(); + errors.append("Note: ").append(ex.getMessage()).append("%n"); + showEffectiveOwnership = false; + } + catch (IllegalArgumentException ex) + { + System.out.printf("%nError: %s%n", ex.getMessage()); + return; + } + + + System.out.println(); + for (Entry entry : NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) + printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); + + if (haveVnodes) + { + System.out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); + System.out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); + } + + System.out.printf("%n " + errors.toString()); + } + else + { + // Calculate per-token ownership of the ring + Map ownerships; + try + { + ownerships = probe.effectiveOwnership(keyspace); + } + catch (IllegalStateException ex) + { + ownerships = probe.getOwnership(); + errors.append("Note: ").append(ex.getMessage()).append("%n"); + showEffectiveOwnership = false; + } + catch (IllegalArgumentException ex) + { + System.out.printf("%nError: %s%n", ex.getMessage()); + return; + } + + + System.out.println(); + for (Entry entry : NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) + printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(), showEffectiveOwnership); + + if (haveVnodes) + { + System.out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); + System.out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); + } + + System.out.printf("%n " + errors.toString()); + } + } catch (Exception e) { - ownerships = probe.getOwnership(); - errors.append("Note: ").append(ex.getMessage()).append("%n"); - showEffectiveOwnership = false; + e.printStackTrace(); + throw e; } - catch (IllegalArgumentException ex) - { - System.out.printf("%nError: %s%n", ex.getMessage()); - return; - } - - - System.out.println(); - for (Entry entry : NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships).entrySet()) - printDc(probe, format, entry.getKey(), endpointsToTokens, entry.getValue(),showEffectiveOwnership); - - if (haveVnodes) - { - System.out.println(" Warning: \"nodetool ring\" is used to output all the tokens of a node."); - System.out.println(" To view status related info of a node use \"nodetool status\" instead.\n"); - } - - System.out.printf("%n " + errors.toString()); } private void printDc(NodeProbe probe, String format, @@ -111,12 +155,12 @@ public class Ring extends NodeToolCmd LinkedHashMultimap endpointsToTokens, SetHostStat hoststats,boolean showEffectiveOwnership) { - Collection liveNodes = probe.getLiveNodes(); - Collection deadNodes = probe.getUnreachableNodes(); - Collection joiningNodes = probe.getJoiningNodes(); - Collection leavingNodes = probe.getLeavingNodes(); - Collection movingNodes = probe.getMovingNodes(); - Map loadMap = probe.getLoadMap(); + Collection liveNodes = probe.getLiveNodes(false); + Collection deadNodes = probe.getUnreachableNodes(false); + Collection joiningNodes = probe.getJoiningNodes(false); + Collection leavingNodes = probe.getLeavingNodes(false); + Collection movingNodes = probe.getMovingNodes(false); + Map loadMap = probe.getLoadMap(false); System.out.println("Datacenter: " + dc); System.out.println("=========="); @@ -174,4 +218,73 @@ public class Ring extends NodeToolCmd } System.out.println(); } + + private void printDc(NodeProbe probe, String format, + String dc, + LinkedHashMultimap endpointsToTokens, + SetHostStatWithPort hoststats,boolean showEffectiveOwnership) + { + Collection liveNodes = probe.getLiveNodes(true); + Collection deadNodes = probe.getUnreachableNodes(true); + Collection joiningNodes = probe.getJoiningNodes(true); + Collection leavingNodes = probe.getLeavingNodes(true); + Collection movingNodes = probe.getMovingNodes(true); + Map loadMap = probe.getLoadMap(true); + + System.out.println("Datacenter: " + dc); + System.out.println("=========="); + + // get the total amount of replicas for this dc and the last token in this dc's ring + List tokens = new ArrayList<>(); + String lastToken = ""; + + for (HostStatWithPort stat : hoststats) + { + tokens.addAll(endpointsToTokens.get(stat.endpoint.toString())); + lastToken = tokens.get(tokens.size() - 1); + } + + System.out.printf(format, "Address", "Rack", "Status", "State", "Load", "Owns", "Token"); + + if (hoststats.size() > 1) + System.out.printf(format, "", "", "", "", "", "", lastToken); + else + System.out.println(); + + for (HostStatWithPort stat : hoststats) + { + String endpoint = stat.endpoint.toString(); + String rack; + try + { + rack = probe.getEndpointSnitchInfoProxy().getRack(endpoint); + } + catch (UnknownHostException e) + { + rack = "Unknown"; + } + + String status = liveNodes.contains(endpoint) + ? "Up" + : deadNodes.contains(endpoint) + ? "Down" + : "?"; + + String state = "Normal"; + + if (joiningNodes.contains(endpoint)) + state = "Joining"; + else if (leavingNodes.contains(endpoint)) + state = "Leaving"; + else if (movingNodes.contains(endpoint)) + state = "Moving"; + + String load = loadMap.containsKey(endpoint) + ? loadMap.get(endpoint) + : "?"; + String owns = stat.owns != null && showEffectiveOwnership? new DecimalFormat("##0.00%").format(stat.owns) : "?"; + System.out.printf(format, stat.ipOrDns(), rack, status, state, load, owns, stat.token); + } + System.out.println(); + } } diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java b/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java new file mode 100644 index 0000000000..67cd46498e --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/SetHostStatWithPort.java @@ -0,0 +1,56 @@ +/* + * 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.cassandra.tools.nodetool; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.cassandra.locator.InetAddressAndPort; + +public class SetHostStatWithPort implements Iterable +{ + final List hostStats = new ArrayList<>(); + final boolean resolveIp; + + public SetHostStatWithPort(boolean resolveIp) + { + this.resolveIp = resolveIp; + } + + public int size() + { + return hostStats.size(); + } + + @Override + public Iterator iterator() + { + return hostStats.iterator(); + } + + public void add(String token, String host, Map ownerships) throws UnknownHostException + { + InetAddressAndPort endpoint = InetAddressAndPort.getByName(host); + Float owns = ownerships.get(endpoint); + hostStats.add(new HostStatWithPort(token, endpoint, resolveIp, owns)); + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/Status.java b/src/java/org/apache/cassandra/tools/nodetool/Status.java index f3ef606246..49724a5dfc 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Status.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Status.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.SortedMap; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; @@ -55,72 +56,130 @@ public class Status extends NodeToolCmd @Override public void execute(NodeProbe probe) { - joiningNodes = probe.getJoiningNodes(); - leavingNodes = probe.getLeavingNodes(); - movingNodes = probe.getMovingNodes(); - loadMap = probe.getLoadMap(); - Map tokensToEndpoints = probe.getTokenToEndpointMap(); - liveNodes = probe.getLiveNodes(); - unreachableNodes = probe.getUnreachableNodes(); - hostIDMap = probe.getHostIdMap(); + joiningNodes = probe.getJoiningNodes(withPort); + leavingNodes = probe.getLeavingNodes(withPort); + movingNodes = probe.getMovingNodes(withPort); + loadMap = probe.getLoadMap(withPort); + Map tokensToEndpoints = probe.getTokenToEndpointMap(withPort); + liveNodes = probe.getLiveNodes(withPort); + unreachableNodes = probe.getUnreachableNodes(withPort); + hostIDMap = probe.getHostIdMap(withPort); epSnitchInfo = probe.getEndpointSnitchInfoProxy(); StringBuilder errors = new StringBuilder(); - Map ownerships = null; - boolean hasEffectiveOwns = false; - try + if (withPort) { - ownerships = probe.effectiveOwnership(keyspace); - hasEffectiveOwns = true; - } - catch (IllegalStateException e) - { - ownerships = probe.getOwnership(); - errors.append("Note: ").append(e.getMessage()).append("%n"); - } - catch (IllegalArgumentException ex) - { - System.out.printf("%nError: %s%n", ex.getMessage()); - System.exit(1); - } - - SortedMap dcs = NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships); - - // More tokens than nodes (aka vnodes)? - if (dcs.values().size() < tokensToEndpoints.keySet().size()) - isTokenPerNode = false; - - findMaxAddressLength(dcs); - - // Datacenters - for (Map.Entry dc : dcs.entrySet()) - { - String dcHeader = String.format("Datacenter: %s%n", dc.getKey()); - System.out.printf(dcHeader); - for (int i = 0; i < (dcHeader.length() - 1); i++) System.out.print('='); - System.out.println(); - - // Legend - System.out.println("Status=Up/Down"); - System.out.println("|/ State=Normal/Leaving/Joining/Moving"); - - printNodesHeader(hasEffectiveOwns, isTokenPerNode); - - ArrayListMultimap hostToTokens = ArrayListMultimap.create(); - for (HostStat stat : dc.getValue()) - hostToTokens.put(stat.endpoint, stat); - - for (InetAddress endpoint : hostToTokens.keySet()) + Map ownerships = null; + boolean hasEffectiveOwns = false; + try { - Float owns = ownerships.get(endpoint); - List tokens = hostToTokens.get(endpoint); - printNode(endpoint.getHostAddress(), owns, tokens, hasEffectiveOwns, isTokenPerNode); + ownerships = probe.effectiveOwnershipWithPort(keyspace); + hasEffectiveOwns = true; } + catch (IllegalStateException e) + { + ownerships = probe.getOwnershipWithPort(); + errors.append("Note: ").append(e.getMessage()).append("%n"); + } + catch (IllegalArgumentException ex) + { + System.out.printf("%nError: %s%n", ex.getMessage()); + System.exit(1); + } + + SortedMap dcs = NodeTool.getOwnershipByDcWithPort(probe, resolveIp, tokensToEndpoints, ownerships); + + // More tokens than nodes (aka vnodes)? + if (dcs.values().size() < tokensToEndpoints.keySet().size()) + isTokenPerNode = false; + + findMaxAddressLengthWithPort(dcs); + + // Datacenters + for (Map.Entry dc : dcs.entrySet()) + { + String dcHeader = String.format("Datacenter: %s%n", dc.getKey()); + System.out.printf(dcHeader); + for (int i = 0; i < (dcHeader.length() - 1); i++) System.out.print('='); + System.out.println(); + + // Legend + System.out.println("Status=Up/Down"); + System.out.println("|/ State=Normal/Leaving/Joining/Moving"); + + printNodesHeader(hasEffectiveOwns, isTokenPerNode); + + ArrayListMultimap hostToTokens = ArrayListMultimap.create(); + for (HostStatWithPort stat : dc.getValue()) + hostToTokens.put(stat.endpoint, stat); + + for (InetAddressAndPort endpoint : hostToTokens.keySet()) + { + Float owns = ownerships.get(endpoint); + List tokens = hostToTokens.get(endpoint); + printNodeWithPort(endpoint.toString(), owns, tokens, hasEffectiveOwns, isTokenPerNode); + } + } + + System.out.printf("%n" + errors.toString()); } + else + { + Map ownerships = null; + boolean hasEffectiveOwns = false; + try + { + ownerships = probe.effectiveOwnership(keyspace); + hasEffectiveOwns = true; + } + catch (IllegalStateException e) + { + ownerships = probe.getOwnership(); + errors.append("Note: ").append(e.getMessage()).append("%n"); + } + catch (IllegalArgumentException ex) + { + System.out.printf("%nError: %s%n", ex.getMessage()); + System.exit(1); + } - System.out.printf("%n" + errors.toString()); + SortedMap dcs = NodeTool.getOwnershipByDc(probe, resolveIp, tokensToEndpoints, ownerships); + // More tokens than nodes (aka vnodes)? + if (dcs.values().size() < tokensToEndpoints.keySet().size()) + isTokenPerNode = false; + + findMaxAddressLength(dcs); + + // Datacenters + for (Map.Entry dc : dcs.entrySet()) + { + String dcHeader = String.format("Datacenter: %s%n", dc.getKey()); + System.out.printf(dcHeader); + for (int i = 0; i < (dcHeader.length() - 1); i++) System.out.print('='); + System.out.println(); + + // Legend + System.out.println("Status=Up/Down"); + System.out.println("|/ State=Normal/Leaving/Joining/Moving"); + + printNodesHeader(hasEffectiveOwns, isTokenPerNode); + + ArrayListMultimap hostToTokens = ArrayListMultimap.create(); + for (HostStat stat : dc.getValue()) + hostToTokens.put(stat.endpoint, stat); + + for (InetAddress endpoint : hostToTokens.keySet()) + { + Float owns = ownerships.get(endpoint); + List tokens = hostToTokens.get(endpoint); + printNode(endpoint.getHostAddress(), owns, tokens, hasEffectiveOwns, isTokenPerNode); + } + } + + System.out.printf("%n" + errors.toString()); + } } private void findMaxAddressLength(Map dcs) @@ -135,6 +194,18 @@ public class Status extends NodeToolCmd } } + private void findMaxAddressLengthWithPort(Map dcs) + { + maxAddressLength = 0; + for (Map.Entry dc : dcs.entrySet()) + { + for (HostStatWithPort stat : dc.getValue()) + { + maxAddressLength = Math.max(maxAddressLength, stat.ipOrDns().length()); + } + } + } + private void printNodesHeader(boolean hasEffectiveOwns, boolean isTokenPerNode) { String fmt = getFormat(hasEffectiveOwns, isTokenPerNode); @@ -177,6 +248,37 @@ public class Status extends NodeToolCmd System.out.printf(fmt, status, state, endpointDns, load, tokens.size(), strOwns, hostID, rack); } + private void printNodeWithPort(String endpoint, Float owns, List tokens, boolean hasEffectiveOwns, boolean isTokenPerNode) + { + String status, state, load, strOwns, hostID, rack, fmt; + fmt = getFormat(hasEffectiveOwns, isTokenPerNode); + if (liveNodes.contains(endpoint)) status = "U"; + else if (unreachableNodes.contains(endpoint)) status = "D"; + else status = "?"; + if (joiningNodes.contains(endpoint)) state = "J"; + else if (leavingNodes.contains(endpoint)) state = "L"; + else if (movingNodes.contains(endpoint)) state = "M"; + else state = "N"; + + load = loadMap.containsKey(endpoint) ? loadMap.get(endpoint) : "?"; + strOwns = owns != null && hasEffectiveOwns ? new DecimalFormat("##0.0%").format(owns) : "?"; + hostID = hostIDMap.get(endpoint); + + try + { + rack = epSnitchInfo.getRack(endpoint); + } catch (UnknownHostException e) + { + throw new RuntimeException(e); + } + + String endpointDns = tokens.get(0).ipOrDns(); + if (isTokenPerNode) + System.out.printf(fmt, status, state, endpointDns, load, strOwns, hostID, tokens.get(0).token, rack); + else + System.out.printf(fmt, status, state, endpointDns, load, tokens.size(), strOwns, hostID, rack); + } + private String getFormat( boolean hasEffectiveOwns, boolean isTokenPerNode) diff --git a/src/java/org/apache/cassandra/tracing/ExpiredTraceState.java b/src/java/org/apache/cassandra/tracing/ExpiredTraceState.java index 9230d38f67..bf95080201 100644 --- a/src/java/org/apache/cassandra/tracing/ExpiredTraceState.java +++ b/src/java/org/apache/cassandra/tracing/ExpiredTraceState.java @@ -29,7 +29,7 @@ class ExpiredTraceState extends TraceState ExpiredTraceState(TraceState delegate) { - super(FBUtilities.getBroadcastAddress(), delegate.sessionId, delegate.traceType); + super(FBUtilities.getBroadcastAddressAndPort(), delegate.sessionId, delegate.traceType); this.delegate = delegate; } diff --git a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java index 20c992cb9f..487ed6578f 100644 --- a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java +++ b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java @@ -48,29 +48,31 @@ public final class TraceKeyspace private static final TableMetadata Sessions = parse(SESSIONS, - "tracing sessions", - "CREATE TABLE %s (" - + "session_id uuid," - + "command text," - + "client inet," - + "coordinator inet," - + "duration int," - + "parameters map," - + "request text," - + "started_at timestamp," - + "PRIMARY KEY ((session_id)))"); + "tracing sessions", + "CREATE TABLE %s (" + + "session_id uuid," + + "command text," + + "client inet," + + "coordinator inet," + + "coordinator_port int," + + "duration int," + + "parameters map," + + "request text," + + "started_at timestamp," + + "PRIMARY KEY ((session_id)))"); private static final TableMetadata Events = parse(EVENTS, - "tracing events", - "CREATE TABLE %s (" - + "session_id uuid," - + "event_id timeuuid," - + "activity text," - + "source inet," - + "source_elapsed int," - + "thread text," - + "PRIMARY KEY ((session_id), event_id))"); + "tracing events", + "CREATE TABLE %s (" + + "session_id uuid," + + "event_id timeuuid," + + "activity text," + + "source inet," + + "source_port int," + + "source_elapsed int," + + "thread text," + + "PRIMARY KEY ((session_id), event_id))"); private static TableMetadata parse(String table, String description, String cql) { @@ -100,7 +102,8 @@ public final class TraceKeyspace builder.row() .ttl(ttl) .add("client", client) - .add("coordinator", FBUtilities.getBroadcastAddress()) + .add("coordinator", FBUtilities.getBroadcastAddressAndPort().address) + .add("coordinator_port", FBUtilities.getBroadcastAddressAndPort().port) .add("request", request) .add("started_at", new Date(startedAt)) .add("command", command) @@ -125,7 +128,8 @@ public final class TraceKeyspace .ttl(ttl); rowBuilder.add("activity", message) - .add("source", FBUtilities.getBroadcastAddress()) + .add("source", FBUtilities.getBroadcastAddressAndPort().address) + .add("source_port", FBUtilities.getBroadcastAddressAndPort().port) .add("thread", threadName); if (elapsed >= 0) diff --git a/src/java/org/apache/cassandra/tracing/TraceState.java b/src/java/org/apache/cassandra/tracing/TraceState.java index b4eff6b386..a53846cd7a 100644 --- a/src/java/org/apache/cassandra/tracing/TraceState.java +++ b/src/java/org/apache/cassandra/tracing/TraceState.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.tracing; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.List; import java.util.UUID; @@ -28,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Stopwatch; import org.slf4j.helpers.MessageFormatter; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.progress.ProgressEvent; import org.apache.cassandra.utils.progress.ProgressEventNotifier; @@ -40,7 +40,7 @@ import org.apache.cassandra.utils.progress.ProgressListener; public abstract class TraceState implements ProgressEventNotifier { public final UUID sessionId; - public final InetAddress coordinator; + public final InetAddressAndPort coordinator; public final Stopwatch watch; public final ByteBuffer sessionIdBytes; public final Tracing.TraceType traceType; @@ -63,7 +63,7 @@ public abstract class TraceState implements ProgressEventNotifier // See CASSANDRA-7626 for more details. private final AtomicInteger references = new AtomicInteger(1); - protected TraceState(InetAddress coordinator, UUID sessionId, Tracing.TraceType traceType) + protected TraceState(InetAddressAndPort coordinator, UUID sessionId, Tracing.TraceType traceType) { assert coordinator != null; assert sessionId != null; diff --git a/src/java/org/apache/cassandra/tracing/TraceStateImpl.java b/src/java/org/apache/cassandra/tracing/TraceStateImpl.java index 349000a436..2722406b2f 100644 --- a/src/java/org/apache/cassandra/tracing/TraceStateImpl.java +++ b/src/java/org/apache/cassandra/tracing/TraceStateImpl.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.tracing; -import java.net.InetAddress; import java.util.Collections; import java.util.Set; import java.util.UUID; @@ -36,6 +35,7 @@ import org.apache.cassandra.concurrent.StageManager; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.Mutation; import org.apache.cassandra.exceptions.OverloadedException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.StorageProxy; import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.WrappedRunnable; @@ -54,7 +54,7 @@ public class TraceStateImpl extends TraceState private final Set> pendingFutures = ConcurrentHashMap.newKeySet(); - public TraceStateImpl(InetAddress coordinator, UUID sessionId, Tracing.TraceType traceType) + public TraceStateImpl(InetAddressAndPort coordinator, UUID sessionId, Tracing.TraceType traceType) { super(coordinator, sessionId, traceType); } diff --git a/src/java/org/apache/cassandra/tracing/Tracing.java b/src/java/org/apache/cassandra/tracing/Tracing.java index 4cdddba3a1..55e36a4a7a 100644 --- a/src/java/org/apache/cassandra/tracing/Tracing.java +++ b/src/java/org/apache/cassandra/tracing/Tracing.java @@ -19,15 +19,17 @@ */ package org.apache.cassandra.tracing; +import java.io.IOException; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,8 +37,13 @@ import io.netty.util.concurrent.FastThreadLocal; import org.apache.cassandra.concurrent.ExecutorLocal; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.marshal.TimeUUIDType; +import org.apache.cassandra.io.IVersionedSerializer; +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.UUIDGen; @@ -48,8 +55,23 @@ import org.apache.cassandra.utils.UUIDGen; */ public abstract class Tracing implements ExecutorLocal { - public static final String TRACE_HEADER = "TraceSession"; - public static final String TRACE_TYPE = "TraceType"; + public static final IVersionedSerializer traceTypeSerializer = new IVersionedSerializer() + { + public void serialize(TraceType traceType, DataOutputPlus out, int version) throws IOException + { + out.write((byte)traceType.ordinal()); + } + + public TraceType deserialize(DataInputPlus in, int version) throws IOException + { + return TraceType.deserialize(in.readByte()); + } + + public long serializedSize(TraceType traceType, int version) + { + return 1; + } + }; public enum TraceType { @@ -83,7 +105,7 @@ public abstract class Tracing implements ExecutorLocal protected static final Logger logger = LoggerFactory.getLogger(Tracing.class); - private final InetAddress localAddress = FBUtilities.getLocalAddress(); + private final InetAddressAndPort localAddress = FBUtilities.getLocalAddressAndPort(); private final FastThreadLocal state = new FastThreadLocal<>(); @@ -228,21 +250,19 @@ public abstract class Tracing implements ExecutorLocal */ public TraceState initializeFromMessage(final MessageIn message) { - final byte[] sessionBytes = message.parameters.get(TRACE_HEADER); + final UUID sessionId = (UUID)message.parameters.get(ParameterType.TRACE_SESSION); - if (sessionBytes == null) + if (sessionId == null) return null; - assert sessionBytes.length == 16; - UUID sessionId = UUIDGen.getUUID(ByteBuffer.wrap(sessionBytes)); TraceState ts = get(sessionId); if (ts != null && ts.acquireReference()) return ts; - byte[] tmpBytes; + TraceType tmpType; TraceType traceType = TraceType.QUERY; - if ((tmpBytes = message.parameters.get(TRACE_TYPE)) != null) - traceType = TraceType.deserialize(tmpBytes[0]); + if ((tmpType = (TraceType)message.parameters.get(ParameterType.TRACE_TYPE)) != null) + traceType = tmpType; if (message.verb == MessagingService.Verb.REQUEST_RESPONSE) { @@ -257,16 +277,16 @@ public abstract class Tracing implements ExecutorLocal } } - public Map getTraceHeaders() + public List getTraceHeaders() { assert isTracing(); - return ImmutableMap.of( - TRACE_HEADER, UUIDGen.decompose(Tracing.instance.getSessionId()), - TRACE_TYPE, new byte[] { Tracing.TraceType.serialize(Tracing.instance.getTraceType()) }); + return ImmutableList.of( + ParameterType.TRACE_SESSION, Tracing.instance.getSessionId(), + ParameterType.TRACE_TYPE, Tracing.instance.getTraceType()); } - protected abstract TraceState newTraceState(InetAddress coordinator, UUID sessionId, Tracing.TraceType traceType); + protected abstract TraceState newTraceState(InetAddressAndPort coordinator, UUID sessionId, Tracing.TraceType traceType); // repair just gets a varargs method since it's so heavyweight anyway public static void traceRepair(String format, Object... args) diff --git a/src/java/org/apache/cassandra/tracing/TracingImpl.java b/src/java/org/apache/cassandra/tracing/TracingImpl.java index 789216e775..1e32f1019e 100644 --- a/src/java/org/apache/cassandra/tracing/TracingImpl.java +++ b/src/java/org/apache/cassandra/tracing/TracingImpl.java @@ -26,6 +26,7 @@ import java.util.UUID; import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.concurrent.StageManager; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.WrappedRunnable; @@ -93,7 +94,7 @@ class TracingImpl extends Tracing } @Override - protected TraceState newTraceState(InetAddress coordinator, UUID sessionId, TraceType traceType) + protected TraceState newTraceState(InetAddressAndPort coordinator, UUID sessionId, TraceType traceType) { return new TraceStateImpl(coordinator, sessionId, traceType); } diff --git a/src/java/org/apache/cassandra/transport/Event.java b/src/java/org/apache/cassandra/transport/Event.java index ed77e59986..77edf8a738 100644 --- a/src/java/org/apache/cassandra/transport/Event.java +++ b/src/java/org/apache/cassandra/transport/Event.java @@ -24,6 +24,7 @@ import java.util.List; import com.google.common.base.Objects; import io.netty.buffer.ByteBuf; +import org.apache.cassandra.locator.InetAddressAndPort; public abstract class Event { @@ -110,19 +111,19 @@ public abstract class Event this.change = change; } - public static TopologyChange newNode(InetAddress host, int port) + public static TopologyChange newNode(InetAddressAndPort address) { - return new TopologyChange(Change.NEW_NODE, new InetSocketAddress(host, port)); + return new TopologyChange(Change.NEW_NODE, new InetSocketAddress(address.address, address.port)); } - public static TopologyChange removedNode(InetAddress host, int port) + public static TopologyChange removedNode(InetAddressAndPort address) { - return new TopologyChange(Change.REMOVED_NODE, new InetSocketAddress(host, port)); + return new TopologyChange(Change.REMOVED_NODE, new InetSocketAddress(address.address, address.port)); } - public static TopologyChange movedNode(InetAddress host, int port) + public static TopologyChange movedNode(InetAddressAndPort address) { - return new TopologyChange(Change.MOVED_NODE, new InetSocketAddress(host, port)); + return new TopologyChange(Change.MOVED_NODE, new InetSocketAddress(address.address, address.port)); } // Assumes the type has already been deserialized @@ -181,14 +182,14 @@ public abstract class Event this.status = status; } - public static StatusChange nodeUp(InetAddress host, int port) + public static StatusChange nodeUp(InetAddressAndPort address) { - return new StatusChange(Status.UP, new InetSocketAddress(host, port)); + return new StatusChange(Status.UP, new InetSocketAddress(address.address, address.port)); } - public static StatusChange nodeDown(InetAddress host, int port) + public static StatusChange nodeDown(InetAddressAndPort address) { - return new StatusChange(Status.DOWN, new InetSocketAddress(host, port)); + return new StatusChange(Status.DOWN, new InetSocketAddress(address.address, address.port)); } // Assumes the type has already been deserialized diff --git a/src/java/org/apache/cassandra/transport/ProtocolVersion.java b/src/java/org/apache/cassandra/transport/ProtocolVersion.java index cd73c86255..838176ac29 100644 --- a/src/java/org/apache/cassandra/transport/ProtocolVersion.java +++ b/src/java/org/apache/cassandra/transport/ProtocolVersion.java @@ -43,7 +43,8 @@ public enum ProtocolVersion implements Comparable V2(2, "v2", false), // no longer supported V3(3, "v3", false), V4(4, "v4", false), - V5(5, "v5-beta", true); + V5(5, "v5", false), + V6(6, "v6-beta", true); /** The version number */ private final int num; @@ -62,7 +63,7 @@ public enum ProtocolVersion implements Comparable } /** The supported versions stored as an array, these should be private and are required for fast decoding*/ - private final static ProtocolVersion[] SUPPORTED_VERSIONS = new ProtocolVersion[] { V3, V4, V5 }; + private final static ProtocolVersion[] SUPPORTED_VERSIONS = new ProtocolVersion[] { V3, V4, V5, V6 }; final static ProtocolVersion MIN_SUPPORTED_VERSION = SUPPORTED_VERSIONS[0]; final static ProtocolVersion MAX_SUPPORTED_VERSION = SUPPORTED_VERSIONS[SUPPORTED_VERSIONS.length - 1]; @@ -73,8 +74,8 @@ public enum ProtocolVersion implements Comparable public final static EnumSet UNSUPPORTED = EnumSet.complementOf(SUPPORTED); /** The preferred versions */ - public final static ProtocolVersion CURRENT = V4; - public final static Optional BETA = Optional.of(V5); + public final static ProtocolVersion CURRENT = V5; + public final static Optional BETA = Optional.of(V6); public static List supportedVersions() { diff --git a/src/java/org/apache/cassandra/transport/Server.java b/src/java/org/apache/cassandra/transport/Server.java index d3f1c2cd97..cd04edcd32 100644 --- a/src/java/org/apache/cassandra/transport/Server.java +++ b/src/java/org/apache/cassandra/transport/Server.java @@ -51,6 +51,7 @@ import io.netty.util.internal.logging.Slf4JLoggerFactory; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.SchemaChangeListener; import org.apache.cassandra.security.SSLFactory; @@ -454,51 +455,32 @@ public class Server implements CassandraDaemon.Server // We keep track of the latest status change events we have sent to avoid sending duplicates // since StorageService may send duplicate notifications (CASSANDRA-7816, CASSANDRA-8236, CASSANDRA-9156) - private final Map latestEvents = new ConcurrentHashMap<>(); + private final Map latestEvents = new ConcurrentHashMap<>(); // We also want to delay delivering a NEW_NODE notification until the new node has set its RPC ready // state. This tracks the endpoints which have joined, but not yet signalled they're ready for clients - private final Set endpointsPendingJoinedNotification = ConcurrentHashMap.newKeySet(); - - - private static final InetAddress bindAll; - static - { - try - { - bindAll = InetAddress.getByAddress(new byte[4]); - } - catch (UnknownHostException e) - { - throw new AssertionError(e); - } - } + private final Set endpointsPendingJoinedNotification = ConcurrentHashMap.newKeySet(); private EventNotifier(Server server) { this.server = server; } - private InetAddress getRpcAddress(InetAddress endpoint) + private InetAddressAndPort getNativeAddress(InetAddressAndPort endpoint) { try { - InetAddress rpcAddress = InetAddress.getByName(StorageService.instance.getRpcaddress(endpoint)); - // If rpcAddress == 0.0.0.0 (i.e. bound on all addresses), returning that is not very helpful, - // so return the internal address (which is ok since "we're bound on all addresses"). - // Note that after all nodes are running a version that includes CASSANDRA-5899, rpcAddress should - // never be 0.0.0.0, so this can eventually be removed. - return rpcAddress.equals(bindAll) ? endpoint : rpcAddress; + return InetAddressAndPort.getByName(StorageService.instance.getNativeaddress(endpoint, true)); } catch (UnknownHostException e) { // That should not happen, so log an error, but return the // endpoint address since there's a good change this is right logger.error("Problem retrieving RPC address for {}", endpoint, e); - return endpoint; + return InetAddressAndPort.getByAddressOverrideDefaults(endpoint.address, DatabaseDescriptor.getNativeTransportPort()); } } - private void send(InetAddress endpoint, Event.NodeEvent event) + private void send(InetAddressAndPort endpoint, Event.NodeEvent event) { if (logger.isTraceEnabled()) logger.trace("Sending event for endpoint {}, rpc address {}", endpoint, event.nodeAddress()); @@ -508,8 +490,8 @@ public class Server implements CassandraDaemon.Server // then don't send the notification. This covers the case of rpc_address set to "localhost", // which is not useful to any driver and in fact may cauase serious problems to some drivers, // see CASSANDRA-10052 - if (!endpoint.equals(FBUtilities.getBroadcastAddress()) && - event.nodeAddress().equals(FBUtilities.getBroadcastRpcAddress())) + if (!endpoint.equals(FBUtilities.getBroadcastAddressAndPort()) && + event.nodeAddress().equals(FBUtilities.getJustBroadcastNativeAddress())) return; send(event); @@ -520,38 +502,38 @@ public class Server implements CassandraDaemon.Server server.connectionTracker.send(event); } - public void onJoinCluster(InetAddress endpoint) + public void onJoinCluster(InetAddressAndPort endpoint) { if (!StorageService.instance.isRpcReady(endpoint)) endpointsPendingJoinedNotification.add(endpoint); else - onTopologyChange(endpoint, Event.TopologyChange.newNode(getRpcAddress(endpoint), server.socket.getPort())); + onTopologyChange(endpoint, Event.TopologyChange.newNode(getNativeAddress(endpoint))); } - public void onLeaveCluster(InetAddress endpoint) + public void onLeaveCluster(InetAddressAndPort endpoint) { - onTopologyChange(endpoint, Event.TopologyChange.removedNode(getRpcAddress(endpoint), server.socket.getPort())); + onTopologyChange(endpoint, Event.TopologyChange.removedNode(getNativeAddress(endpoint))); } - public void onMove(InetAddress endpoint) + public void onMove(InetAddressAndPort endpoint) { - onTopologyChange(endpoint, Event.TopologyChange.movedNode(getRpcAddress(endpoint), server.socket.getPort())); + onTopologyChange(endpoint, Event.TopologyChange.movedNode(getNativeAddress(endpoint))); } - public void onUp(InetAddress endpoint) + public void onUp(InetAddressAndPort endpoint) { if (endpointsPendingJoinedNotification.remove(endpoint)) onJoinCluster(endpoint); - onStatusChange(endpoint, Event.StatusChange.nodeUp(getRpcAddress(endpoint), server.socket.getPort())); + onStatusChange(endpoint, Event.StatusChange.nodeUp(getNativeAddress(endpoint))); } - public void onDown(InetAddress endpoint) + public void onDown(InetAddressAndPort endpoint) { - onStatusChange(endpoint, Event.StatusChange.nodeDown(getRpcAddress(endpoint), server.socket.getPort())); + onStatusChange(endpoint, Event.StatusChange.nodeDown(getNativeAddress(endpoint))); } - private void onTopologyChange(InetAddress endpoint, Event.TopologyChange event) + private void onTopologyChange(InetAddressAndPort endpoint, Event.TopologyChange event) { if (logger.isTraceEnabled()) logger.trace("Topology changed event : {}, {}", endpoint, event.change); @@ -565,7 +547,7 @@ public class Server implements CassandraDaemon.Server } } - private void onStatusChange(InetAddress endpoint, Event.StatusChange event) + private void onStatusChange(InetAddressAndPort endpoint, Event.StatusChange event) { if (logger.isTraceEnabled()) logger.trace("Status changed event : {}, {}", endpoint, event.status); diff --git a/src/java/org/apache/cassandra/transport/messages/ErrorMessage.java b/src/java/org/apache/cassandra/transport/messages/ErrorMessage.java index 9163d56d57..84af41c6d3 100644 --- a/src/java/org/apache/cassandra/transport/messages/ErrorMessage.java +++ b/src/java/org/apache/cassandra/transport/messages/ErrorMessage.java @@ -33,6 +33,7 @@ import org.apache.cassandra.cql3.functions.FunctionName; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.WriteType; import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.transport.*; import org.apache.cassandra.utils.MD5Digest; @@ -88,14 +89,14 @@ public class ErrorMessage extends Message.Response // The number of failures is also present in protocol v5, but used instead to specify the size of the failure map int failure = body.readInt(); - Map failureReasonByEndpoint = new ConcurrentHashMap<>(); + Map failureReasonByEndpoint = new ConcurrentHashMap<>(); if (version.isGreaterOrEqualTo(ProtocolVersion.V5)) { for (int i = 0; i < failure; i++) { InetAddress endpoint = CBUtil.readInetAddr(body); RequestFailureReason failureReason = RequestFailureReason.fromCode(body.readUnsignedShort()); - failureReasonByEndpoint.put(endpoint, failureReason); + failureReasonByEndpoint.put(InetAddressAndPort.getByAddress(endpoint), failureReason); } } @@ -195,9 +196,9 @@ public class ErrorMessage extends Message.Response if (version.isGreaterOrEqualTo(ProtocolVersion.V5)) { - for (Map.Entry entry : rfe.failureReasonByEndpoint.entrySet()) + for (Map.Entry entry : rfe.failureReasonByEndpoint.entrySet()) { - CBUtil.writeInetAddr(entry.getKey(), dest); + CBUtil.writeInetAddr(entry.getKey().address, dest); dest.writeShort(entry.getValue().code); } } @@ -260,9 +261,9 @@ public class ErrorMessage extends Message.Response if (version.isGreaterOrEqualTo(ProtocolVersion.V5)) { - for (Map.Entry entry : rfe.failureReasonByEndpoint.entrySet()) + for (Map.Entry entry : rfe.failureReasonByEndpoint.entrySet()) { - size += CBUtil.sizeOfInetAddr(entry.getKey()); + size += CBUtil.sizeOfInetAddr(entry.getKey().address); size += 2; // RequestFailureReason code } } diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java index 1cb59d465a..3ca8b89593 100644 --- a/src/java/org/apache/cassandra/utils/FBUtilities.java +++ b/src/java/org/apache/cassandra/utils/FBUtilities.java @@ -58,6 +58,7 @@ import org.apache.cassandra.io.sstable.metadata.ValidationMetadata; import org.apache.cassandra.io.util.DataOutputBuffer; import org.apache.cassandra.io.util.DataOutputBufferFixed; import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.AsyncOneResponse; import org.codehaus.jackson.JsonFactory; @@ -78,7 +79,10 @@ public class FBUtilities private static volatile InetAddress localInetAddress; private static volatile InetAddress broadcastInetAddress; - private static volatile InetAddress broadcastRpcAddress; + private static volatile InetAddress broadcastNativeAddress; + private static volatile InetAddressAndPort broadcastNativeAddressAndPort; + private static volatile InetAddressAndPort broadcastInetAddressAndPort; + private static volatile InetAddressAndPort localInetAddressAndPort; public static int getAvailableProcessors() { @@ -92,9 +96,9 @@ public class FBUtilities public static final int MAX_UNSIGNED_SHORT = 0xFFFF; /** - * Please use getBroadcastAddress instead. You need this only when you have to listen/connect. + * Please use getJustBroadcastAddress instead. You need this only when you have to listen/connect. */ - public static InetAddress getLocalAddress() + public static InetAddress getJustLocalAddress() { if (localInetAddress == null) try @@ -110,30 +114,57 @@ public class FBUtilities return localInetAddress; } - public static InetAddress getBroadcastAddress() + public static InetAddressAndPort getLocalAddressAndPort() + { + if (localInetAddressAndPort == null) + { + localInetAddressAndPort = InetAddressAndPort.getByAddress(getJustLocalAddress()); + } + return localInetAddressAndPort; + } + + public static InetAddress getJustBroadcastAddress() { if (broadcastInetAddress == null) broadcastInetAddress = DatabaseDescriptor.getBroadcastAddress() == null - ? getLocalAddress() + ? getJustLocalAddress() : DatabaseDescriptor.getBroadcastAddress(); return broadcastInetAddress; } + public static InetAddressAndPort getBroadcastAddressAndPort() + { + if (broadcastInetAddressAndPort == null) + { + broadcastInetAddressAndPort = InetAddressAndPort.getByAddress(getJustBroadcastAddress()); + } + return broadcastInetAddressAndPort; + } + /** * THIS IS FOR TESTING ONLY!! */ public static void setBroadcastInetAddress(InetAddress addr) { broadcastInetAddress = addr; + broadcastInetAddressAndPort = InetAddressAndPort.getByAddress(broadcastInetAddress); } - public static InetAddress getBroadcastRpcAddress() + public static InetAddress getJustBroadcastNativeAddress() { - if (broadcastRpcAddress == null) - broadcastRpcAddress = DatabaseDescriptor.getBroadcastRpcAddress() == null + if (broadcastNativeAddress == null) + broadcastNativeAddress = DatabaseDescriptor.getBroadcastRpcAddress() == null ? DatabaseDescriptor.getRpcAddress() : DatabaseDescriptor.getBroadcastRpcAddress(); - return broadcastRpcAddress; + return broadcastNativeAddress; + } + + public static InetAddressAndPort getBroadcastNativeAddressAndPort() + { + if (broadcastNativeAddressAndPort == null) + broadcastNativeAddressAndPort = InetAddressAndPort.getByAddressOverrideDefaults(getJustBroadcastNativeAddress(), + DatabaseDescriptor.getNativeTransportPort()); + return broadcastNativeAddressAndPort; } public static Collection getAllLocalAddresses() @@ -838,7 +869,9 @@ public class FBUtilities public static void reset() { localInetAddress = null; + localInetAddressAndPort = null; broadcastInetAddress = null; - broadcastRpcAddress = null; + broadcastInetAddressAndPort = null; + broadcastNativeAddress = null; } } diff --git a/src/java/org/apache/cassandra/utils/JMXServerUtils.java b/src/java/org/apache/cassandra/utils/JMXServerUtils.java index e78ed01746..bb5c3ac077 100644 --- a/src/java/org/apache/cassandra/utils/JMXServerUtils.java +++ b/src/java/org/apache/cassandra/utils/JMXServerUtils.java @@ -236,7 +236,7 @@ public class JMXServerUtils String hostName; if (serverAddress == null) { - hostName = FBUtilities.getBroadcastAddress() instanceof Inet6Address ? "[::]" : "0.0.0.0"; + hostName = FBUtilities.getJustBroadcastAddress() instanceof Inet6Address ? "[::]" : "0.0.0.0"; } else { diff --git a/src/java/org/apache/cassandra/utils/Mx4jTool.java b/src/java/org/apache/cassandra/utils/Mx4jTool.java index cd42aca616..5baaea2549 100644 --- a/src/java/org/apache/cassandra/utils/Mx4jTool.java +++ b/src/java/org/apache/cassandra/utils/Mx4jTool.java @@ -79,7 +79,7 @@ public class Mx4jTool { String sAddress = System.getProperty("mx4jaddress"); if (StringUtils.isEmpty(sAddress)) - sAddress = FBUtilities.getBroadcastAddress().getHostAddress(); + sAddress = FBUtilities.getBroadcastAddressAndPort().address.getHostAddress(); return sAddress; } diff --git a/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java b/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java index 84a4c325b8..52fc6ac77e 100644 --- a/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java +++ b/src/java/org/apache/cassandra/utils/NativeSSTableLoaderClient.java @@ -19,10 +19,14 @@ package org.apache.cassandra.utils; import java.net.InetAddress; import java.nio.ByteBuffer; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.*; +import java.util.stream.Collectors; import com.datastax.driver.core.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.*; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.ColumnMetadata.ClusteringOrder; @@ -34,33 +38,46 @@ import org.apache.cassandra.dht.Token.TokenFactory; import org.apache.cassandra.io.sstable.SSTableLoader; import org.apache.cassandra.schema.TableMetadata; +import org.apache.cassandra.schema.CQLTypeParser; +import org.apache.cassandra.schema.SchemaKeyspace; +import org.apache.cassandra.schema.Types; + public class NativeSSTableLoaderClient extends SSTableLoader.Client { protected final Map tables; - private final Collection hosts; + private final Collection hosts; private final int port; + private final int storagePort; private final AuthProvider authProvider; private final SSLOptions sslOptions; + private final boolean allowServerPortDiscovery; - public NativeSSTableLoaderClient(Collection hosts, int port, String username, String password, SSLOptions sslOptions) + public NativeSSTableLoaderClient(Collection hosts, int nativePort, int storagePort, String username, String password, SSLOptions sslOptions, boolean allowServerPortDiscovery) { - this(hosts, port, new PlainTextAuthProvider(username, password), sslOptions); + this(hosts, nativePort, storagePort, new PlainTextAuthProvider(username, password), sslOptions, allowServerPortDiscovery); } - public NativeSSTableLoaderClient(Collection hosts, int port, AuthProvider authProvider, SSLOptions sslOptions) + public NativeSSTableLoaderClient(Collection hosts, int nativePort, int storagePort, AuthProvider authProvider, SSLOptions sslOptions, boolean allowServerPortDiscovery) { super(); this.tables = new HashMap<>(); this.hosts = hosts; - this.port = port; + this.port = nativePort; this.authProvider = authProvider; this.sslOptions = sslOptions; + this.allowServerPortDiscovery = allowServerPortDiscovery; + this.storagePort = storagePort; } public void init(String keyspace) { - Cluster.Builder builder = Cluster.builder().addContactPoints(hosts).withPort(port); + Set hostAddresses = hosts.stream().map(host -> host.getAddress()).collect(Collectors.toSet()); + Cluster.Builder builder = Cluster.builder().addContactPoints(hostAddresses).withPort(port).allowBetaProtocolVersion(); + + if (allowServerPortDiscovery) + builder = builder.allowServerPortDiscovery(); + if (sslOptions != null) builder.withSSL(sslOptions); if (authProvider != null) @@ -82,7 +99,18 @@ public class NativeSSTableLoaderClient extends SSTableLoader.Client Range range = new Range<>(tokenFactory.fromString(tokenRange.getStart().getValue().toString()), tokenFactory.fromString(tokenRange.getEnd().getValue().toString())); for (Host endpoint : endpoints) - addRangeForEndpoint(range, endpoint.getAddress()); + { + int portToUse; + if (allowServerPortDiscovery) + { + portToUse = endpoint.getBroadcastAddressOptPort().portOrElse(storagePort); + } + else + { + portToUse = storagePort; + } + addRangeForEndpoint(range, InetAddressAndPort.getByNameOverrideDefaults(endpoint.getAddress().getHostAddress(), portToUse)); + } } Types types = fetchTypes(keyspace, session); @@ -91,6 +119,10 @@ public class NativeSSTableLoaderClient extends SSTableLoader.Client // We only need the TableMetadata for the views, so we only load that. tables.putAll(fetchViews(keyspace, session, partitioner, types)); } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } } public TableMetadataRef getTableMetadata(String tableName) diff --git a/src/java/org/apache/cassandra/utils/UUIDGen.java b/src/java/org/apache/cassandra/utils/UUIDGen.java index 94b583272b..19a0f8346b 100644 --- a/src/java/org/apache/cassandra/utils/UUIDGen.java +++ b/src/java/org/apache/cassandra/utils/UUIDGen.java @@ -356,7 +356,7 @@ public class UUIDGen * The spec says that one option is to take as many source that identify * this node as possible and hash them together. That's what we do here by * gathering all the ip of this host. - * Note that FBUtilities.getBroadcastAddress() should be enough to uniquely + * Note that FBUtilities.getJustBroadcastAddress() should be enough to uniquely * identify the node *in the cluster* but it triggers DatabaseDescriptor * instanciation and the UUID generator is used in Stress for instance, * where we don't want to require the yaml. diff --git a/test/conf/cassandra.yaml b/test/conf/cassandra.yaml index ead2a889a1..640f9b3e34 100644 --- a/test/conf/cassandra.yaml +++ b/test/conf/cassandra.yaml @@ -17,6 +17,7 @@ hints_directory: build/test/cassandra/hints partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner listen_address: 127.0.0.1 storage_port: 7010 +ssl_storage_port: 7011 start_native_transport: true native_transport_port: 9042 column_index_size_in_kb: 4 @@ -27,7 +28,7 @@ disk_access_mode: mmap seed_provider: - class_name: org.apache.cassandra.locator.SimpleSeedProvider parameters: - - seeds: "127.0.0.1" + - seeds: "127.0.0.1:7010" endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch dynamic_snitch: true server_encryption_options: diff --git a/test/data/serialization/4.0/gms.EndpointState.bin b/test/data/serialization/4.0/gms.EndpointState.bin index fb7d16807661a08df9e467a35a5d39f86826bcca..17fc088128ecd46e9d08360c3aba84d08542637f 100644 GIT binary patch delta 40 vcmebDoS>#`W|(SjXq;xAY?NqXmS$>^W@M0Rm||*bZf<5`X=ud2z`z6m*TxA& delta 40 vcmebDoS>#`X=rAWYHn_9WNdC}Y+_=PW@edcW|o*{Vw`GeZeYs5z`z6m$;k-% diff --git a/test/data/serialization/4.0/gms.Gossip.bin b/test/data/serialization/4.0/gms.Gossip.bin index af5ac57146971c419dc6ebc3e8b7a0f79a3374ef..2fbd5d485421e99ded37338d8072fb558111320e 100644 GIT binary patch delta 84 qcmbQoxQx+<0RouV>KPcAq?3TWR3JVH5(Nn{q6z^;@u-~W5(NOFmI__~ delta 76 ocmZ3+IFHeV0Ros<>KPaqf$UTuJ_!;72{0iG0NFTHP4tNZ05%Q?VgLXD diff --git a/test/data/serialization/4.0/service.SyncComplete.bin b/test/data/serialization/4.0/service.SyncComplete.bin index ba84349bd9e53a32610a70fe6114bda6c9810f6b..15cccb85be8e0596775ec955700849c308f16edf 100644 GIT binary patch literal 554 zcmZQ(+g?07+FEY ze=xyT&%nSWodlworIQ#zVyHqO5nrvC0()n{=iBb3_UD{sTLlyY836(*Kn#*EM3ra! p0+wZ9;9{nQJHdv7+{q}N1Y|2fEn7ho%V-cNm`;zPiPOR10sx-wjqv~g literal 538 zcmZQ(+g?07+FEY ze=xyP&%nS0BA6LLd>{skKxt->n9qJu0j5)e&v!2j{JvZuY7@H05M2j5LKS> n3s{zcfs2{it^^wlaw5p73Q%j7QQaDv1O~dZ!>I0TaCiU!g?07+FEY le=xyT4`fOwfhZ;j#VnnKrUuAmfT{zs6hK@c$Y-Lj3jkiYJ0$=B literal 227 zcmZQ#+g?07+FEY he=xyP4`hM}CJ@1lCJW>;fK>o#1rP@aGMVUQGXMa#H>Lmp diff --git a/test/data/serialization/4.0/service.ValidationComplete.bin b/test/data/serialization/4.0/service.ValidationComplete.bin index 7433d64d56535f5bf65174702364d794880aa3b6..edc90b359706677a9db6ada65f67359883ec7fd2 100644 GIT binary patch delta 167 zcmaFN`I2)(I-@w_e6f>Z@rm} hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); // We want a big list of hosts so sorting takes time, making it much more likely to reproduce the // problem we're looking for. for (int i = 0; i < 100; i++) for (int j = 0; j < 256; j++) - hosts.add(InetAddress.getByAddress(new byte[]{127, 0, (byte)i, (byte)j})); + hosts.add(InetAddressAndPort.getByAddress(new byte[]{ 127, 0, (byte)i, (byte)j})); ScoreUpdater updater = new ScoreUpdater(dsnitch, hosts); updater.start(); - List result = null; + List result = null; for (int i = 0; i < ITERATIONS; i++) result = dsnitch.getSortedListByProximity(self, hosts); @@ -85,10 +84,10 @@ public class DynamicEndpointSnitchLongTest public volatile boolean stopped; private final DynamicEndpointSnitch dsnitch; - private final List hosts; + private final List hosts; private final Random random = new Random(); - public ScoreUpdater(DynamicEndpointSnitch dsnitch, List hosts) + public ScoreUpdater(DynamicEndpointSnitch dsnitch, List hosts) { this.dsnitch = dsnitch; this.hosts = hosts; @@ -98,7 +97,7 @@ public class DynamicEndpointSnitchLongTest { while (!stopped) { - InetAddress host = hosts.get(random.nextInt(hosts.size())); + InetAddressAndPort host = hosts.get(random.nextInt(hosts.size())); int score = random.nextInt(SCORE_RANGE); dsnitch.receiveTiming(host, score); } diff --git a/test/long/org/apache/cassandra/streaming/LongStreamingTest.java b/test/long/org/apache/cassandra/streaming/LongStreamingTest.java index 799ac7708e..bd7ef20893 100644 --- a/test/long/org/apache/cassandra/streaming/LongStreamingTest.java +++ b/test/long/org/apache/cassandra/streaming/LongStreamingTest.java @@ -122,7 +122,7 @@ public class LongStreamingTest public void init(String keyspace) { for (Range range : StorageService.instance.getLocalRanges(KS)) - addRangeForEndpoint(range, FBUtilities.getBroadcastAddress()); + addRangeForEndpoint(range, FBUtilities.getBroadcastAddressAndPort()); this.ks = keyspace; } @@ -149,7 +149,7 @@ public class LongStreamingTest public void init(String keyspace) { for (Range range : StorageService.instance.getLocalRanges(KS)) - addRangeForEndpoint(range, FBUtilities.getBroadcastAddress()); + addRangeForEndpoint(range, FBUtilities.getBroadcastAddressAndPort()); this.ks = keyspace; } diff --git a/test/microbench/org/apache/cassandra/test/microbench/PendingRangesBench.java b/test/microbench/org/apache/cassandra/test/microbench/PendingRangesBench.java index 9ec1aa67d9..68cfd7eb8b 100644 --- a/test/microbench/org/apache/cassandra/test/microbench/PendingRangesBench.java +++ b/test/microbench/org/apache/cassandra/test/microbench/PendingRangesBench.java @@ -25,11 +25,11 @@ import com.google.common.collect.Multimap; import org.apache.cassandra.dht.RandomPartitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.PendingRangeMaps; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.HashSet; @@ -50,7 +50,7 @@ public class PendingRangesBench PendingRangeMaps pendingRangeMaps; int maxToken = 256 * 100; - Multimap, InetAddress> oldPendingRanges; + Multimap, InetAddressAndPort> oldPendingRanges; private Range genRange(String left, String right) { @@ -63,7 +63,7 @@ public class PendingRangesBench pendingRangeMaps = new PendingRangeMaps(); oldPendingRanges = HashMultimap.create(); - InetAddress[] addresses = {InetAddress.getByName("127.0.0.1"), InetAddress.getByName("127.0.0.2")}; + InetAddressAndPort[] addresses = { InetAddressAndPort.getByName("127.0.0.1"), InetAddressAndPort.getByName("127.0.0.2")}; for (int i = 0; i < maxToken; i++) { @@ -97,8 +97,8 @@ public class PendingRangesBench { int randomToken = ThreadLocalRandom.current().nextInt(maxToken * 10 + 5); Token searchToken = new RandomPartitioner.BigIntegerToken(Integer.toString(randomToken)); - Set endpoints = new HashSet<>(); - for (Map.Entry, Collection> entry : oldPendingRanges.asMap().entrySet()) + Set endpoints = new HashSet<>(); + for (Map.Entry, Collection> entry : oldPendingRanges.asMap().entrySet()) { if (entry.getKey().contains(searchToken)) endpoints.addAll(entry.getValue()); diff --git a/test/unit/org/apache/cassandra/OffsetAwareConfigurationLoader.java b/test/unit/org/apache/cassandra/OffsetAwareConfigurationLoader.java index 0e4a3cf939..d06caba2d0 100644 --- a/test/unit/org/apache/cassandra/OffsetAwareConfigurationLoader.java +++ b/test/unit/org/apache/cassandra/OffsetAwareConfigurationLoader.java @@ -20,8 +20,15 @@ package org.apache.cassandra; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.YamlConfigurationLoader; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import java.io.File; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.stream.Collectors; + +import com.google.common.base.Joiner; public class OffsetAwareConfigurationLoader extends YamlConfigurationLoader @@ -52,6 +59,31 @@ public class OffsetAwareConfigurationLoader extends YamlConfigurationLoader config.native_transport_port += offset; config.storage_port += offset; + //Rewrite the seed ports string + String[] hosts = config.seed_provider.parameters.get("seeds").split(",", -1); + String rewrittenSeeds = Joiner.on(", ").join(Arrays.stream(hosts).map(host -> { + StringBuilder sb = new StringBuilder(); + try + { + InetAddressAndPort address = InetAddressAndPort.getByName(host.trim()); + if (address.address instanceof Inet6Address) + { + sb.append('[').append(address.address.getHostAddress()).append(']'); + } + else + { + sb.append(address.address.getHostAddress()); + } + sb.append(':').append(address.port + offset); + return sb.toString(); + } + catch (UnknownHostException e) + { + throw new ConfigurationException("Error in OffsetAwareConfigurationLoader reworking seed list", e); + } + }).collect(Collectors.toList())); + config.seed_provider.parameters.put("seeds", rewrittenSeeds); + config.commitlog_directory += sep + offset; config.saved_caches_directory += sep + offset; config.hints_directory += sep + offset; diff --git a/test/unit/org/apache/cassandra/Util.java b/test/unit/org/apache/cassandra/Util.java index 7e62c41cb8..1201efaaca 100644 --- a/test/unit/org/apache/cassandra/Util.java +++ b/test/unit/org/apache/cassandra/Util.java @@ -22,7 +22,6 @@ package org.apache.cassandra; import java.io.Closeable; import java.io.EOFException; import java.io.IOError; -import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.*; @@ -39,6 +38,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableMetadata; @@ -192,7 +192,7 @@ public class Util * Creates initial set of nodes and tokens. Nodes are added to StorageService as 'normal' */ public static void createInitialRing(StorageService ss, IPartitioner partitioner, List endpointTokens, - List keyTokens, List hosts, List hostIds, int howMany) + List keyTokens, List hosts, List hostIds, int howMany) throws UnknownHostException { // Expand pool of host IDs as necessary @@ -210,9 +210,12 @@ public class Util for (int i=0; i endpoints = ImmutableMultimap. builder() - .put(LOCAL, InetAddress.getByName("0")) - .put(LOCAL, InetAddress.getByName("00")) - .put("1", InetAddress.getByName("1")) - .put("1", InetAddress.getByName("11")) - .put("2", InetAddress.getByName("2")) - .put("2", InetAddress.getByName("22")) + Multimap endpoints = ImmutableMultimap. builder() + .put(LOCAL, InetAddressAndPort.getByName("0")) + .put(LOCAL, InetAddressAndPort.getByName("00")) + .put("1", InetAddressAndPort.getByName("1")) + .put("1", InetAddressAndPort.getByName("11")) + .put("2", InetAddressAndPort.getByName("2")) + .put("2", InetAddressAndPort.getByName("22")) .build(); - Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); + Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); assertThat(result.size(), is(2)); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("11"))); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("22"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("11"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("22"))); } @Test public void shouldSelectHostFromLocal() throws UnknownHostException { - Multimap endpoints = ImmutableMultimap. builder() - .put(LOCAL, InetAddress.getByName("0")) - .put(LOCAL, InetAddress.getByName("00")) - .put("1", InetAddress.getByName("1")) + Multimap endpoints = ImmutableMultimap. builder() + .put(LOCAL, InetAddressAndPort.getByName("0")) + .put(LOCAL, InetAddressAndPort.getByName("00")) + .put("1", InetAddressAndPort.getByName("1")) .build(); - Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); + Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); assertThat(result.size(), is(2)); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("1"))); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("0"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("1"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("0"))); } @Test public void shouldReturnAsIsIfNoEnoughEndpoints() throws UnknownHostException { - Multimap endpoints = ImmutableMultimap. builder() - .put(LOCAL, InetAddress.getByName("0")) + Multimap endpoints = ImmutableMultimap. builder() + .put(LOCAL, InetAddressAndPort.getByName("0")) .build(); - Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); + Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); assertThat(result.size(), is(1)); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("0"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("0"))); } @Test public void shouldSelectTwoRandomHostsFromSingleOtherRack() throws UnknownHostException { - Multimap endpoints = ImmutableMultimap. builder() - .put(LOCAL, InetAddress.getByName("0")) - .put(LOCAL, InetAddress.getByName("00")) - .put("1", InetAddress.getByName("1")) - .put("1", InetAddress.getByName("11")) - .put("1", InetAddress.getByName("111")) + Multimap endpoints = ImmutableMultimap. builder() + .put(LOCAL, InetAddressAndPort.getByName("0")) + .put(LOCAL, InetAddressAndPort.getByName("00")) + .put("1", InetAddressAndPort.getByName("1")) + .put("1", InetAddressAndPort.getByName("11")) + .put("1", InetAddressAndPort.getByName("111")) .build(); - Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); + Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); // result should be the last two non-local replicas // (Collections.shuffle has been replaced with Collections.reverse for testing) assertThat(result.size(), is(2)); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("11"))); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("111"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("11"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("111"))); } @Test public void shouldSelectTwoRandomHostsFromSingleRack() throws UnknownHostException { - Multimap endpoints = ImmutableMultimap. builder() - .put(LOCAL, InetAddress.getByName("1")) - .put(LOCAL, InetAddress.getByName("11")) - .put(LOCAL, InetAddress.getByName("111")) - .put(LOCAL, InetAddress.getByName("1111")) + Multimap endpoints = ImmutableMultimap. builder() + .put(LOCAL, InetAddressAndPort.getByName("1")) + .put(LOCAL, InetAddressAndPort.getByName("11")) + .put(LOCAL, InetAddressAndPort.getByName("111")) + .put(LOCAL, InetAddressAndPort.getByName("1111")) .build(); - Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); + Collection result = new TestEndpointFilter(LOCAL, endpoints).filter(); // result should be the last two non-local replicas // (Collections.shuffle has been replaced with Collections.reverse for testing) assertThat(result.size(), is(2)); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("111"))); - assertThat(result, JUnitMatchers.hasItem(InetAddress.getByName("1111"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("111"))); + assertThat(result, JUnitMatchers.hasItem(InetAddressAndPort.getByName("1111"))); } private static class TestEndpointFilter extends BatchlogManager.EndpointFilter { - TestEndpointFilter(String localRack, Multimap endpoints) + TestEndpointFilter(String localRack, Multimap endpoints) { super(localRack, endpoints); } @Override - protected boolean isValid(InetAddress input) + protected boolean isValid(InetAddressAndPort input) { // We will use always alive non-localhost endpoints return true; diff --git a/test/unit/org/apache/cassandra/batchlog/BatchlogManagerTest.java b/test/unit/org/apache/cassandra/batchlog/BatchlogManagerTest.java index 34902fe131..33fb209389 100644 --- a/test/unit/org/apache/cassandra/batchlog/BatchlogManagerTest.java +++ b/test/unit/org/apache/cassandra/batchlog/BatchlogManagerTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.batchlog; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import java.util.concurrent.ExecutionException; @@ -29,6 +28,7 @@ import org.junit.*; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.Util.PartitionerSwitcher; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.Schema; @@ -94,7 +94,7 @@ public class BatchlogManagerTest public void setUp() throws Exception { TokenMetadata metadata = StorageService.instance.getTokenMetadata(); - InetAddress localhost = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort localhost = InetAddressAndPort.getByName("127.0.0.1"); metadata.updateNormalToken(Util.token("A"), localhost); metadata.updateHostId(UUIDGen.getTimeUUID(), localhost); Keyspace.open(SchemaConstants.SYSTEM_KEYSPACE_NAME).getColumnFamilyStore(SystemKeyspace.BATCHES).truncateBlocking(); @@ -344,7 +344,7 @@ public class BatchlogManagerTest @Test public void testReplayWithNoPeers() throws Exception { - StorageService.instance.getTokenMetadata().removeEndpoint(InetAddress.getByName("127.0.0.1")); + StorageService.instance.getTokenMetadata().removeEndpoint(InetAddressAndPort.getByName("127.0.0.1")); long initialAllBatches = BatchlogManager.instance.countAllBatches(); long initialReplayedBatches = BatchlogManager.instance.getTotalBatchesReplayed(); diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java index b50a050bba..589afd5195 100644 --- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java +++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java @@ -77,6 +77,7 @@ public class DatabaseDescriptorRefTest "org.apache.cassandra.config.EncryptionOptions$ClientEncryptionOptions", "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptions", "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptions$InternodeEncryption", + "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptions$OutgoingEncryptedPortSource", "org.apache.cassandra.config.YamlConfigurationLoader", "org.apache.cassandra.config.YamlConfigurationLoader$PropertiesChecker", "org.apache.cassandra.config.YamlConfigurationLoader$PropertiesChecker$1", @@ -126,6 +127,7 @@ public class DatabaseDescriptorRefTest "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptionsCustomizer", "org.apache.cassandra.ConsoleAppenderBeanInfo", "org.apache.cassandra.ConsoleAppenderCustomizer", + "org.apache.cassandra.locator.InetAddressAndPort" }; static final Set checkedClasses = new HashSet<>(Arrays.asList(validClasses)); diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java index 6993a65a42..69d2fb593a 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -48,6 +48,7 @@ import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.concurrent.ScheduledExecutors; import org.apache.cassandra.index.SecondaryIndexManager; import org.apache.cassandra.config.EncryptionOptions; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.*; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.functions.FunctionName; @@ -144,9 +145,9 @@ public abstract class CQLTester // Register an EndpointSnitch which returns fixed values for test. DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { - @Override public String getRack(InetAddress endpoint) { return RACK1; } - @Override public String getDatacenter(InetAddress endpoint) { return DATA_CENTER; } - @Override public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) { return 0; } + @Override public String getRack(InetAddressAndPort endpoint) { return RACK1; } + @Override public String getDatacenter(InetAddressAndPort endpoint) { return DATA_CENTER; } + @Override public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } }); try @@ -829,6 +830,11 @@ public abstract class CQLTester return sessionNet(protocolVersion).execute(formatQuery(query), values); } + protected com.datastax.driver.core.ResultSet executeNetWithPaging(ProtocolVersion version, String query, int pageSize) throws Throwable + { + return sessionNet(version).execute(new SimpleStatement(formatQuery(query)).setFetchSize(pageSize)); + } + protected com.datastax.driver.core.ResultSet executeNetWithPaging(String query, int pageSize) throws Throwable { return sessionNet().execute(new SimpleStatement(formatQuery(query)).setFetchSize(pageSize)); diff --git a/test/unit/org/apache/cassandra/cql3/PreparedStatementsTest.java b/test/unit/org/apache/cassandra/cql3/PreparedStatementsTest.java index 0a314da6dc..ce5de627d1 100644 --- a/test/unit/org/apache/cassandra/cql3/PreparedStatementsTest.java +++ b/test/unit/org/apache/cassandra/cql3/PreparedStatementsTest.java @@ -223,7 +223,6 @@ public class PreparedStatementsTest extends CQLTester .withClusterName("Test Cluster") .withPort(nativePort) .withoutJMXReporting() - .allowBetaProtocolVersion() .build()) { try (Session newSession = newCluster.connect()) diff --git a/test/unit/org/apache/cassandra/cql3/ViewComplexTest.java b/test/unit/org/apache/cassandra/cql3/ViewComplexTest.java index 26b9d65be0..665bc44bf3 100644 --- a/test/unit/org/apache/cassandra/cql3/ViewComplexTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewComplexTest.java @@ -851,10 +851,10 @@ public class ViewComplexTest extends CQLTester for (String view : Arrays.asList("mv1", "mv2")) { // paging - assertEquals(1, executeNetWithPaging(String.format("SELECT k,a,b FROM %s limit 1", view), 1).all().size()); - assertEquals(2, executeNetWithPaging(String.format("SELECT k,a,b FROM %s limit 2", view), 1).all().size()); - assertEquals(2, executeNetWithPaging(String.format("SELECT k,a,b FROM %s", view), 1).all().size()); - assertRowsNet(executeNetWithPaging(String.format("SELECT k,a,b FROM %s ", view), 1), + assertEquals(1, executeNetWithPaging(protocolVersion, String.format("SELECT k,a,b FROM %s limit 1", view), 1).all().size()); + assertEquals(2, executeNetWithPaging(protocolVersion, String.format("SELECT k,a,b FROM %s limit 2", view), 1).all().size()); + assertEquals(2, executeNetWithPaging(protocolVersion, String.format("SELECT k,a,b FROM %s", view), 1).all().size()); + assertRowsNet(protocolVersion, executeNetWithPaging(protocolVersion, String.format("SELECT k,a,b FROM %s ", view), 1), row(50, 50, 50), row(100, 100, 100)); // limit diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/CreateTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/CreateTest.java index ac261ca1b6..dc90b4ee4f 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/CreateTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/CreateTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.cql3.validation.operations; -import java.net.InetAddress; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collection; @@ -26,6 +25,7 @@ import java.util.UUID; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.Schema; @@ -514,13 +514,13 @@ public class CreateTest extends CQLTester DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { @Override - public String getRack(InetAddress endpoint) { return RACK1; } + public String getRack(InetAddressAndPort endpoint) { return RACK1; } @Override - public String getDatacenter(InetAddress endpoint) { return "us-east-1"; } + public String getDatacenter(InetAddressAndPort endpoint) { return "us-east-1"; } @Override - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) { return 0; } + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } }); execute("CREATE KEYSPACE Foo WITH replication = { 'class' : 'NetworkTopologyStrategy', 'us-east-1' : 1 };"); diff --git a/test/unit/org/apache/cassandra/db/CleanupTest.java b/test/unit/org/apache/cassandra/db/CleanupTest.java index 044a49e82c..a096c781ad 100644 --- a/test/unit/org/apache/cassandra/db/CleanupTest.java +++ b/test/unit/org/apache/cassandra/db/CleanupTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.db; import java.io.IOException; -import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.AbstractMap; @@ -37,6 +36,7 @@ import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.KeyspaceMetadata; @@ -89,13 +89,13 @@ public class CleanupTest DatabaseDescriptor.setEndpointSnitch(new AbstractNetworkTopologySnitch() { @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return "RC1"; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return "DC1"; } @@ -166,8 +166,8 @@ public class CleanupTest byte[] tk1 = new byte[1], tk2 = new byte[1]; tk1[0] = 2; tk2[0] = 1; - tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(new BytesToken(tk2), InetAddress.getByName("127.0.0.2")); + tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2")); CompactionManager.instance.performCleanup(cfs, 2); @@ -198,8 +198,8 @@ public class CleanupTest byte[] tk1 = new byte[1], tk2 = new byte[1]; tk1[0] = 2; tk2[0] = 1; - tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(new BytesToken(tk2), InetAddress.getByName("127.0.0.2")); + tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2")); CompactionManager.instance.performCleanup(cfs, 2); assertEquals(0, Util.getAll(Util.cmd(cfs).build()).size()); @@ -222,9 +222,9 @@ public class CleanupTest TokenMetadata tmd = StorageService.instance.getTokenMetadata(); tmd.clearUnsafe(); - tmd.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.0.0.1")); + tmd.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.1")); byte[] tk1 = {2}; - tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); + tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1")); Keyspace keyspace = Keyspace.open(KEYSPACE2); @@ -270,8 +270,8 @@ public class CleanupTest byte[] tk1 = new byte[1], tk2 = new byte[1]; tk1[0] = 2; tk2[0] = 1; - tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(new BytesToken(tk2), InetAddress.getByName("127.0.0.2")); + tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2")); for(SSTableReader r: cfs.getLiveSSTables()) CompactionManager.instance.forceUserDefinedCleanup(r.getFilename()); diff --git a/test/unit/org/apache/cassandra/db/DiskBoundaryManagerTest.java b/test/unit/org/apache/cassandra/db/DiskBoundaryManagerTest.java index c56368fa20..6e2a714d95 100644 --- a/test/unit/org/apache/cassandra/db/DiskBoundaryManagerTest.java +++ b/test/unit/org/apache/cassandra/db/DiskBoundaryManagerTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.db; import java.io.File; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; @@ -30,6 +29,7 @@ import org.junit.Test; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.dht.BootStrapper; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; @@ -51,7 +51,7 @@ public class DiskBoundaryManagerTest extends CQLTester { BlacklistedDirectories.clearUnwritableUnsafe(); TokenMetadata metadata = StorageService.instance.getTokenMetadata(); - metadata.updateNormalTokens(BootStrapper.getRandomTokens(metadata, 10), FBUtilities.getBroadcastAddress()); + metadata.updateNormalTokens(BootStrapper.getRandomTokens(metadata, 10), FBUtilities.getBroadcastAddressAndPort()); createTable("create table %s (id int primary key, x text)"); dirs = new Directories(getCurrentColumnFamilyStore().metadata(), Lists.newArrayList(new Directories.DataDirectory(new File("/tmp/1")), new Directories.DataDirectory(new File("/tmp/2")), @@ -86,7 +86,7 @@ public class DiskBoundaryManagerTest extends CQLTester public void updateTokensTest() throws UnknownHostException { DiskBoundaries dbv1 = dbm.getDiskBoundaries(mock); - StorageService.instance.getTokenMetadata().updateNormalTokens(BootStrapper.getRandomTokens(StorageService.instance.getTokenMetadata(), 10), InetAddress.getByName("127.0.0.10")); + StorageService.instance.getTokenMetadata().updateNormalTokens(BootStrapper.getRandomTokens(StorageService.instance.getTokenMetadata(), 10), InetAddressAndPort.getByName("127.0.0.10")); DiskBoundaries dbv2 = dbm.getDiskBoundaries(mock); assertFalse(dbv1.equals(dbv2)); } diff --git a/test/unit/org/apache/cassandra/db/ReadCommandTest.java b/test/unit/org/apache/cassandra/db/ReadCommandTest.java index 64ac62751f..6bb0a1a0c2 100644 --- a/test/unit/org/apache/cassandra/db/ReadCommandTest.java +++ b/test/unit/org/apache/cassandra/db/ReadCommandTest.java @@ -50,9 +50,11 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataOutputBuffer; import org.apache.cassandra.io.util.WrappedDataOutputStreamPlus; +import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.schema.TableMetadata; +import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; @@ -312,7 +314,8 @@ public class ReadCommandTest } } - public void serializerTest() throws IOException + @Test + public void testSerializer() throws IOException { ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(CF2); @@ -324,10 +327,11 @@ public class ReadCommandTest ReadCommand readCommand = Util.cmd(cfs, Util.dk("key")).includeRow("dd").build(); int messagingVersion = MessagingService.current_version; - long size = ReadCommand.serializer.serializedSize(readCommand, messagingVersion); - FakeOutputStream out = new FakeOutputStream(); - ReadCommand.serializer.serialize(readCommand, new WrappedDataOutputStreamPlus(out), messagingVersion); + Tracing.instance.newSession(Tracing.TraceType.QUERY); + MessageOut messageOut = new MessageOut(MessagingService.Verb.READ, readCommand, ReadCommand.serializer); + long size = messageOut.serializedSize(messagingVersion); + messageOut.serialize(new WrappedDataOutputStreamPlus(out), messagingVersion); Assert.assertEquals(size, out.count); } diff --git a/test/unit/org/apache/cassandra/db/RowCacheTest.java b/test/unit/org/apache/cassandra/db/RowCacheTest.java index fee3f2c102..5ca1eefb85 100644 --- a/test/unit/org/apache/cassandra/db/RowCacheTest.java +++ b/test/unit/org/apache/cassandra/db/RowCacheTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.db; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -32,6 +31,7 @@ import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.cache.RowCacheKey; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.marshal.AsciiType; import org.apache.cassandra.db.rows.*; @@ -300,8 +300,8 @@ public class RowCacheTest byte[] tk1, tk2; tk1 = "key1000".getBytes(); tk2 = "key1050".getBytes(); - tmd.updateNormalToken(new BytesToken(tk1), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(new BytesToken(tk2), InetAddress.getByName("127.0.0.2")); + tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2")); store.cleanupCache(); assertEquals(50, CacheService.instance.rowCache.size()); CacheService.instance.setRowCacheCapacityInMB(0); diff --git a/test/unit/org/apache/cassandra/db/SystemKeyspaceMigrator40Test.java b/test/unit/org/apache/cassandra/db/SystemKeyspaceMigrator40Test.java new file mode 100644 index 0000000000..1c051f50d0 --- /dev/null +++ b/test/unit/org/apache/cassandra/db/SystemKeyspaceMigrator40Test.java @@ -0,0 +1,192 @@ +/* + * 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.cassandra.db; + +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.UUID; + + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.junit.Test; + +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.db.marshal.BytesType; +import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.db.marshal.UTF8Type; +import org.apache.cassandra.db.marshal.UUIDType; +import org.apache.cassandra.utils.UUIDGen; + +import static org.junit.Assert.assertEquals; + +public class SystemKeyspaceMigrator40Test extends CQLTester +{ + @Test + public void testMigratePeers() throws Throwable + { + String insert = String.format("INSERT INTO %s (" + + "peer, " + + "data_center, " + + "host_id, " + + "preferred_ip, " + + "rack, " + + "release_version, " + + "rpc_address, " + + "schema_version, " + + "tokens) " + + " values ( ?, ?, ? , ? , ?, ?, ?, ?, ?)", + SystemKeyspaceMigrator40.legacyPeersName); + UUID hostId = UUIDGen.getTimeUUID(); + UUID schemaVersion = UUIDGen.getTimeUUID(); + execute(insert, + InetAddress.getByName("127.0.0.1"), + "dcFoo", + hostId, + InetAddress.getByName("127.0.0.2"), + "rackFoo", "4.0", + InetAddress.getByName("127.0.0.3"), + schemaVersion, + ImmutableSet.of("foobar")); + SystemKeyspaceMigrator40.migrate(); + + int rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.peersName))) + { + rowCount++; + assertEquals(InetAddress.getByName("127.0.0.1"), row.getInetAddress("peer")); + assertEquals(DatabaseDescriptor.getStoragePort(), row.getInt("peer_port")); + assertEquals("dcFoo", row.getString("data_center")); + assertEquals(hostId, row.getUUID("host_id")); + assertEquals(InetAddress.getByName("127.0.0.2"), row.getInetAddress("preferred_ip")); + assertEquals(DatabaseDescriptor.getStoragePort(), row.getInt("preferred_port")); + assertEquals("rackFoo", row.getString("rack")); + assertEquals("4.0", row.getString("release_version")); + assertEquals(InetAddress.getByName("127.0.0.3"), row.getInetAddress("native_address")); + assertEquals(DatabaseDescriptor.getNativeTransportPort(), row.getInt("native_port")); + assertEquals(schemaVersion, row.getUUID("schema_version")); + assertEquals(ImmutableSet.of("foobar"), row.getSet("tokens", UTF8Type.instance)); + } + assertEquals(1, rowCount); + + //Test nulls/missing don't prevent the row from propagating + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.legacyPeersName)); + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.peersName)); + + execute(String.format("INSERT INTO %s (peer) VALUES (?)", SystemKeyspaceMigrator40.legacyPeersName), + InetAddress.getByName("127.0.0.1")); + SystemKeyspaceMigrator40.migrate(); + + rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.peersName))) + { + rowCount++; + } + assertEquals(1, rowCount); + } + + @Test + public void testMigratePeerEvents() throws Throwable + { + String insert = String.format("INSERT INTO %s (" + + "peer, " + + "hints_dropped) " + + " values ( ?, ? )", + SystemKeyspaceMigrator40.legacyPeerEventsName); + UUID uuid = UUIDGen.getTimeUUID(); + execute(insert, + InetAddress.getByName("127.0.0.1"), + ImmutableMap.of(uuid, 42)); + SystemKeyspaceMigrator40.migrate(); + + int rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.peerEventsName))) + { + rowCount++; + assertEquals(InetAddress.getByName("127.0.0.1"), row.getInetAddress("peer")); + assertEquals(DatabaseDescriptor.getStoragePort(), row.getInt("peer_port")); + assertEquals(ImmutableMap.of(uuid, 42), row.getMap("hints_dropped", UUIDType.instance, Int32Type.instance)); + } + assertEquals(1, rowCount); + + //Test nulls/missing don't prevent the row from propagating + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.legacyPeerEventsName)); + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.peerEventsName)); + + execute(String.format("INSERT INTO %s (peer) VALUES (?)", SystemKeyspaceMigrator40.legacyPeerEventsName), + InetAddress.getByName("127.0.0.1")); + SystemKeyspaceMigrator40.migrate(); + + rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.peerEventsName))) + { + rowCount++; + } + assertEquals(1, rowCount); + } + + @Test + public void testMigrateTransferredRanges() throws Throwable + { + String insert = String.format("INSERT INTO %s (" + + "operation, " + + "peer, " + + "keyspace_name, " + + "ranges) " + + " values ( ?, ?, ?, ? )", + SystemKeyspaceMigrator40.legacyTransferredRangesName); + execute(insert, + "foo", + InetAddress.getByName("127.0.0.1"), + "bar", + ImmutableSet.of(ByteBuffer.wrap(new byte[] { 42 }))); + SystemKeyspaceMigrator40.migrate(); + + int rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.transferredRangesName))) + { + rowCount++; + assertEquals("foo", row.getString("operation")); + assertEquals(InetAddress.getByName("127.0.0.1"), row.getInetAddress("peer")); + assertEquals(DatabaseDescriptor.getStoragePort(), row.getInt("peer_port")); + assertEquals("bar", row.getString("keyspace_name")); + assertEquals(ImmutableSet.of(ByteBuffer.wrap(new byte[] { 42 })), row.getSet("ranges", BytesType.instance)); + } + assertEquals(1, rowCount); + + //Test nulls/missing don't prevent the row from propagating + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.legacyTransferredRangesName)); + execute(String.format("TRUNCATE %s", SystemKeyspaceMigrator40.transferredRangesName)); + + execute(String.format("INSERT INTO %s (operation, peer, keyspace_name) VALUES (?, ?, ?)", SystemKeyspaceMigrator40.legacyTransferredRangesName), + "foo", + InetAddress.getByName("127.0.0.1"), + "bar"); + SystemKeyspaceMigrator40.migrate(); + + rowCount = 0; + for (UntypedResultSet.Row row : execute(String.format("SELECT * FROM %s", SystemKeyspaceMigrator40.transferredRangesName))) + { + rowCount++; + } + assertEquals(1, rowCount); + } +} diff --git a/test/unit/org/apache/cassandra/db/SystemKeyspaceTest.java b/test/unit/org/apache/cassandra/db/SystemKeyspaceTest.java index d1b8ff5556..3bc04c113c 100644 --- a/test/unit/org/apache/cassandra/db/SystemKeyspaceTest.java +++ b/test/unit/org/apache/cassandra/db/SystemKeyspaceTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.db; import java.io.IOException; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; @@ -26,6 +25,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.UntypedResultSet; @@ -53,7 +53,7 @@ public class SystemKeyspaceTest public void testLocalTokens() { // Remove all existing tokens - Collection current = SystemKeyspace.loadTokens().asMap().get(FBUtilities.getLocalAddress()); + Collection current = SystemKeyspace.loadTokens().asMap().get(FBUtilities.getLocalAddressAndPort()); if (current != null && !current.isEmpty()) SystemKeyspace.updateTokens(current); @@ -74,7 +74,7 @@ public class SystemKeyspaceTest public void testNonLocalToken() throws UnknownHostException { BytesToken token = new BytesToken(ByteBufferUtil.bytes("token3")); - InetAddress address = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort address = InetAddressAndPort.getByName("127.0.0.2"); SystemKeyspace.updateTokens(address, Collections.singletonList(token)); assert SystemKeyspace.loadTokens().get(address).contains(token); SystemKeyspace.removeEndpoint(address); diff --git a/test/unit/org/apache/cassandra/db/compaction/AbstractPendingRepairTest.java b/test/unit/org/apache/cassandra/db/compaction/AbstractPendingRepairTest.java index 0baad3baa8..3e38dfccbf 100644 --- a/test/unit/org/apache/cassandra/db/compaction/AbstractPendingRepairTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/AbstractPendingRepairTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.db.compaction; import java.io.IOException; -import java.net.InetAddress; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -31,6 +30,7 @@ import org.junit.Ignore; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.cql3.statements.CreateTableStatement; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.cql3.QueryProcessor; @@ -67,7 +67,7 @@ public class AbstractPendingRepairTest extends AbstractRepairTest // cutoff messaging service MessagingService.instance().addMessageSink(new IMessageSink() { - public boolean allowOutgoingMessage(MessageOut message, int id, InetAddress to) + public boolean allowOutgoingMessage(MessageOut message, int id, InetAddressAndPort to) { return false; } diff --git a/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java b/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java index 5f05fabfdd..6e7e18473d 100644 --- a/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/AntiCompactionTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.db.compaction; import java.io.File; import java.io.IOException; -import java.net.InetAddress; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -35,6 +34,7 @@ import org.junit.After; import org.junit.Ignore; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.db.*; @@ -96,7 +96,7 @@ public class AntiCompactionTest private void registerParentRepairSession(UUID sessionID, Collection> ranges, long repairedAt, UUID pendingRepair) throws IOException { ActiveRepairService.instance.registerParentRepairSession(sessionID, - InetAddress.getByName("10.0.0.1"), + InetAddressAndPort.getByName("10.0.0.1"), Lists.newArrayList(cfs), ranges, pendingRepair != null || repairedAt != UNREPAIRED_SSTABLE, repairedAt, true, PreviewKind.NONE); diff --git a/test/unit/org/apache/cassandra/db/compaction/CompactionManagerGetSSTablesForValidationTest.java b/test/unit/org/apache/cassandra/db/compaction/CompactionManagerGetSSTablesForValidationTest.java index b9e3c17434..8290adf294 100644 --- a/test/unit/org/apache/cassandra/db/compaction/CompactionManagerGetSSTablesForValidationTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/CompactionManagerGetSSTablesForValidationTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.db.compaction; -import java.net.InetAddress; import java.util.Collections; import java.util.Iterator; import java.util.Set; @@ -33,6 +32,7 @@ import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.cql3.statements.CreateTableStatement; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.schema.Schema; @@ -58,7 +58,7 @@ public class CompactionManagerGetSSTablesForValidationTest private String ks; private static final String tbl = "tbl"; private ColumnFamilyStore cfs; - private static InetAddress coordinator; + private static InetAddressAndPort coordinator; private static Token MT; @@ -73,7 +73,7 @@ public class CompactionManagerGetSSTablesForValidationTest public static void setupClass() throws Exception { SchemaLoader.prepareServer(); - coordinator = InetAddress.getByName("10.0.0.1"); + coordinator = InetAddressAndPort.getByName("10.0.0.1"); MT = DatabaseDescriptor.getPartitioner().getMinimumToken(); } diff --git a/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java b/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java index 624f1191af..567984d7c1 100644 --- a/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java @@ -195,7 +195,7 @@ public class LeveledCompactionStrategyTest int gcBefore = keyspace.getColumnFamilyStore(CF_STANDARDDLEVELED).gcBefore(FBUtilities.nowInSeconds()); UUID parentRepSession = UUID.randomUUID(); ActiveRepairService.instance.registerParentRepairSession(parentRepSession, - FBUtilities.getBroadcastAddress(), + FBUtilities.getBroadcastAddressAndPort(), Arrays.asList(cfs), Arrays.asList(range), false, @@ -203,7 +203,7 @@ public class LeveledCompactionStrategyTest true, PreviewKind.NONE); RepairJobDesc desc = new RepairJobDesc(parentRepSession, UUID.randomUUID(), KEYSPACE1, CF_STANDARDDLEVELED, Arrays.asList(range)); - Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), gcBefore, PreviewKind.NONE); + Validator validator = new Validator(desc, FBUtilities.getBroadcastAddressAndPort(), gcBefore, PreviewKind.NONE); CompactionManager.instance.submitValidation(cfs, validator).get(); } diff --git a/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java b/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java index 658b87a540..423ad28150 100644 --- a/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java +++ b/test/unit/org/apache/cassandra/db/view/ViewUtilsTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.db.view; -import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -28,6 +27,7 @@ import org.junit.Test; import junit.framework.Assert; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.dht.OrderPreservingPartitioner.StringToken; @@ -59,12 +59,12 @@ public class ViewUtilsTest metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map replicationMap = new HashMap<>(); replicationMap.put(ReplicationParams.CLASS, NetworkTopologyStrategy.class.getName()); @@ -76,12 +76,12 @@ public class ViewUtilsTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, replicationMap)); Schema.instance.load(meta); - Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", - new StringToken("CA"), - new StringToken("BB")); + Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", + new StringToken("CA"), + new StringToken("BB")); Assert.assertTrue(naturalEndpoint.isPresent()); - Assert.assertEquals(InetAddress.getByName("127.0.0.2"), naturalEndpoint.get()); + Assert.assertEquals(InetAddressAndPort.getByName("127.0.0.2"), naturalEndpoint.get()); } @@ -92,12 +92,12 @@ public class ViewUtilsTest metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map replicationMap = new HashMap<>(); replicationMap.put(ReplicationParams.CLASS, NetworkTopologyStrategy.class.getName()); @@ -109,12 +109,12 @@ public class ViewUtilsTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, replicationMap)); Schema.instance.load(meta); - Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", - new StringToken("CA"), - new StringToken("BB")); + Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", + new StringToken("CA"), + new StringToken("BB")); Assert.assertTrue(naturalEndpoint.isPresent()); - Assert.assertEquals(InetAddress.getByName("127.0.0.1"), naturalEndpoint.get()); + Assert.assertEquals(InetAddressAndPort.getByName("127.0.0.1"), naturalEndpoint.get()); } @Test @@ -124,12 +124,12 @@ public class ViewUtilsTest metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map replicationMap = new HashMap<>(); replicationMap.put(ReplicationParams.CLASS, NetworkTopologyStrategy.class.getName()); @@ -141,9 +141,9 @@ public class ViewUtilsTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, replicationMap)); Schema.instance.load(meta); - Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", - new StringToken("AB"), - new StringToken("BB")); + Optional naturalEndpoint = ViewUtils.getViewNaturalEndpoint("Keyspace1", + new StringToken("AB"), + new StringToken("BB")); Assert.assertFalse(naturalEndpoint.isPresent()); } diff --git a/test/unit/org/apache/cassandra/dht/BootStrapperTest.java b/test/unit/org/apache/cassandra/dht/BootStrapperTest.java index a1054bba40..f11cb62f0b 100644 --- a/test/unit/org/apache/cassandra/dht/BootStrapperTest.java +++ b/test/unit/org/apache/cassandra/dht/BootStrapperTest.java @@ -20,7 +20,6 @@ package org.apache.cassandra.dht; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.HashSet; @@ -41,6 +40,7 @@ import org.junit.runner.RunWith; import org.apache.cassandra.OrderedJUnit4ClassRunner; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.dht.tokenallocator.TokenAllocation; @@ -96,32 +96,32 @@ public class BootStrapperTest generateFakeEndpoints(numOldNodes); Token myToken = tmd.partitioner.getRandomToken(); - InetAddress myEndpoint = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort myEndpoint = InetAddressAndPort.getByName("127.0.0.1"); assertEquals(numOldNodes, tmd.sortedTokens().size()); RangeStreamer s = new RangeStreamer(tmd, null, myEndpoint, StreamOperation.BOOTSTRAP, true, DatabaseDescriptor.getEndpointSnitch(), new StreamStateStore(), false, 1); IFailureDetector mockFailureDetector = new IFailureDetector() { - public boolean isAlive(InetAddress ep) + public boolean isAlive(InetAddressAndPort ep) { return true; } - public void interpret(InetAddress ep) { throw new UnsupportedOperationException(); } - public void report(InetAddress ep) { throw new UnsupportedOperationException(); } + public void interpret(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } + public void report(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } public void registerFailureDetectionEventListener(IFailureDetectionEventListener listener) { throw new UnsupportedOperationException(); } public void unregisterFailureDetectionEventListener(IFailureDetectionEventListener listener) { throw new UnsupportedOperationException(); } - public void remove(InetAddress ep) { throw new UnsupportedOperationException(); } - public void forceConviction(InetAddress ep) { throw new UnsupportedOperationException(); } + public void remove(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } + public void forceConviction(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } }; s.addSourceFilter(new RangeStreamer.FailureDetectorSourceFilter(mockFailureDetector)); s.addRanges(keyspaceName, Keyspace.open(keyspaceName).getReplicationStrategy().getPendingAddressRanges(tmd, myToken, myEndpoint)); - Collection>>> toFetch = s.toFetch().get(keyspaceName); + Collection>>> toFetch = s.toFetch().get(keyspaceName); // Check we get get RF new ranges in total Set> ranges = new HashSet<>(); - for (Map.Entry>> e : toFetch) + for (Map.Entry>> e : toFetch) ranges.addAll(e.getValue()); assertEquals(replicationFactor, ranges.size()); @@ -151,7 +151,7 @@ public class BootStrapperTest for (int i = 1; i <= numOldNodes; i++) { // leave .1 for myEndpoint - InetAddress addr = InetAddress.getByName("127." + dc + "." + rack + "." + (i + 1)); + InetAddressAndPort addr = InetAddressAndPort.getByName("127." + dc + "." + rack + "." + (i + 1)); List tokens = Lists.newArrayListWithCapacity(numVNodes); for (int j = 0; j < numVNodes; ++j) tokens.add(p.getRandomToken()); @@ -167,7 +167,7 @@ public class BootStrapperTest String ks = "BootStrapperTestKeyspace3"; TokenMetadata tm = new TokenMetadata(); generateFakeEndpoints(tm, 10, vn); - InetAddress addr = FBUtilities.getBroadcastAddress(); + InetAddressAndPort addr = FBUtilities.getBroadcastAddressAndPort(); allocateTokensForNode(vn, ks, tm, addr); } @@ -184,15 +184,15 @@ public class BootStrapperTest // Register peers with expected DC for NetworkTopologyStrategy. TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); - metadata.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.1.0.99")); - metadata.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.15.0.99")); + metadata.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.1.0.99")); + metadata.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.15.0.99")); SchemaLoader.createKeyspace(ks, KeyspaceParams.nts(dc, replicas, "15", 15), SchemaLoader.standardCFMD(ks, "Standard1")); TokenMetadata tm = StorageService.instance.getTokenMetadata(); tm.clearUnsafe(); for (int i = 0; i < rackCount; ++i) generateFakeEndpoints(tm, 10, vn, dc, Integer.toString(i)); - InetAddress addr = InetAddress.getByName("127." + dc + ".0.99"); + InetAddressAndPort addr = InetAddressAndPort.getByName("127." + dc + ".0.99"); allocateTokensForNode(vn, ks, tm, addr); // Note: Not matching replication factor in second datacentre, but this should not affect us. } finally { @@ -230,7 +230,7 @@ public class BootStrapperTest testAllocateTokensNetworkStrategy(1, 1); } - private void allocateTokensForNode(int vn, String ks, TokenMetadata tm, InetAddress addr) + private void allocateTokensForNode(int vn, String ks, TokenMetadata tm, InetAddressAndPort addr) { SummaryStatistics os = TokenAllocation.replicatedOwnershipStats(tm.cloneOnlyTokenMap(), Keyspace.open(ks).getReplicationStrategy(), addr); Collection tokens = BootStrapper.allocateTokens(tm, addr, ks, vn, 0); @@ -260,14 +260,14 @@ public class BootStrapperTest TokenMetadata tm = new TokenMetadata(); generateFakeEndpoints(tm, 10, vn); - InetAddress dcaddr = FBUtilities.getBroadcastAddress(); + InetAddressAndPort dcaddr = FBUtilities.getBroadcastAddressAndPort(); SummaryStatistics os3 = TokenAllocation.replicatedOwnershipStats(tm, Keyspace.open(ks3).getReplicationStrategy(), dcaddr); SummaryStatistics os2 = TokenAllocation.replicatedOwnershipStats(tm, Keyspace.open(ks2).getReplicationStrategy(), dcaddr); String cks = ks3; String nks = ks2; for (int i=11; i<=20; ++i) { - allocateTokensForNode(vn, cks, tm, InetAddress.getByName("127.0.0." + (i + 1))); + allocateTokensForNode(vn, cks, tm, InetAddressAndPort.getByName("127.0.0." + (i + 1))); String t = cks; cks = nks; nks = t; } diff --git a/test/unit/org/apache/cassandra/dht/RangeFetchMapCalculatorTest.java b/test/unit/org/apache/cassandra/dht/RangeFetchMapCalculatorTest.java index 1b3fe03b1b..78e87c1b96 100644 --- a/test/unit/org/apache/cassandra/dht/RangeFetchMapCalculatorTest.java +++ b/test/unit/org/apache/cassandra/dht/RangeFetchMapCalculatorTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.dht; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; @@ -34,6 +33,7 @@ import org.junit.Test; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.locator.AbstractNetworkTopologySnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -50,13 +50,13 @@ public class RangeFetchMapCalculatorTest //Odd IPs are in DC1 and Even are in DC2. Endpoints upto .14 will have unique racks and // then will be same for a set of three. @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return "RAC1"; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { if (getIPLastPart(endpoint) <= 50) return DatabaseDescriptor.getLocalDataCenter(); @@ -66,9 +66,9 @@ public class RangeFetchMapCalculatorTest return DatabaseDescriptor.getLocalDataCenter() + "Remote"; } - private int getIPLastPart(InetAddress endpoint) + private int getIPLastPart(InetAddressAndPort endpoint) { - String str = endpoint.toString(); + String str = endpoint.address.toString(); int index = str.lastIndexOf("."); return Integer.parseInt(str.substring(index + 1).trim()); } @@ -78,7 +78,7 @@ public class RangeFetchMapCalculatorTest @Test public void testWithSingleSource() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.2"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.3"); @@ -86,7 +86,7 @@ public class RangeFetchMapCalculatorTest addNonTrivialRangeAndSources(rangesWithSources, 41, 50, "127.0.0.5"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); Assert.assertEquals(4, map.asMap().keySet().size()); @@ -95,7 +95,7 @@ public class RangeFetchMapCalculatorTest @Test public void testWithNonOverlappingSource() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1", "127.0.0.2"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.3", "127.0.0.4"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.5", "127.0.0.6"); @@ -103,7 +103,7 @@ public class RangeFetchMapCalculatorTest addNonTrivialRangeAndSources(rangesWithSources, 41, 50, "127.0.0.9", "127.0.0.10"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); Assert.assertEquals(5, map.asMap().keySet().size()); @@ -112,13 +112,13 @@ public class RangeFetchMapCalculatorTest @Test public void testWithRFThreeReplacement() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1", "127.0.0.2"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.2", "127.0.0.3"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.3", "127.0.0.4"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); //We should validate that it streamed from 3 unique sources @@ -128,7 +128,7 @@ public class RangeFetchMapCalculatorTest @Test public void testForMultipleRoundsComputation() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.3"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.3"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.3"); @@ -136,21 +136,21 @@ public class RangeFetchMapCalculatorTest addNonTrivialRangeAndSources(rangesWithSources, 41, 50, "127.0.0.3", "127.0.0.2"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); //We should validate that it streamed from 2 unique sources Assert.assertEquals(2, map.asMap().keySet().size()); assertArrays(Arrays.asList(generateNonTrivialRange(1, 10), generateNonTrivialRange(11, 20), generateNonTrivialRange(21, 30), generateNonTrivialRange(31, 40)), - map.asMap().get(InetAddress.getByName("127.0.0.3"))); - assertArrays(Arrays.asList(generateNonTrivialRange(41, 50)), map.asMap().get(InetAddress.getByName("127.0.0.2"))); + map.asMap().get(InetAddressAndPort.getByName("127.0.0.3"))); + assertArrays(Arrays.asList(generateNonTrivialRange(41, 50)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.2"))); } @Test public void testForMultipleRoundsComputationWithLocalHost() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.1"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.1"); @@ -158,19 +158,19 @@ public class RangeFetchMapCalculatorTest addNonTrivialRangeAndSources(rangesWithSources, 41, 50, "127.0.0.1", "127.0.0.2"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); //We should validate that it streamed from only non local host and only one range Assert.assertEquals(1, map.asMap().keySet().size()); - assertArrays(Arrays.asList(generateNonTrivialRange(41, 50)), map.asMap().get(InetAddress.getByName("127.0.0.2"))); + assertArrays(Arrays.asList(generateNonTrivialRange(41, 50)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.2"))); } @Test public void testForEmptyGraph() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.1"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.1"); @@ -178,7 +178,7 @@ public class RangeFetchMapCalculatorTest addNonTrivialRangeAndSources(rangesWithSources, 41, 50, "127.0.0.1"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); //All ranges map to local host so we will not stream anything. assertTrue(map.isEmpty()); } @@ -186,7 +186,7 @@ public class RangeFetchMapCalculatorTest @Test public void testWithNoSourceWithLocal() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1", "127.0.0.5"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.2"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.3"); @@ -194,11 +194,11 @@ public class RangeFetchMapCalculatorTest //Return false for all except 127.0.0.5 final RangeStreamer.ISourceFilter filter = new RangeStreamer.ISourceFilter() { - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { try { - if (endpoint.equals(InetAddress.getByName("127.0.0.5"))) + if (endpoint.equals(InetAddressAndPort.getByName("127.0.0.5"))) return false; else return true; @@ -211,28 +211,28 @@ public class RangeFetchMapCalculatorTest }; RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, Arrays.asList(filter), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); //We should validate that it streamed from only non local host and only one range Assert.assertEquals(2, map.asMap().keySet().size()); - assertArrays(Arrays.asList(generateNonTrivialRange(11, 20)), map.asMap().get(InetAddress.getByName("127.0.0.2"))); - assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddress.getByName("127.0.0.3"))); + assertArrays(Arrays.asList(generateNonTrivialRange(11, 20)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.2"))); + assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.3"))); } @Test (expected = IllegalStateException.class) public void testWithNoLiveSource() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.5"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.2"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.3"); final RangeStreamer.ISourceFilter allDeadFilter = new RangeStreamer.ISourceFilter() { - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { return false; } @@ -245,25 +245,25 @@ public class RangeFetchMapCalculatorTest @Test public void testForLocalDC() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.1", "127.0.0.3", "127.0.0.53"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.1", "127.0.0.3", "127.0.0.57"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.2", "127.0.0.59", "127.0.0.61"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, new ArrayList<>(), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); Assert.assertEquals(2, map.asMap().size()); //Should have streamed from local DC endpoints - assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddress.getByName("127.0.0.2"))); - assertArrays(Arrays.asList(generateNonTrivialRange(1, 10), generateNonTrivialRange(11, 20)), map.asMap().get(InetAddress.getByName("127.0.0.3"))); + assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.2"))); + assertArrays(Arrays.asList(generateNonTrivialRange(1, 10), generateNonTrivialRange(11, 20)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.3"))); } @Test public void testForRemoteDC() throws Exception { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.3", "127.0.0.51"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.3", "127.0.0.55"); addNonTrivialRangeAndSources(rangesWithSources, 21, 30, "127.0.0.2", "127.0.0.59"); @@ -271,11 +271,11 @@ public class RangeFetchMapCalculatorTest //Reject only 127.0.0.3 and accept everyone else final RangeStreamer.ISourceFilter localHostFilter = new RangeStreamer.ISourceFilter() { - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { try { - if (endpoint.equals(InetAddress.getByName("127.0.0.3"))) + if (endpoint.equals(InetAddressAndPort.getByName("127.0.0.3"))) return false; else return true; @@ -288,20 +288,20 @@ public class RangeFetchMapCalculatorTest }; RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, Arrays.asList(localHostFilter), "Test"); - Multimap> map = calculator.getRangeFetchMap(); + Multimap> map = calculator.getRangeFetchMap(); validateRange(rangesWithSources, map); Assert.assertEquals(3, map.asMap().size()); //Should have streamed from remote DC endpoint - assertArrays(Arrays.asList(generateNonTrivialRange(1, 10)), map.asMap().get(InetAddress.getByName("127.0.0.51"))); - assertArrays(Arrays.asList(generateNonTrivialRange(11, 20)), map.asMap().get(InetAddress.getByName("127.0.0.55"))); - assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddress.getByName("127.0.0.2"))); + assertArrays(Arrays.asList(generateNonTrivialRange(1, 10)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.51"))); + assertArrays(Arrays.asList(generateNonTrivialRange(11, 20)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.55"))); + assertArrays(Arrays.asList(generateNonTrivialRange(21, 30)), map.asMap().get(InetAddressAndPort.getByName("127.0.0.2"))); } @Test public void testTrivialRanges() throws UnknownHostException { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); // add non-trivial ranges addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.3", "127.0.0.51"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.3", "127.0.0.55"); @@ -309,17 +309,17 @@ public class RangeFetchMapCalculatorTest // and a trivial one: addTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.3", "127.0.0.51"); RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, Collections.emptyList(), "Test"); - Multimap> optMap = calculator.getRangeFetchMapForNonTrivialRanges(); - Multimap> trivialMap = calculator.getRangeFetchMapForTrivialRanges(optMap); - assertTrue(trivialMap.get(InetAddress.getByName("127.0.0.3")).contains(generateTrivialRange(1,10)) ^ - trivialMap.get(InetAddress.getByName("127.0.0.51")).contains(generateTrivialRange(1,10))); + Multimap> optMap = calculator.getRangeFetchMapForNonTrivialRanges(); + Multimap> trivialMap = calculator.getRangeFetchMapForTrivialRanges(optMap); + assertTrue(trivialMap.get(InetAddressAndPort.getByName("127.0.0.3")).contains(generateTrivialRange(1,10)) ^ + trivialMap.get(InetAddressAndPort.getByName("127.0.0.51")).contains(generateTrivialRange(1,10))); assertFalse(optMap.containsKey(generateTrivialRange(1, 10))); } @Test(expected = IllegalStateException.class) public void testNotEnoughEndpointsForTrivialRange() throws UnknownHostException { - Multimap, InetAddress> rangesWithSources = HashMultimap.create(); + Multimap, InetAddressAndPort> rangesWithSources = HashMultimap.create(); // add non-trivial ranges addNonTrivialRangeAndSources(rangesWithSources, 1, 10, "127.0.0.3", "127.0.0.51"); addNonTrivialRangeAndSources(rangesWithSources, 11, 20, "127.0.0.3", "127.0.0.55"); @@ -329,11 +329,11 @@ public class RangeFetchMapCalculatorTest RangeStreamer.ISourceFilter filter = new RangeStreamer.ISourceFilter() { - public boolean shouldInclude(InetAddress endpoint) + public boolean shouldInclude(InetAddressAndPort endpoint) { try { - if (endpoint.equals(InetAddress.getByName("127.0.0.3"))) + if (endpoint.equals(InetAddressAndPort.getByName("127.0.0.3"))) return false; } catch (UnknownHostException e) @@ -344,8 +344,8 @@ public class RangeFetchMapCalculatorTest } }; RangeFetchMapCalculator calculator = new RangeFetchMapCalculator(rangesWithSources, Collections.singleton(filter), "Test"); - Multimap> optMap = calculator.getRangeFetchMapForNonTrivialRanges(); - Multimap> trivialMap = calculator.getRangeFetchMapForTrivialRanges(optMap); + Multimap> optMap = calculator.getRangeFetchMapForNonTrivialRanges(); + Multimap> trivialMap = calculator.getRangeFetchMapForTrivialRanges(optMap); } @@ -355,35 +355,35 @@ public class RangeFetchMapCalculatorTest assertTrue(result.containsAll(expected)); } - private void validateRange(Multimap, InetAddress> rangesWithSources, Multimap> result) + private void validateRange(Multimap, InetAddressAndPort> rangesWithSources, Multimap> result) { - for (Map.Entry> entry : result.entries()) + for (Map.Entry> entry : result.entries()) { assertTrue(rangesWithSources.get(entry.getValue()).contains(entry.getKey())); } } - private void addNonTrivialRangeAndSources(Multimap, InetAddress> rangesWithSources, int left, int right, String... hosts) throws UnknownHostException + private void addNonTrivialRangeAndSources(Multimap, InetAddressAndPort> rangesWithSources, int left, int right, String... hosts) throws UnknownHostException { - for (InetAddress endpoint : makeAddrs(hosts)) + for (InetAddressAndPort endpoint : makeAddrs(hosts)) { rangesWithSources.put(generateNonTrivialRange(left, right), endpoint); } } - private void addTrivialRangeAndSources(Multimap, InetAddress> rangesWithSources, int left, int right, String... hosts) throws UnknownHostException + private void addTrivialRangeAndSources(Multimap, InetAddressAndPort> rangesWithSources, int left, int right, String... hosts) throws UnknownHostException { - for (InetAddress endpoint : makeAddrs(hosts)) + for (InetAddressAndPort endpoint : makeAddrs(hosts)) { rangesWithSources.put(generateTrivialRange(left, right), endpoint); } } - private Collection makeAddrs(String... hosts) throws UnknownHostException + private Collection makeAddrs(String... hosts) throws UnknownHostException { - ArrayList addrs = new ArrayList(hosts.length); + ArrayList addrs = new ArrayList<>(hosts.length); for (String host : hosts) - addrs.add(InetAddress.getByName(host)); + addrs.add(InetAddressAndPort.getByName(host)); return addrs; } diff --git a/test/unit/org/apache/cassandra/dht/StreamStateStoreTest.java b/test/unit/org/apache/cassandra/dht/StreamStateStoreTest.java index b5f80360ec..ad5f8f5951 100644 --- a/test/unit/org/apache/cassandra/dht/StreamStateStoreTest.java +++ b/test/unit/org/apache/cassandra/dht/StreamStateStoreTest.java @@ -17,13 +17,13 @@ */ package org.apache.cassandra.dht; -import java.net.InetAddress; import java.util.Collections; import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.DefaultConnectionFactory; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.streaming.StreamEvent; @@ -50,7 +50,7 @@ public class StreamStateStoreTest Token.TokenFactory factory = p.getTokenFactory(); Range range = new Range<>(factory.fromString("0"), factory.fromString("100")); - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); StreamSession session = new StreamSession(local, local, new DefaultConnectionFactory(), 0, true, null, PreviewKind.NONE); session.addStreamRequest("keyspace1", Collections.singleton(range), Collections.singleton("cf")); diff --git a/test/unit/org/apache/cassandra/gms/ArrivalWindowTest.java b/test/unit/org/apache/cassandra/gms/ArrivalWindowTest.java index 511511bc5c..ea59300f33 100644 --- a/test/unit/org/apache/cassandra/gms/ArrivalWindowTest.java +++ b/test/unit/org/apache/cassandra/gms/ArrivalWindowTest.java @@ -25,9 +25,8 @@ import static org.junit.Assert.*; import org.junit.Test; -import java.lang.RuntimeException; -import java.net.InetAddress; -import java.net.UnknownHostException; +import org.apache.cassandra.locator.InetAddressAndPort; +import org.apache.cassandra.utils.FBUtilities; public class ArrivalWindowTest { @@ -36,15 +35,7 @@ public class ArrivalWindowTest { final ArrivalWindow windowWithNano = new ArrivalWindow(4); final long toNano = 1000000L; - InetAddress ep; - try - { - ep = InetAddress.getLocalHost(); - } - catch (UnknownHostException e) - { - throw new RuntimeException(e); - } + InetAddressAndPort ep = FBUtilities.getLocalAddressAndPort(); windowWithNano.add(111 * toNano, ep); windowWithNano.add(222 * toNano, ep); windowWithNano.add(333 * toNano, ep); diff --git a/test/unit/org/apache/cassandra/gms/EndpointStateTest.java b/test/unit/org/apache/cassandra/gms/EndpointStateTest.java index 2453fe8805..6e0cc75a0f 100644 --- a/test/unit/org/apache/cassandra/gms/EndpointStateTest.java +++ b/test/unit/org/apache/cassandra/gms/EndpointStateTest.java @@ -72,7 +72,7 @@ public class EndpointStateTest public void run() { state.addApplicationState(ApplicationState.TOKENS, valueFactory.tokens(tokens)); - state.addApplicationState(ApplicationState.STATUS, valueFactory.normal(tokens)); + state.addApplicationState(ApplicationState.STATUS_WITH_PORT, valueFactory.normal(tokens)); } }); @@ -86,7 +86,7 @@ public class EndpointStateTest for (Map.Entry entry : state.states()) values.put(entry.getKey(), entry.getValue()); - if (values.containsKey(ApplicationState.STATUS) && !values.containsKey(ApplicationState.TOKENS)) + if (values.containsKey(ApplicationState.STATUS_WITH_PORT) && !values.containsKey(ApplicationState.TOKENS)) { numFailures.incrementAndGet(); System.out.println(String.format("Failed: %s", values)); @@ -129,7 +129,7 @@ public class EndpointStateTest { Map states = new EnumMap<>(ApplicationState.class); states.put(ApplicationState.TOKENS, valueFactory.tokens(tokens)); - states.put(ApplicationState.STATUS, valueFactory.normal(tokens)); + states.put(ApplicationState.STATUS_WITH_PORT, valueFactory.normal(tokens)); state.addApplicationStates(states); } }); @@ -158,7 +158,7 @@ public class EndpointStateTest for (Map.Entry entry : states) values.put(entry.getKey(), entry.getValue()); - assertTrue(values.containsKey(ApplicationState.STATUS)); + assertTrue(values.containsKey(ApplicationState.STATUS_WITH_PORT)); assertTrue(values.containsKey(ApplicationState.TOKENS)); assertTrue(values.containsKey(ApplicationState.INTERNAL_IP)); assertTrue(values.containsKey(ApplicationState.HOST_ID)); diff --git a/test/unit/org/apache/cassandra/gms/FailureDetectorTest.java b/test/unit/org/apache/cassandra/gms/FailureDetectorTest.java index dca00b1131..554e894b4f 100644 --- a/test/unit/org/apache/cassandra/gms/FailureDetectorTest.java +++ b/test/unit/org/apache/cassandra/gms/FailureDetectorTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; @@ -33,6 +32,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.RandomPartitioner; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.StorageService; @@ -60,7 +60,7 @@ public class FailureDetectorTest ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); // we want to convict if there is any heartbeat data present in the FD @@ -69,12 +69,12 @@ public class FailureDetectorTest // create a ring of 2 nodes Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, 3); - InetAddress leftHost = hosts.get(1); + InetAddressAndPort leftHost = hosts.get(1); FailureDetector.instance.report(leftHost); // trigger handleStateLeft in StorageService - ss.onChange(leftHost, ApplicationState.STATUS, + ss.onChange(leftHost, ApplicationState.STATUS_WITH_PORT, valueFactory.left(Collections.singleton(endpointTokens.get(1)), Gossiper.computeExpireTime())); // confirm that handleStateLeft was called and leftEndpoint was removed from TokenMetadata diff --git a/test/unit/org/apache/cassandra/gms/GossipDigestTest.java b/test/unit/org/apache/cassandra/gms/GossipDigestTest.java index 3191b039e6..cb67a54585 100644 --- a/test/unit/org/apache/cassandra/gms/GossipDigestTest.java +++ b/test/unit/org/apache/cassandra/gms/GossipDigestTest.java @@ -26,8 +26,7 @@ import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputBuffer; -import java.net.InetAddress; - +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.junit.Test; @@ -36,7 +35,7 @@ public class GossipDigestTest @Test public void test() throws IOException { - InetAddress endpoint = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort endpoint = InetAddressAndPort.getByName("127.0.0.1"); int generation = 0; int maxVersion = 123; GossipDigest expected = new GossipDigest(endpoint, generation, maxVersion); diff --git a/test/unit/org/apache/cassandra/gms/GossiperTest.java b/test/unit/org/apache/cassandra/gms/GossiperTest.java index 83f12d13a0..8c65cb4d7f 100644 --- a/test/unit/org/apache/cassandra/gms/GossiperTest.java +++ b/test/unit/org/apache/cassandra/gms/GossiperTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -33,6 +32,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.RandomPartitioner; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.StorageService; @@ -49,7 +49,7 @@ public class GossiperTest TokenMetadata tmd = StorageService.instance.getTokenMetadata(); ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); @Before @@ -62,7 +62,7 @@ public class GossiperTest public void testLargeGenerationJump() throws UnknownHostException, InterruptedException { Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, 2); - InetAddress remoteHostAddress = hosts.get(1); + InetAddressAndPort remoteHostAddress = hosts.get(1); EndpointState initialRemoteState = Gossiper.instance.getEndpointStateForEndpoint(remoteHostAddress); HeartBeatState initialRemoteHeartBeat = initialRemoteState.getHeartBeatState(); diff --git a/test/unit/org/apache/cassandra/gms/PendingRangeCalculatorServiceTest.java b/test/unit/org/apache/cassandra/gms/PendingRangeCalculatorServiceTest.java index 0939db0290..833ee8bcaf 100644 --- a/test/unit/org/apache/cassandra/gms/PendingRangeCalculatorServiceTest.java +++ b/test/unit/org/apache/cassandra/gms/PendingRangeCalculatorServiceTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.gms; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -35,6 +34,7 @@ import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.dht.ByteOrderedPartitioner; import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.StorageService; import org.jboss.byteman.contrib.bmunit.BMRule; import org.jboss.byteman.contrib.bmunit.BMUnitRunner; @@ -66,7 +66,7 @@ public class PendingRangeCalculatorServiceTest action = "org.apache.cassandra.gms.PendingRangeCalculatorServiceTest.calculationLock.lock()") public void testDelayedResponse() throws UnknownHostException, InterruptedException { - InetAddress otherNodeAddr = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort otherNodeAddr = InetAddressAndPort.getByName("127.0.0.2"); UUID otherHostId = UUID.randomUUID(); // introduce node for first major state change @@ -112,7 +112,7 @@ public class PendingRangeCalculatorServiceTest } } - private Map getStates(InetAddress otherNodeAddr, UUID hostId, int ver, boolean bootstrapping) + private Map getStates(InetAddressAndPort otherNodeAddr, UUID hostId, int ver, boolean bootstrapping) { HeartBeatState hb = new HeartBeatState(1, ver); EndpointState state = new EndpointState(hb); @@ -125,7 +125,7 @@ public class PendingRangeCalculatorServiceTest state.addApplicationState(ApplicationState.HOST_ID, StorageService.instance.valueFactory.hostId(hostId)); state.addApplicationState(ApplicationState.NET_VERSION, StorageService.instance.valueFactory.networkVersion()); - Map states = new HashMap<>(); + Map states = new HashMap<>(); states.put(otherNodeAddr, state); return states; } diff --git a/test/unit/org/apache/cassandra/gms/SerializationsTest.java b/test/unit/org/apache/cassandra/gms/SerializationsTest.java index 0df266f3fa..90ce10ba0d 100644 --- a/test/unit/org/apache/cassandra/gms/SerializationsTest.java +++ b/test/unit/org/apache/cassandra/gms/SerializationsTest.java @@ -24,6 +24,7 @@ import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus; import org.apache.cassandra.io.util.DataOutputStreamPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; @@ -31,7 +32,6 @@ import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -78,9 +78,9 @@ public class SerializationsTest extends AbstractSerializationsTester private void testGossipDigestWrite() throws IOException { - Map states = new HashMap(); - states.put(InetAddress.getByName("127.0.0.1"), Statics.EndpointSt); - states.put(InetAddress.getByName("127.0.0.2"), Statics.EndpointSt); + Map states = new HashMap<>(); + states.put(InetAddressAndPort.getByName("127.0.0.1"), Statics.EndpointSt); + states.put(InetAddressAndPort.getByName("127.0.0.2"), Statics.EndpointSt); GossipDigestAck ack = new GossipDigestAck(Statics.Digests, states); GossipDigestAck2 ack2 = new GossipDigestAck2(states); GossipDigestSyn syn = new GossipDigestSyn("Not a real cluster name", @@ -132,9 +132,9 @@ public class SerializationsTest extends AbstractSerializationsTester { HeartbeatSt.updateHeartBeat(); EndpointSt.addApplicationState(ApplicationState.LOAD, vv0); - EndpointSt.addApplicationState(ApplicationState.STATUS, vv1); + EndpointSt.addApplicationState(ApplicationState.STATUS_WITH_PORT, vv1); for (int i = 0; i < 100; i++) - Digests.add(new GossipDigest(FBUtilities.getBroadcastAddress(), 100 + i, 1000 + 2 * i)); + Digests.add(new GossipDigest(FBUtilities.getBroadcastAddressAndPort(), 100 + i, 1000 + 2 * i)); } } } diff --git a/test/unit/org/apache/cassandra/hints/HintTest.java b/test/unit/org/apache/cassandra/hints/HintTest.java index 23189e103e..e8960f5118 100644 --- a/test/unit/org/apache/cassandra/hints/HintTest.java +++ b/test/unit/org/apache/cassandra/hints/HintTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.hints; import java.io.IOException; -import java.net.InetAddress; import java.util.Collections; import java.util.UUID; @@ -39,6 +38,7 @@ import org.apache.cassandra.dht.BootStrapper; import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputBuffer; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.metrics.StorageMetrics; import org.apache.cassandra.net.MessageIn; @@ -81,7 +81,7 @@ public class HintTest public void resetGcGraceSeconds() { TokenMetadata tokenMeta = StorageService.instance.getTokenMetadata(); - InetAddress local = FBUtilities.getBroadcastAddress(); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); tokenMeta.clearUnsafe(); tokenMeta.updateHostId(UUID.randomUUID(), local); tokenMeta.updateNormalTokens(BootStrapper.getRandomTokens(tokenMeta, 1), local); @@ -230,8 +230,8 @@ public class HintTest // Prepare metadata with injected stale endpoint serving the mutation key. TokenMetadata tokenMeta = StorageService.instance.getTokenMetadata(); - InetAddress local = FBUtilities.getBroadcastAddress(); - InetAddress endpoint = InetAddress.getByName("1.1.1.1"); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); + InetAddressAndPort endpoint = InetAddressAndPort.getByName("1.1.1.1"); UUID localId = StorageService.instance.getLocalHostUUID(); UUID targetId = UUID.randomUUID(); tokenMeta.updateHostId(targetId, endpoint); @@ -271,8 +271,8 @@ public class HintTest // Prepare metadata with injected stale endpoint. TokenMetadata tokenMeta = StorageService.instance.getTokenMetadata(); - InetAddress local = FBUtilities.getBroadcastAddress(); - InetAddress endpoint = InetAddress.getByName("1.1.1.1"); + InetAddressAndPort local = FBUtilities.getBroadcastAddressAndPort(); + InetAddressAndPort endpoint = InetAddressAndPort.getByName("1.1.1.1"); UUID localId = StorageService.instance.getLocalHostUUID(); UUID targetId = UUID.randomUUID(); tokenMeta.updateHostId(targetId, endpoint); diff --git a/test/unit/org/apache/cassandra/hints/HintsServiceTest.java b/test/unit/org/apache/cassandra/hints/HintsServiceTest.java index 2da8993b04..b71140f093 100644 --- a/test/unit/org/apache/cassandra/hints/HintsServiceTest.java +++ b/test/unit/org/apache/cassandra/hints/HintsServiceTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.hints; -import java.net.InetAddress; import java.util.Collections; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -34,6 +33,7 @@ import org.junit.Test; import com.datastax.driver.core.utils.MoreFutures; import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.DecoratedKey; @@ -182,7 +182,7 @@ public class HintsServiceTest private MockMessagingSpy sendHintsAndResponses(int noOfHints, int noOfResponses) { // create spy for hint messages, but only create responses for noOfResponses hints - MessageIn messageIn = MessageIn.create(FBUtilities.getBroadcastAddress(), + MessageIn messageIn = MessageIn.create(FBUtilities.getBroadcastAddressAndPort(), HintResponse.instance, Collections.emptyMap(), MessagingService.Verb.REQUEST_RESPONSE, @@ -217,17 +217,17 @@ public class HintsServiceTest { private boolean isAlive = true; - public boolean isAlive(InetAddress ep) + public boolean isAlive(InetAddressAndPort ep) { return isAlive; } - public void interpret(InetAddress ep) + public void interpret(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } - public void report(InetAddress ep) + public void report(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } @@ -242,12 +242,12 @@ public class HintsServiceTest throw new UnsupportedOperationException(); } - public void remove(InetAddress ep) + public void remove(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } - public void forceConviction(InetAddress ep) + public void forceConviction(InetAddressAndPort ep) { throw new UnsupportedOperationException(); } diff --git a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index d63d6c3fa4..dbb929dcde 100644 --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@ -650,7 +650,7 @@ public class CQLSSTableWriterTest { this.keyspace = keyspace; for (Range range : StorageService.instance.getLocalRanges(ks)) - addRangeForEndpoint(range, FBUtilities.getBroadcastAddress()); + addRangeForEndpoint(range, FBUtilities.getBroadcastAddressAndPort()); } public TableMetadataRef getTableMetadata(String cfName) diff --git a/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java b/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java index 6e7d1730bc..8fc69bfdbb 100644 --- a/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java @@ -215,7 +215,7 @@ public class LegacySSTableTest details.add(new StreamSession.SSTableStreamingSections(sstable.ref(), sstable.getPositionsForRanges(ranges), sstable.estimatedKeysForRanges(ranges))); - new StreamPlan(StreamOperation.OTHER).transferFiles(FBUtilities.getBroadcastAddress(), details) + new StreamPlan(StreamOperation.OTHER).transferFiles(FBUtilities.getBroadcastAddressAndPort(), details) .execute().get(); } diff --git a/test/unit/org/apache/cassandra/io/sstable/SSTableLoaderTest.java b/test/unit/org/apache/cassandra/io/sstable/SSTableLoaderTest.java index 4f3739fb3e..430b7c279e 100644 --- a/test/unit/org/apache/cassandra/io/sstable/SSTableLoaderTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/SSTableLoaderTest.java @@ -104,7 +104,7 @@ public class SSTableLoaderTest { this.keyspace = keyspace; for (Range range : StorageService.instance.getLocalRanges(KEYSPACE1)) - addRangeForEndpoint(range, FBUtilities.getBroadcastAddress()); + addRangeForEndpoint(range, FBUtilities.getBroadcastAddressAndPort()); } public TableMetadataRef getTableMetadata(String tableName) diff --git a/test/unit/org/apache/cassandra/locator/CloudstackSnitchTest.java b/test/unit/org/apache/cassandra/locator/CloudstackSnitchTest.java index 34f8204a69..391f789657 100644 --- a/test/unit/org/apache/cassandra/locator/CloudstackSnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/CloudstackSnitchTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; import java.util.EnumMap; import java.util.Map; @@ -77,8 +76,8 @@ public class CloudstackSnitchTest { az = "ch-gva-1"; CloudstackSnitch snitch = new TestCloudstackSnitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); - InetAddress nonlocal = InetAddress.getByName("127.0.0.7"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort nonlocal = InetAddressAndPort.getByName("127.0.0.7"); Gossiper.instance.addSavedEndpoint(nonlocal); Map stateMap = new EnumMap<>(ApplicationState.class); @@ -99,7 +98,7 @@ public class CloudstackSnitchTest { az = "ch-gva-1"; CloudstackSnitch snitch = new TestCloudstackSnitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); assertEquals("ch-gva", snitch.getDatacenter(local)); assertEquals("1", snitch.getRack(local)); diff --git a/test/unit/org/apache/cassandra/locator/DynamicEndpointSnitchTest.java b/test/unit/org/apache/cassandra/locator/DynamicEndpointSnitchTest.java index 8a59a4a0cb..bf1e4c2444 100644 --- a/test/unit/org/apache/cassandra/locator/DynamicEndpointSnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/DynamicEndpointSnitchTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; import java.util.*; import org.junit.BeforeClass; @@ -41,7 +40,7 @@ public class DynamicEndpointSnitchTest DatabaseDescriptor.daemonInitialization(); } - private static void setScores(DynamicEndpointSnitch dsnitch, int rounds, List hosts, Integer... scores) throws InterruptedException + private static void setScores(DynamicEndpointSnitch dsnitch, int rounds, List hosts, Integer... scores) throws InterruptedException { for (int round = 0; round < rounds; round++) { @@ -58,16 +57,16 @@ public class DynamicEndpointSnitchTest StorageService.instance.unsafeInitialize(); SimpleSnitch ss = new SimpleSnitch(); DynamicEndpointSnitch dsnitch = new DynamicEndpointSnitch(ss, String.valueOf(ss.hashCode())); - InetAddress self = FBUtilities.getBroadcastAddress(); - InetAddress host1 = InetAddress.getByName("127.0.0.2"); - InetAddress host2 = InetAddress.getByName("127.0.0.3"); - InetAddress host3 = InetAddress.getByName("127.0.0.4"); - InetAddress host4 = InetAddress.getByName("127.0.0.5"); - List hosts = Arrays.asList(host1, host2, host3); + InetAddressAndPort self = FBUtilities.getBroadcastAddressAndPort(); + InetAddressAndPort host1 = InetAddressAndPort.getByName("127.0.0.2"); + InetAddressAndPort host2 = InetAddressAndPort.getByName("127.0.0.3"); + InetAddressAndPort host3 = InetAddressAndPort.getByName("127.0.0.4"); + InetAddressAndPort host4 = InetAddressAndPort.getByName("127.0.0.5"); + List hosts = Arrays.asList(host1, host2, host3); // first, make all hosts equal setScores(dsnitch, 1, hosts, 10, 10, 10); - List order = Arrays.asList(host1, host2, host3); + List order = Arrays.asList(host1, host2, host3); assertEquals(order, dsnitch.getSortedListByProximity(self, Arrays.asList(host1, host2, host3))); // make host1 a little worse diff --git a/test/unit/org/apache/cassandra/locator/EC2SnitchTest.java b/test/unit/org/apache/cassandra/locator/EC2SnitchTest.java index 09973a8bc8..182ff1ae73 100644 --- a/test/unit/org/apache/cassandra/locator/EC2SnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/EC2SnitchTest.java @@ -20,7 +20,7 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.EnumMap; import java.util.Map; @@ -72,8 +72,8 @@ public class EC2SnitchTest { az = "us-east-1d"; Ec2Snitch snitch = new TestEC2Snitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); - InetAddress nonlocal = InetAddress.getByName("127.0.0.7"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort nonlocal = InetAddressAndPort.getByName("127.0.0.7"); Gossiper.instance.addSavedEndpoint(nonlocal); Map stateMap = new EnumMap<>(ApplicationState.class); @@ -93,7 +93,7 @@ public class EC2SnitchTest { az = "us-east-2d"; Ec2Snitch snitch = new TestEC2Snitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); assertEquals("us-east-2", snitch.getDatacenter(local)); assertEquals("2d", snitch.getRack(local)); } diff --git a/test/unit/org/apache/cassandra/locator/GoogleCloudSnitchTest.java b/test/unit/org/apache/cassandra/locator/GoogleCloudSnitchTest.java index c63044c95f..ea6fa2de56 100644 --- a/test/unit/org/apache/cassandra/locator/GoogleCloudSnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/GoogleCloudSnitchTest.java @@ -20,7 +20,6 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; import java.util.EnumMap; import java.util.Map; @@ -72,8 +71,8 @@ public class GoogleCloudSnitchTest { az = "us-central1-a"; GoogleCloudSnitch snitch = new TestGoogleCloudSnitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); - InetAddress nonlocal = InetAddress.getByName("127.0.0.7"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort nonlocal = InetAddressAndPort.getByName("127.0.0.7"); Gossiper.instance.addSavedEndpoint(nonlocal); Map stateMap = new EnumMap<>(ApplicationState.class); @@ -93,7 +92,7 @@ public class GoogleCloudSnitchTest { az = "asia-east1-a"; GoogleCloudSnitch snitch = new TestGoogleCloudSnitch(); - InetAddress local = InetAddress.getByName("127.0.0.1"); + InetAddressAndPort local = InetAddressAndPort.getByName("127.0.0.1"); assertEquals("asia-east1", snitch.getDatacenter(local)); assertEquals("a", snitch.getRack(local)); } diff --git a/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java b/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java index 77734f7207..da260038ca 100644 --- a/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java @@ -18,9 +18,8 @@ package org.apache.cassandra.locator; -import java.net.InetAddress; +import java.net.UnknownHostException; -import com.google.common.net.InetAddresses; import org.junit.BeforeClass; import org.junit.Test; @@ -45,7 +44,15 @@ public class GossipingPropertyFileSnitchTest final String endpointString, final String expectedDatacenter, final String expectedRack) { - final InetAddress endpoint = InetAddresses.forString(endpointString); + final InetAddressAndPort endpoint; + try + { + endpoint = InetAddressAndPort.getByName(endpointString); + } + catch (UnknownHostException e) + { + throw new RuntimeException(e); + } assertEquals(expectedDatacenter, snitch.getDatacenter(endpoint)); assertEquals(expectedRack, snitch.getRack(endpoint)); } @@ -54,6 +61,6 @@ public class GossipingPropertyFileSnitchTest public void testLoadConfig() throws Exception { final GossipingPropertyFileSnitch snitch = new GossipingPropertyFileSnitch(); - checkEndpoint(snitch, FBUtilities.getBroadcastAddress().getHostAddress(), "DC1", "RAC1"); + checkEndpoint(snitch, FBUtilities.getBroadcastAddressAndPort().toString(), "DC1", "RAC1"); } } diff --git a/test/unit/org/apache/cassandra/locator/InetAddressAndPortTest.java b/test/unit/org/apache/cassandra/locator/InetAddressAndPortTest.java new file mode 100644 index 0000000000..c32b9a952b --- /dev/null +++ b/test/unit/org/apache/cassandra/locator/InetAddressAndPortTest.java @@ -0,0 +1,143 @@ +/* + * 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.cassandra.locator; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class InetAddressAndPortTest +{ + private static interface ThrowingRunnable + { + public void run() throws Throwable; + } + + @Test + public void getByNameIPv4Test() throws Exception + { + //Negative port + shouldThrow(() -> InetAddressAndPort.getByName("127.0.0.1:-1"), IllegalArgumentException.class); + //Too large port + shouldThrow(() -> InetAddressAndPort.getByName("127.0.0.1:65536"), IllegalArgumentException.class); + + //bad address, caught by InetAddress + shouldThrow(() -> InetAddressAndPort.getByName("127.0.0.1.0"), UnknownHostException.class); + + //Test default port + InetAddressAndPort address = InetAddressAndPort.getByName("127.0.0.1"); + assertEquals(InetAddress.getByName("127.0.0.1"), address.address); + assertEquals(InetAddressAndPort.defaultPort, address.port); + + //Test overriding default port + address = InetAddressAndPort.getByName("127.0.0.1:42"); + assertEquals(InetAddress.getByName("127.0.0.1"), address.address); + assertEquals(42, address.port); + } + + @Test + public void getByNameIPv6Test() throws Exception + { + //Negative port + shouldThrow(() -> InetAddressAndPort.getByName("[2001:0db8:0000:0000:0000:ff00:0042:8329]:-1"), IllegalArgumentException.class); + //Too large port + shouldThrow(() -> InetAddressAndPort.getByName("[2001:0db8:0000:0000:0000:ff00:0042:8329]:65536"), IllegalArgumentException.class); + + //bad address, caught by InetAddress + shouldThrow(() -> InetAddressAndPort.getByName("2001:0db8:0000:0000:0000:ff00:0042:8329:8329"), UnknownHostException.class); + + //Test default port + InetAddressAndPort address = InetAddressAndPort.getByName("2001:0db8:0000:0000:0000:ff00:0042:8329"); + assertEquals(InetAddress.getByName("2001:0db8:0000:0000:0000:ff00:0042:8329"), address.address); + assertEquals(InetAddressAndPort.defaultPort, address.port); + + //Test overriding default port + address = InetAddressAndPort.getByName("[2001:0db8:0000:0000:0000:ff00:0042:8329]:42"); + assertEquals(InetAddress.getByName("2001:0db8:0000:0000:0000:ff00:0042:8329"), address.address); + assertEquals(42, address.port); + } + + @Test + public void compareAndEqualsAndHashCodeTest() throws Exception + { + InetAddressAndPort address1 = InetAddressAndPort.getByName("127.0.0.1:42"); + InetAddressAndPort address4 = InetAddressAndPort.getByName("127.0.0.1:43"); + InetAddressAndPort address5 = InetAddressAndPort.getByName("127.0.0.1:41"); + InetAddressAndPort address6 = InetAddressAndPort.getByName("127.0.0.2:42"); + InetAddressAndPort address7 = InetAddressAndPort.getByName("127.0.0.0:42"); + + assertEquals(0, address1.compareTo(address1)); + assertEquals(-1, address1.compareTo(address4)); + assertEquals(1, address1.compareTo(address5)); + assertEquals(-1, address1.compareTo(address6)); + assertEquals(1, address1.compareTo(address7)); + + assertEquals(address1, address1); + assertEquals(address1.hashCode(), address1.hashCode()); + assertEquals(address1, InetAddressAndPort.getByName("127.0.0.1:42")); + assertEquals(address1.hashCode(), InetAddressAndPort.getByName("127.0.0.1:42").hashCode()); + assertEquals(address1, InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", 42)); + assertEquals(address1.hashCode(), InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", 42).hashCode()); + int originalPort = InetAddressAndPort.defaultPort; + InetAddressAndPort.initializeDefaultPort(42); + try + { + assertEquals(address1, InetAddressAndPort.getByName("127.0.0.1")); + assertEquals(address1.hashCode(), InetAddressAndPort.getByName("127.0.0.1").hashCode()); + } + finally + { + InetAddressAndPort.initializeDefaultPort(originalPort); + } + assertTrue(!address1.equals(address4)); + assertTrue(!address1.equals(address5)); + assertTrue(!address1.equals(address6)); + assertTrue(!address1.equals(address7)); + } + + @Test + public void toStringTest() throws Exception + { + String ipv4 = "127.0.0.1:42"; + String ipv6 = "[2001:db8:0:0:0:ff00:42:8329]:42"; + assertEquals(ipv4, InetAddressAndPort.getByName(ipv4).toString()); + assertEquals(ipv6, InetAddressAndPort.getByName(ipv6).toString()); + } + + + private void shouldThrow(ThrowingRunnable t, Class expectedClass) + { + try + { + t.run(); + } + catch (Throwable thrown) + { + assertEquals(thrown.getClass(), expectedClass); + return; + } + fail("Runnable didn't throw"); + } + +} diff --git a/test/unit/org/apache/cassandra/locator/NetworkTopologyStrategyTest.java b/test/unit/org/apache/cassandra/locator/NetworkTopologyStrategyTest.java index f64b84a929..ab6c6cdb70 100644 --- a/test/unit/org/apache/cassandra/locator/NetworkTopologyStrategyTest.java +++ b/test/unit/org/apache/cassandra/locator/NetworkTopologyStrategyTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.stream.Collectors; @@ -73,9 +72,9 @@ public class NetworkTopologyStrategyTest assert strategy.getReplicationFactor("DC2") == 2; assert strategy.getReplicationFactor("DC3") == 1; // Query for the natural hosts - ArrayList endpoints = strategy.getNaturalEndpoints(new StringToken("123")); + ArrayList endpoints = strategy.getNaturalEndpoints(new StringToken("123")); assert 6 == endpoints.size(); - assert 6 == new HashSet(endpoints).size(); // ensure uniqueness + assert 6 == new HashSet<>(endpoints).size(); // ensure uniqueness } @Test @@ -97,9 +96,9 @@ public class NetworkTopologyStrategyTest assert strategy.getReplicationFactor("DC2") == 3; assert strategy.getReplicationFactor("DC3") == 0; // Query for the natural hosts - ArrayList endpoints = strategy.getNaturalEndpoints(new StringToken("123")); + ArrayList endpoints = strategy.getNaturalEndpoints(new StringToken("123")); assert 6 == endpoints.size(); - assert 6 == new HashSet(endpoints).size(); // ensure uniqueness + assert 6 == new HashSet<>(endpoints).size(); // ensure uniqueness } @Test @@ -113,7 +112,7 @@ public class NetworkTopologyStrategyTest DatabaseDescriptor.setEndpointSnitch(snitch); TokenMetadata metadata = new TokenMetadata(); Map configOptions = new HashMap(); - Multimap tokens = HashMultimap.create(); + Multimap tokens = HashMultimap.create(); int totalRF = 0; for (int dc = 0; dc < dcRacks.length; ++dc) @@ -125,7 +124,7 @@ public class NetworkTopologyStrategyTest for (int ep = 1; ep <= dcEndpoints[dc]/dcRacks[dc]; ++ep) { byte[] ipBytes = new byte[]{10, (byte)dc, (byte)rack, (byte)ep}; - InetAddress address = InetAddress.getByAddress(ipBytes); + InetAddressAndPort address = InetAddressAndPort.getByAddress(ipBytes); StringToken token = new StringToken(String.format("%02x%02x%02x", ep, rack, dc)); logger.debug("adding node {} at {}", address, token); tokens.put(address, token); @@ -138,8 +137,8 @@ public class NetworkTopologyStrategyTest for (String testToken : new String[]{"123456", "200000", "000402", "ffffff", "400200"}) { - List endpoints = strategy.calculateNaturalEndpoints(new StringToken(testToken), metadata); - Set epSet = new HashSet(endpoints); + List endpoints = strategy.calculateNaturalEndpoints(new StringToken(testToken), metadata); + Set epSet = new HashSet<>(endpoints); Assert.assertEquals(totalRF, endpoints.size()); Assert.assertEquals(totalRF, epSet.size()); @@ -172,7 +171,7 @@ public class NetworkTopologyStrategyTest public void tokenFactory(TokenMetadata metadata, String token, byte[] bytes) throws UnknownHostException { Token token1 = new StringToken(token); - InetAddress add1 = InetAddress.getByAddress(bytes); + InetAddressAndPort add1 = InetAddressAndPort.getByAddress(bytes); metadata.updateNormalToken(token1, add1); } @@ -184,9 +183,9 @@ public class NetworkTopologyStrategyTest final int RUNS = 10; StorageService.instance.setPartitionerUnsafe(Murmur3Partitioner.instance); Map datacenters = ImmutableMap.of("rf1", 1, "rf3", 3, "rf5_1", 5, "rf5_2", 5, "rf5_3", 5); - List nodes = new ArrayList<>(NODES); + List nodes = new ArrayList<>(NODES); for (byte i=0; i expected = calculateNaturalEndpoints(token, tokenMetadata, datacenters, snitch); - List actual = nts.calculateNaturalEndpoints(token, tokenMetadata); + List expected = calculateNaturalEndpoints(token, tokenMetadata, datacenters, snitch); + List actual = nts.calculateNaturalEndpoints(token, tokenMetadata); if (endpointsDiffer(expected, actual)) { System.err.println("Endpoints mismatch for token " + token); @@ -221,7 +220,7 @@ public class NetworkTopologyStrategyTest } } - private boolean endpointsDiffer(List ep1, List ep2) + private boolean endpointsDiffer(List ep1, List ep2) { // Because the old algorithm does not put the nodes in the correct order in the case where more replicas // are required than there are racks in a dc, we accept different order as long as the primary @@ -230,15 +229,15 @@ public class NetworkTopologyStrategyTest return false; if (!ep1.get(0).equals(ep2.get(0))) return true; - Set s1 = new HashSet<>(ep1); - Set s2 = new HashSet<>(ep2); + Set s1 = new HashSet<>(ep1); + Set s2 = new HashSet<>(ep2); return !s1.equals(s2); } - IEndpointSnitch generateSnitch(Map datacenters, Collection nodes, Random rand) + IEndpointSnitch generateSnitch(Map datacenters, Collection nodes, Random rand) { - final Map nodeToRack = new HashMap<>(); - final Map nodeToDC = new HashMap<>(); + final Map nodeToRack = new HashMap<>(); + final Map nodeToDC = new HashMap<>(); Map> racksPerDC = new HashMap<>(); datacenters.forEach((dc, rf) -> racksPerDC.put(dc, randomRacks(rf, rand))); int rf = datacenters.values().stream().mapToInt(x -> x).sum(); @@ -250,7 +249,7 @@ public class NetworkTopologyStrategyTest dcs[pos++] = dce.getKey(); } - for (InetAddress node : nodes) + for (InetAddressAndPort node : nodes) { String dc = dcs[rand.nextInt(rf)]; List racks = racksPerDC.get(dc); @@ -261,12 +260,12 @@ public class NetworkTopologyStrategyTest return new AbstractNetworkTopologySnitch() { - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return nodeToRack.get(endpoint); } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return nodeToDC.get(endpoint); } @@ -283,20 +282,20 @@ public class NetworkTopologyStrategyTest } // Copy of older endpoints calculation algorithm for comparison - public static List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata, Map datacenters, IEndpointSnitch snitch) + public static List calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata, Map datacenters, IEndpointSnitch snitch) { // we want to preserve insertion order so that the first added endpoint becomes primary - Set replicas = new LinkedHashSet<>(); + Set replicas = new LinkedHashSet<>(); // replicas we have found in each DC - Map> dcReplicas = new HashMap<>(datacenters.size()); + Map> dcReplicas = new HashMap<>(datacenters.size()); for (Map.Entry dc : datacenters.entrySet()) - dcReplicas.put(dc.getKey(), new HashSet(dc.getValue())); + dcReplicas.put(dc.getKey(), new HashSet(dc.getValue())); Topology topology = tokenMetadata.getTopology(); // all endpoints in each DC, so we can check when we have exhausted all the members of a DC - Multimap allEndpoints = topology.getDatacenterEndpoints(); + Multimap allEndpoints = topology.getDatacenterEndpoints(); // all racks in a DC so we can check when we have exhausted all racks in a DC - Map> racks = topology.getDatacenterRacks(); + Map> racks = topology.getDatacenterRacks(); assert !allEndpoints.isEmpty() && !racks.isEmpty() : "not aware of any cluster members"; // tracks the racks we have already placed replicas in @@ -306,15 +305,15 @@ public class NetworkTopologyStrategyTest // tracks the endpoints that we skipped over while looking for unique racks // when we relax the rack uniqueness we can append this to the current result so we don't have to wind back the iterator - Map> skippedDcEndpoints = new HashMap<>(datacenters.size()); + Map> skippedDcEndpoints = new HashMap<>(datacenters.size()); for (Map.Entry dc : datacenters.entrySet()) - skippedDcEndpoints.put(dc.getKey(), new LinkedHashSet()); + skippedDcEndpoints.put(dc.getKey(), new LinkedHashSet()); Iterator tokenIter = TokenMetadata.ringIterator(tokenMetadata.sortedTokens(), searchToken, false); while (tokenIter.hasNext() && !hasSufficientReplicas(dcReplicas, allEndpoints, datacenters)) { Token next = tokenIter.next(); - InetAddress ep = tokenMetadata.getEndpoint(next); + InetAddressAndPort ep = tokenMetadata.getEndpoint(next); String dc = snitch.getDatacenter(ep); // have we already found all replicas for this dc? if (!datacenters.containsKey(dc) || hasSufficientReplicas(dc, dcReplicas, allEndpoints, datacenters)) @@ -341,10 +340,10 @@ public class NetworkTopologyStrategyTest // if we've run out of distinct racks, add the hosts we skipped past already (up to RF) if (seenRacks.get(dc).size() == racks.get(dc).keySet().size()) { - Iterator skippedIt = skippedDcEndpoints.get(dc).iterator(); + Iterator skippedIt = skippedDcEndpoints.get(dc).iterator(); while (skippedIt.hasNext() && !hasSufficientReplicas(dc, dcReplicas, allEndpoints, datacenters)) { - InetAddress nextSkipped = skippedIt.next(); + InetAddressAndPort nextSkipped = skippedIt.next(); dcReplicas.get(dc).add(nextSkipped); replicas.add(nextSkipped); } @@ -353,15 +352,15 @@ public class NetworkTopologyStrategyTest } } - return new ArrayList(replicas); + return new ArrayList(replicas); } - private static boolean hasSufficientReplicas(String dc, Map> dcReplicas, Multimap allEndpoints, Map datacenters) + private static boolean hasSufficientReplicas(String dc, Map> dcReplicas, Multimap allEndpoints, Map datacenters) { return dcReplicas.get(dc).size() >= Math.min(allEndpoints.get(dc).size(), getReplicationFactor(dc, datacenters)); } - private static boolean hasSufficientReplicas(Map> dcReplicas, Multimap allEndpoints, Map datacenters) + private static boolean hasSufficientReplicas(Map> dcReplicas, Multimap allEndpoints, Map datacenters) { for (String dc : datacenters.keySet()) if (!hasSufficientReplicas(dc, dcReplicas, allEndpoints, datacenters)) diff --git a/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java b/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java index e6e17cdb4d..9c90d571f9 100644 --- a/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java +++ b/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; @@ -45,7 +44,7 @@ public class OldNetworkTopologyStrategyTest { private List keyTokens; private TokenMetadata tmd; - private Map> expectedResults; + private Map> expectedResults; @BeforeClass public static void setupDD() @@ -58,7 +57,7 @@ public class OldNetworkTopologyStrategyTest { keyTokens = new ArrayList(); tmd = new TokenMetadata(); - expectedResults = new HashMap>(); + expectedResults = new HashMap>(); } /** @@ -136,12 +135,12 @@ public class OldNetworkTopologyStrategyTest testGetEndpoints(strategy, keyTokens.toArray(new Token[0])); } - private ArrayList buildResult(String... addresses) throws UnknownHostException + private ArrayList buildResult(String... addresses) throws UnknownHostException { - ArrayList result = new ArrayList(); + ArrayList result = new ArrayList<>(); for (String address : addresses) { - result.add(InetAddress.getByName(address)); + result.add(InetAddressAndPort.getByName(address)); } return result; } @@ -153,7 +152,7 @@ public class OldNetworkTopologyStrategyTest BigIntegerToken keyToken = new BigIntegerToken(keyTokenID); keyTokens.add(keyToken); - InetAddress ep = InetAddress.getByName(endpointAddress); + InetAddressAndPort ep = InetAddressAndPort.getByName(endpointAddress); tmd.updateNormalToken(endpointToken, ep); } @@ -161,10 +160,10 @@ public class OldNetworkTopologyStrategyTest { for (Token keyToken : keyTokens) { - List endpoints = strategy.getNaturalEndpoints(keyToken); + List endpoints = strategy.getNaturalEndpoints(keyToken); for (int j = 0; j < endpoints.size(); j++) { - ArrayList hostsExpected = expectedResults.get(keyToken.toString()); + ArrayList hostsExpected = expectedResults.get(keyToken.toString()); assertEquals(endpoints.get(j), hostsExpected.get(j)); } } @@ -340,7 +339,7 @@ public class OldNetworkTopologyStrategyTest int lastIPPart = 1; for (BigIntegerToken token : tokens) - tokenMetadataCurrent.updateNormalToken(token, InetAddress.getByName("254.0.0." + Integer.toString(lastIPPart++))); + tokenMetadataCurrent.updateNormalToken(token, InetAddressAndPort.getByName("254.0.0." + Integer.toString(lastIPPart++))); return tokenMetadataCurrent; } @@ -360,7 +359,7 @@ public class OldNetworkTopologyStrategyTest { RackInferringSnitch endpointSnitch = new RackInferringSnitch(); - InetAddress movingNode = InetAddress.getByName("254.0.0." + Integer.toString(movingNodeIdx + 1)); + InetAddressAndPort movingNode = InetAddressAndPort.getByName("254.0.0." + Integer.toString(movingNodeIdx + 1)); TokenMetadata tokenMetadataCurrent = initTokenMetadata(tokens); diff --git a/test/unit/org/apache/cassandra/locator/PendingRangeMapsTest.java b/test/unit/org/apache/cassandra/locator/PendingRangeMapsTest.java index 7121550be9..56fd181fb2 100644 --- a/test/unit/org/apache/cassandra/locator/PendingRangeMapsTest.java +++ b/test/unit/org/apache/cassandra/locator/PendingRangeMapsTest.java @@ -25,7 +25,6 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.junit.Test; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; @@ -44,12 +43,12 @@ public class PendingRangeMapsTest { { PendingRangeMaps pendingRangeMaps = new PendingRangeMaps(); - pendingRangeMaps.addPendingRange(genRange("5", "15"), InetAddress.getByName("127.0.0.1")); - pendingRangeMaps.addPendingRange(genRange("15", "25"), InetAddress.getByName("127.0.0.2")); - pendingRangeMaps.addPendingRange(genRange("25", "35"), InetAddress.getByName("127.0.0.3")); - pendingRangeMaps.addPendingRange(genRange("35", "45"), InetAddress.getByName("127.0.0.4")); - pendingRangeMaps.addPendingRange(genRange("45", "55"), InetAddress.getByName("127.0.0.5")); - pendingRangeMaps.addPendingRange(genRange("45", "65"), InetAddress.getByName("127.0.0.6")); + pendingRangeMaps.addPendingRange(genRange("5", "15"), InetAddressAndPort.getByName("127.0.0.1")); + pendingRangeMaps.addPendingRange(genRange("15", "25"), InetAddressAndPort.getByName("127.0.0.2")); + pendingRangeMaps.addPendingRange(genRange("25", "35"), InetAddressAndPort.getByName("127.0.0.3")); + pendingRangeMaps.addPendingRange(genRange("35", "45"), InetAddressAndPort.getByName("127.0.0.4")); + pendingRangeMaps.addPendingRange(genRange("45", "55"), InetAddressAndPort.getByName("127.0.0.5")); + pendingRangeMaps.addPendingRange(genRange("45", "65"), InetAddressAndPort.getByName("127.0.0.6")); assertEquals(0, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("0")).size()); assertEquals(0, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("5")).size()); @@ -62,8 +61,8 @@ public class PendingRangeMapsTest { assertEquals(2, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("55")).size()); assertEquals(1, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("65")).size()); - Collection endpoints = pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("15")); - assertTrue(endpoints.contains(InetAddress.getByName("127.0.0.1"))); + Collection endpoints = pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("15")); + assertTrue(endpoints.contains(InetAddressAndPort.getByName("127.0.0.1"))); } @Test @@ -71,13 +70,13 @@ public class PendingRangeMapsTest { { PendingRangeMaps pendingRangeMaps = new PendingRangeMaps(); - pendingRangeMaps.addPendingRange(genRange("5", "15"), InetAddress.getByName("127.0.0.1")); - pendingRangeMaps.addPendingRange(genRange("15", "25"), InetAddress.getByName("127.0.0.2")); - pendingRangeMaps.addPendingRange(genRange("25", "35"), InetAddress.getByName("127.0.0.3")); - pendingRangeMaps.addPendingRange(genRange("35", "45"), InetAddress.getByName("127.0.0.4")); - pendingRangeMaps.addPendingRange(genRange("45", "55"), InetAddress.getByName("127.0.0.5")); - pendingRangeMaps.addPendingRange(genRange("45", "65"), InetAddress.getByName("127.0.0.6")); - pendingRangeMaps.addPendingRange(genRange("65", "7"), InetAddress.getByName("127.0.0.7")); + pendingRangeMaps.addPendingRange(genRange("5", "15"), InetAddressAndPort.getByName("127.0.0.1")); + pendingRangeMaps.addPendingRange(genRange("15", "25"), InetAddressAndPort.getByName("127.0.0.2")); + pendingRangeMaps.addPendingRange(genRange("25", "35"), InetAddressAndPort.getByName("127.0.0.3")); + pendingRangeMaps.addPendingRange(genRange("35", "45"), InetAddressAndPort.getByName("127.0.0.4")); + pendingRangeMaps.addPendingRange(genRange("45", "55"), InetAddressAndPort.getByName("127.0.0.5")); + pendingRangeMaps.addPendingRange(genRange("45", "65"), InetAddressAndPort.getByName("127.0.0.6")); + pendingRangeMaps.addPendingRange(genRange("65", "7"), InetAddressAndPort.getByName("127.0.0.7")); assertEquals(1, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("0")).size()); assertEquals(1, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("5")).size()); @@ -91,8 +90,8 @@ public class PendingRangeMapsTest { assertEquals(2, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("55")).size()); assertEquals(1, pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("65")).size()); - Collection endpoints = pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("6")); - assertTrue(endpoints.contains(InetAddress.getByName("127.0.0.1"))); - assertTrue(endpoints.contains(InetAddress.getByName("127.0.0.7"))); + Collection endpoints = pendingRangeMaps.pendingEndpointsFor(new BigIntegerToken("6")); + assertTrue(endpoints.contains(InetAddressAndPort.getByName("127.0.0.1"))); + assertTrue(endpoints.contains(InetAddressAndPort.getByName("127.0.0.7"))); } } diff --git a/test/unit/org/apache/cassandra/locator/PropertyFileSnitchTest.java b/test/unit/org/apache/cassandra/locator/PropertyFileSnitchTest.java index 522b118c42..df81a9b840 100644 --- a/test/unit/org/apache/cassandra/locator/PropertyFileSnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/PropertyFileSnitchTest.java @@ -18,7 +18,7 @@ package org.apache.cassandra.locator; import java.io.IOException; -import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -32,8 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; - -import com.google.common.net.InetAddresses; +import java.util.regex.Matcher; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.IPartitioner; @@ -61,7 +60,7 @@ public class PropertyFileSnitchTest private Path backupFile; private VersionedValue.VersionedValueFactory valueFactory; - private Map> tokenMap; + private Map> tokenMap; @BeforeClass public static void setupDD() @@ -78,17 +77,17 @@ public class PropertyFileSnitchTest restoreOrigConfigFile(); - InetAddress[] hosts = { - InetAddress.getByName("127.0.0.1"), // this exists in the config file - InetAddress.getByName("127.0.0.2"), // this exists in the config file - InetAddress.getByName("127.0.0.9"), // this does not exist in the config file + InetAddressAndPort[] hosts = { + InetAddressAndPort.getByName("127.0.0.1"), // this exists in the config file + InetAddressAndPort.getByName("127.0.0.2"), // this exists in the config file + InetAddressAndPort.getByName("127.0.0.9"), // this does not exist in the config file }; IPartitioner partitioner = new RandomPartitioner(); valueFactory = new VersionedValue.VersionedValueFactory(partitioner); tokenMap = new HashMap<>(); - for (InetAddress host : hosts) + for (InetAddressAndPort host : hosts) { Set tokens = Collections.singleton(partitioner.getRandomToken()); Gossiper.instance.initializeNodeUnsafe(host, UUID.randomUUID(), 1); @@ -117,13 +116,21 @@ public class PropertyFileSnitchTest for (String line : lines) { String[] info = line.split("="); - if (info.length == 2 && replacements.containsKey(info[0])) + if (info.length == 2 && !line.startsWith("#") && !line.startsWith("default=")) { - String replacement = replacements.get(info[0]); - if (!replacement.isEmpty()) // empty means remove this line - newLines.add(info[0] + '=' + replacement); + InetAddressAndPort address = InetAddressAndPort.getByName(info[0].replaceAll(Matcher.quoteReplacement("\\:"), ":")); + String replacement = replacements.get(address.toString()); + if (replacement != null) + { + if (!replacement.isEmpty()) // empty means remove this line + newLines.add(info[0] + '=' + replacement); - replaced.add(info[0]); + replaced.add(address.toString()); + } + else + { + newLines.add(line); + } } else { @@ -138,21 +145,26 @@ public class PropertyFileSnitchTest continue; if (!replacement.getValue().isEmpty()) // empty means remove this line so do nothing here - newLines.add(replacement.getKey() + '=' + replacement.getValue()); + { + String escaped = replacement.getKey().replaceAll(Matcher.quoteReplacement(":"), "\\\\:"); + newLines.add(escaped + '=' + replacement.getValue()); + } } Files.write(effectiveFile, newLines, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING); } - private void setNodeShutdown(InetAddress host) + private void setNodeShutdown(InetAddressAndPort host) { StorageService.instance.getTokenMetadata().removeEndpoint(host); + Gossiper.instance.injectApplicationState(host, ApplicationState.STATUS_WITH_PORT, valueFactory.shutdown(true)); Gossiper.instance.injectApplicationState(host, ApplicationState.STATUS, valueFactory.shutdown(true)); Gossiper.instance.markDead(host, Gossiper.instance.getEndpointStateForEndpoint(host)); } - private void setNodeLive(InetAddress host) + private void setNodeLive(InetAddressAndPort host) { + Gossiper.instance.injectApplicationState(host, ApplicationState.STATUS_WITH_PORT, valueFactory.normal(tokenMap.get(host))); Gossiper.instance.injectApplicationState(host, ApplicationState.STATUS, valueFactory.normal(tokenMap.get(host))); Gossiper.instance.realMarkAlive(host, Gossiper.instance.getEndpointStateForEndpoint(host)); StorageService.instance.getTokenMetadata().updateNormalTokens(tokenMap.get(host), host); @@ -160,9 +172,9 @@ public class PropertyFileSnitchTest private static void checkEndpoint(final AbstractNetworkTopologySnitch snitch, final String endpointString, final String expectedDatacenter, - final String expectedRack) + final String expectedRack) throws UnknownHostException { - final InetAddress endpoint = InetAddresses.forString(endpointString); + final InetAddressAndPort endpoint = InetAddressAndPort.getByName(endpointString); assertEquals(expectedDatacenter, snitch.getDatacenter(endpoint)); assertEquals(expectedRack, snitch.getRack(endpoint)); } @@ -174,25 +186,25 @@ public class PropertyFileSnitchTest @Test public void testChangeHostRack() throws Exception { - final InetAddress host = InetAddress.getByName("127.0.0.1"); + final InetAddressAndPort host = InetAddressAndPort.getByName("127.0.0.1"); final PropertyFileSnitch snitch = new PropertyFileSnitch(/*refreshPeriodInSeconds*/1); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC1"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC1"); try { setNodeLive(host); Files.copy(effectiveFile, backupFile); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC1:RAC2")); + replaceConfigFile(Collections.singletonMap(host.toString(), "DC1:RAC2")); Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC1"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC1"); setNodeShutdown(host); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC1:RAC2")); + replaceConfigFile(Collections.singletonMap(host.toString(), "DC1:RAC2")); Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC2"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC2"); } finally { @@ -208,25 +220,25 @@ public class PropertyFileSnitchTest @Test public void testChangeHostDc() throws Exception { - final InetAddress host = InetAddress.getByName("127.0.0.1"); + final InetAddressAndPort host = InetAddressAndPort.getByName("127.0.0.1"); final PropertyFileSnitch snitch = new PropertyFileSnitch(/*refreshPeriodInSeconds*/1); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC1"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC1"); try { setNodeLive(host); Files.copy(effectiveFile, backupFile); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC2:RAC1")); + replaceConfigFile(Collections.singletonMap(host.toString(), "DC2:RAC1")); Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC1"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC1"); setNodeShutdown(host); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC2:RAC1")); + replaceConfigFile(Collections.singletonMap(host.toString(), "DC2:RAC1")); Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC2", "RAC1"); + checkEndpoint(snitch, host.toString(), "DC2", "RAC1"); } finally { @@ -243,25 +255,25 @@ public class PropertyFileSnitchTest @Test public void testAddHost() throws Exception { - final InetAddress host = InetAddress.getByName("127.0.0.9"); + final InetAddressAndPort host = InetAddressAndPort.getByName("127.0.0.9"); final PropertyFileSnitch snitch = new PropertyFileSnitch(/*refreshPeriodInSeconds*/1); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "r1"); // default + checkEndpoint(snitch, host.toString(), "DC1", "r1"); // default try { setNodeLive(host); Files.copy(effectiveFile, backupFile); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC2:RAC2")); // add this line if not yet there + replaceConfigFile(Collections.singletonMap(host.toString(), "DC2:RAC2")); // add this line if not yet there Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "r1"); // unchanged + checkEndpoint(snitch, host.toString(), "DC1", "r1"); // unchanged setNodeShutdown(host); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "DC2:RAC2")); // add this line if not yet there + replaceConfigFile(Collections.singletonMap(host.toString(), "DC2:RAC2")); // add this line if not yet there Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC2", "RAC2"); // changed + checkEndpoint(snitch, host.toString(), "DC2", "RAC2"); // changed } finally { @@ -278,25 +290,25 @@ public class PropertyFileSnitchTest @Test public void testRemoveHost() throws Exception { - final InetAddress host = InetAddress.getByName("127.0.0.2"); + final InetAddressAndPort host = InetAddressAndPort.getByName("127.0.0.2"); final PropertyFileSnitch snitch = new PropertyFileSnitch(/*refreshPeriodInSeconds*/1); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC2"); + checkEndpoint(snitch, host.toString(), "DC1", "RAC2"); try { setNodeLive(host); Files.copy(effectiveFile, backupFile); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "")); // removes line if found + replaceConfigFile(Collections.singletonMap(host.toString(), "")); // removes line if found Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "RAC2"); // unchanged + checkEndpoint(snitch, host.toString(), "DC1", "RAC2"); // unchanged setNodeShutdown(host); - replaceConfigFile(Collections.singletonMap(host.getHostAddress(), "")); // removes line if found + replaceConfigFile(Collections.singletonMap(host.toString(), "")); // removes line if found Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "r1"); // default + checkEndpoint(snitch, host.toString(), "DC1", "r1"); // default } finally { @@ -313,9 +325,9 @@ public class PropertyFileSnitchTest @Test public void testChangeDefault() throws Exception { - final InetAddress host = InetAddress.getByName("127.0.0.9"); + final InetAddressAndPort host = InetAddressAndPort.getByName("127.0.0.9"); final PropertyFileSnitch snitch = new PropertyFileSnitch(/*refreshPeriodInSeconds*/1); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "r1"); // default + checkEndpoint(snitch, host.toString(), "DC1", "r1"); // default try { @@ -325,13 +337,13 @@ public class PropertyFileSnitchTest replaceConfigFile(Collections.singletonMap("default", "DC2:r2")); // change default Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC1", "r1"); // unchanged + checkEndpoint(snitch, host.toString(), "DC1", "r1"); // unchanged setNodeShutdown(host); replaceConfigFile(Collections.singletonMap("default", "DC2:r2")); // change default again (refresh file update) Thread.sleep(1500); - checkEndpoint(snitch, host.getHostAddress(), "DC2", "r2"); // default updated + checkEndpoint(snitch, host.toString(), "DC2", "r2"); // default updated } finally { diff --git a/test/unit/org/apache/cassandra/locator/ReconnectableSnitchHelperTest.java b/test/unit/org/apache/cassandra/locator/ReconnectableSnitchHelperTest.java index 232865a819..b1c3775794 100644 --- a/test/unit/org/apache/cassandra/locator/ReconnectableSnitchHelperTest.java +++ b/test/unit/org/apache/cassandra/locator/ReconnectableSnitchHelperTest.java @@ -50,7 +50,7 @@ public class ReconnectableSnitchHelperTest public void failedAuthentication() throws Exception { DatabaseDescriptor.setInternodeAuthenticator(MessagingServiceTest.ALLOW_NOTHING_AUTHENTICATOR); - InetAddress address = InetAddress.getByName("127.0.0.250"); + InetAddressAndPort address = InetAddressAndPort.getByName("127.0.0.250"); //Should tolerate null returns by MS for the connection ReconnectableSnitchHelper.reconnect(address, address, null, null); } diff --git a/test/unit/org/apache/cassandra/locator/ReplicationStrategyEndpointCacheTest.java b/test/unit/org/apache/cassandra/locator/ReplicationStrategyEndpointCacheTest.java index b83194b237..a8caa721f4 100644 --- a/test/unit/org/apache/cassandra/locator/ReplicationStrategyEndpointCacheTest.java +++ b/test/unit/org/apache/cassandra/locator/ReplicationStrategyEndpointCacheTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -55,14 +54,14 @@ public class ReplicationStrategyEndpointCacheTest strategy = getStrategyWithNewTokenMetadata(Keyspace.open(KEYSPACE).getReplicationStrategy(), tmd); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(10)), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(20)), InetAddress.getByName("127.0.0.2")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(30)), InetAddress.getByName("127.0.0.3")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(40)), InetAddress.getByName("127.0.0.4")); - //tmd.updateNormalToken(new BigIntegerToken(String.valueOf(50)), InetAddress.getByName("127.0.0.5")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(60)), InetAddress.getByName("127.0.0.6")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(70)), InetAddress.getByName("127.0.0.7")); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(80)), InetAddress.getByName("127.0.0.8")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(10)), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(20)), InetAddressAndPort.getByName("127.0.0.2")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(30)), InetAddressAndPort.getByName("127.0.0.3")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(40)), InetAddressAndPort.getByName("127.0.0.4")); + //tmd.updateNormalToken(new BigIntegerToken(String.valueOf(50)), InetAddressAndPort.getByName("127.0.0.5", null, null)); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(60)), InetAddressAndPort.getByName("127.0.0.6")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(70)), InetAddressAndPort.getByName("127.0.0.7")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(80)), InetAddressAndPort.getByName("127.0.0.8")); } @Test @@ -90,31 +89,31 @@ public class ReplicationStrategyEndpointCacheTest public void runCacheRespectsTokenChangesTest(Class stratClass, Map configOptions) throws Exception { setup(stratClass, configOptions); - ArrayList initial; - ArrayList endpoints; + ArrayList initial; + ArrayList endpoints; endpoints = strategy.getNaturalEndpoints(searchToken); assert endpoints.size() == 5 : StringUtils.join(endpoints, ","); // test token addition, in DC2 before existing token initial = strategy.getNaturalEndpoints(searchToken); - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(35)), InetAddress.getByName("127.0.0.5")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(35)), InetAddressAndPort.getByName("127.0.0.5")); endpoints = strategy.getNaturalEndpoints(searchToken); assert endpoints.size() == 5 : StringUtils.join(endpoints, ","); assert !endpoints.equals(initial); // test token removal, newly created token initial = strategy.getNaturalEndpoints(searchToken); - tmd.removeEndpoint(InetAddress.getByName("127.0.0.5")); + tmd.removeEndpoint(InetAddressAndPort.getByName("127.0.0.5")); endpoints = strategy.getNaturalEndpoints(searchToken); assert endpoints.size() == 5 : StringUtils.join(endpoints, ","); - assert !endpoints.contains(InetAddress.getByName("127.0.0.5")); + assert !endpoints.contains(InetAddressAndPort.getByName("127.0.0.5")); assert !endpoints.equals(initial); // test token change initial = strategy.getNaturalEndpoints(searchToken); //move .8 after search token but before other DC3 - tmd.updateNormalToken(new BigIntegerToken(String.valueOf(25)), InetAddress.getByName("127.0.0.8")); + tmd.updateNormalToken(new BigIntegerToken(String.valueOf(25)), InetAddressAndPort.getByName("127.0.0.8")); endpoints = strategy.getNaturalEndpoints(searchToken); assert endpoints.size() == 5 : StringUtils.join(endpoints, ","); assert !endpoints.equals(initial); @@ -129,7 +128,7 @@ public class ReplicationStrategyEndpointCacheTest super(keyspaceName, tokenMetadata, snitch, configOptions); } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { assert !called : "calculateNaturalEndpoints was already called, result should have been cached"; called = true; @@ -146,7 +145,7 @@ public class ReplicationStrategyEndpointCacheTest super(keyspaceName, tokenMetadata, snitch, configOptions); } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { assert !called : "calculateNaturalEndpoints was already called, result should have been cached"; called = true; @@ -163,7 +162,7 @@ public class ReplicationStrategyEndpointCacheTest super(keyspaceName, tokenMetadata, snitch, configOptions); } - public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) + public List calculateNaturalEndpoints(Token token, TokenMetadata metadata) { assert !called : "calculateNaturalEndpoints was already called, result should have been cached"; called = true; diff --git a/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java b/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java index f97a6e5197..fe77b0e2a7 100644 --- a/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java +++ b/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -98,22 +97,22 @@ public class SimpleStrategyTest { tmd = new TokenMetadata(); strategy = getStrategy(keyspaceName, tmd); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); for (int i = 0; i < endpointTokens.length; i++) { - InetAddress ep = InetAddress.getByName("127.0.0." + String.valueOf(i + 1)); + InetAddressAndPort ep = InetAddressAndPort.getByName("127.0.0." + String.valueOf(i + 1)); tmd.updateNormalToken(endpointTokens[i], ep); hosts.add(ep); } for (int i = 0; i < keyTokens.length; i++) { - List endpoints = strategy.getNaturalEndpoints(keyTokens[i]); + List endpoints = strategy.getNaturalEndpoints(keyTokens[i]); assertEquals(strategy.getReplicationFactor(), endpoints.size()); - List correctEndpoints = new ArrayList(); + List correctEndpoints = new ArrayList<>(); for (int j = 0; j < endpoints.size(); j++) correctEndpoints.add(hosts.get((i + j + 1) % hosts.size())); - assertEquals(new HashSet(correctEndpoints), new HashSet(endpoints)); + assertEquals(new HashSet<>(correctEndpoints), new HashSet<>(endpoints)); } } } @@ -135,17 +134,17 @@ public class SimpleStrategyTest keyTokens[i] = new BigIntegerToken(String.valueOf(RING_SIZE * 2 * i + RING_SIZE)); } - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); for (int i = 0; i < endpointTokens.length; i++) { - InetAddress ep = InetAddress.getByName("127.0.0." + String.valueOf(i + 1)); + InetAddressAndPort ep = InetAddressAndPort.getByName("127.0.0." + String.valueOf(i + 1)); tmd.updateNormalToken(endpointTokens[i], ep); hosts.add(ep); } // bootstrap at the end of the ring Token bsToken = new BigIntegerToken(String.valueOf(210)); - InetAddress bootstrapEndpoint = InetAddress.getByName("127.0.0.11"); + InetAddressAndPort bootstrapEndpoint = InetAddressAndPort.getByName("127.0.0.11"); tmd.addBootstrapToken(bsToken, bootstrapEndpoint); AbstractReplicationStrategy strategy = null; @@ -159,7 +158,7 @@ public class SimpleStrategyTest for (int i = 0; i < keyTokens.length; i++) { - Collection endpoints = tmd.getWriteEndpoints(keyTokens[i], keyspaceName, strategy.getNaturalEndpoints(keyTokens[i])); + Collection endpoints = tmd.getWriteEndpoints(keyTokens[i], keyspaceName, strategy.getNaturalEndpoints(keyTokens[i])); assertTrue(endpoints.size() >= replicationFactor); for (int j = 0; j < replicationFactor; j++) diff --git a/test/unit/org/apache/cassandra/locator/TokenMetadataTest.java b/test/unit/org/apache/cassandra/locator/TokenMetadataTest.java index e5a86fdc2b..b589d2d595 100644 --- a/test/unit/org/apache/cassandra/locator/TokenMetadataTest.java +++ b/test/unit/org/apache/cassandra/locator/TokenMetadataTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.locator; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Map; @@ -56,8 +55,8 @@ public class TokenMetadataTest { DatabaseDescriptor.daemonInitialization(); tmd = StorageService.instance.getTokenMetadata(); - tmd.updateNormalToken(token(ONE), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(token(SIX), InetAddress.getByName("127.0.0.6")); + tmd.updateNormalToken(token(ONE), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(token(SIX), InetAddressAndPort.getByName("127.0.0.6")); } private static void testRingIterator(ArrayList ring, String start, boolean includeMin, String... expected) @@ -98,8 +97,8 @@ public class TokenMetadataTest @Test public void testTopologyUpdate_RackConsolidation() throws UnknownHostException { - final InetAddress first = InetAddress.getByName("127.0.0.1"); - final InetAddress second = InetAddress.getByName("127.0.0.6"); + final InetAddressAndPort first = InetAddressAndPort.getByName("127.0.0.1"); + final InetAddressAndPort second = InetAddressAndPort.getByName("127.0.0.6"); final String DATA_CENTER = "datacenter1"; final String RACK1 = "rack1"; final String RACK2 = "rack2"; @@ -107,19 +106,19 @@ public class TokenMetadataTest DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return endpoint.equals(first) ? RACK1 : RACK2; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return DATA_CENTER; } @Override - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -134,14 +133,14 @@ public class TokenMetadataTest TokenMetadata.Topology topology = tokenMetadata.getTopology(); assertNotNull(topology); - Multimap allEndpoints = topology.getDatacenterEndpoints(); + Multimap allEndpoints = topology.getDatacenterEndpoints(); assertNotNull(allEndpoints); assertTrue(allEndpoints.size() == 2); assertTrue(allEndpoints.containsKey(DATA_CENTER)); assertTrue(allEndpoints.get(DATA_CENTER).contains(first)); assertTrue(allEndpoints.get(DATA_CENTER).contains(second)); - Map> racks = topology.getDatacenterRacks(); + Map> racks = topology.getDatacenterRacks(); assertNotNull(racks); assertTrue(racks.size() == 1); assertTrue(racks.containsKey(DATA_CENTER)); @@ -154,19 +153,19 @@ public class TokenMetadataTest DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return RACK1; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return DATA_CENTER; } @Override - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -196,8 +195,8 @@ public class TokenMetadataTest @Test public void testTopologyUpdate_RackExpansion() throws UnknownHostException { - final InetAddress first = InetAddress.getByName("127.0.0.1"); - final InetAddress second = InetAddress.getByName("127.0.0.6"); + final InetAddressAndPort first = InetAddressAndPort.getByName("127.0.0.1"); + final InetAddressAndPort second = InetAddressAndPort.getByName("127.0.0.6"); final String DATA_CENTER = "datacenter1"; final String RACK1 = "rack1"; final String RACK2 = "rack2"; @@ -205,19 +204,19 @@ public class TokenMetadataTest DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return RACK1; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return DATA_CENTER; } @Override - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -232,14 +231,14 @@ public class TokenMetadataTest TokenMetadata.Topology topology = tokenMetadata.getTopology(); assertNotNull(topology); - Multimap allEndpoints = topology.getDatacenterEndpoints(); + Multimap allEndpoints = topology.getDatacenterEndpoints(); assertNotNull(allEndpoints); assertTrue(allEndpoints.size() == 2); assertTrue(allEndpoints.containsKey(DATA_CENTER)); assertTrue(allEndpoints.get(DATA_CENTER).contains(first)); assertTrue(allEndpoints.get(DATA_CENTER).contains(second)); - Map> racks = topology.getDatacenterRacks(); + Map> racks = topology.getDatacenterRacks(); assertNotNull(racks); assertTrue(racks.size() == 1); assertTrue(racks.containsKey(DATA_CENTER)); @@ -252,19 +251,19 @@ public class TokenMetadataTest DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch() { @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return endpoint.equals(first) ? RACK1 : RACK2; } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return DATA_CENTER; } @Override - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -293,8 +292,8 @@ public class TokenMetadataTest @Test public void testEndpointSizes() throws UnknownHostException { - final InetAddress first = InetAddress.getByName("127.0.0.1"); - final InetAddress second = InetAddress.getByName("127.0.0.6"); + final InetAddressAndPort first = InetAddressAndPort.getByName("127.0.0.1"); + final InetAddressAndPort second = InetAddressAndPort.getByName("127.0.0.6"); tmd.updateNormalToken(token(ONE), first); tmd.updateNormalToken(token(SIX), second); diff --git a/test/unit/org/apache/cassandra/metrics/HintedHandOffMetricsTest.java b/test/unit/org/apache/cassandra/metrics/HintedHandOffMetricsTest.java index 2394e0c7ff..2b13715298 100644 --- a/test/unit/org/apache/cassandra/metrics/HintedHandOffMetricsTest.java +++ b/test/unit/org/apache/cassandra/metrics/HintedHandOffMetricsTest.java @@ -20,7 +20,6 @@ */ package org.apache.cassandra.metrics; -import java.net.InetAddress; import java.util.Map; import java.util.UUID; @@ -35,6 +34,7 @@ import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.UUIDType; import org.apache.cassandra.hints.HintsService; +import org.apache.cassandra.locator.InetAddressAndPort; import static org.junit.Assert.assertEquals; import static org.apache.cassandra.cql3.QueryProcessor.executeInternal; @@ -53,11 +53,15 @@ public class HintedHandOffMetricsTest DatabaseDescriptor.getHintsDirectory().mkdirs(); for (int i = 0; i < 99; i++) - HintsService.instance.metrics.incrPastWindow(InetAddress.getByName("127.0.0.1")); + HintsService.instance.metrics.incrPastWindow(InetAddressAndPort.getLocalHost()); HintsService.instance.metrics.log(); - UntypedResultSet rows = executeInternal("SELECT hints_dropped FROM system." + SystemKeyspace.PEER_EVENTS); + UntypedResultSet rows = executeInternal("SELECT hints_dropped FROM system." + SystemKeyspace.PEER_EVENTS_V2); Map returned = rows.one().getMap("hints_dropped", UUIDType.instance, Int32Type.instance); assertEquals(Iterators.getLast(returned.values().iterator()).intValue(), 99); + + rows = executeInternal("SELECT hints_dropped FROM system." + SystemKeyspace.LEGACY_PEER_EVENTS); + returned = rows.one().getMap("hints_dropped", UUIDType.instance, Int32Type.instance); + assertEquals(Iterators.getLast(returned.values().iterator()).intValue(), 99); } } diff --git a/test/unit/org/apache/cassandra/net/CompactEndpointSerializationHelperTest.java b/test/unit/org/apache/cassandra/net/CompactEndpointSerializationHelperTest.java new file mode 100644 index 0000000000..32324556c1 --- /dev/null +++ b/test/unit/org/apache/cassandra/net/CompactEndpointSerializationHelperTest.java @@ -0,0 +1,72 @@ +/* + * 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.cassandra.net; + +import java.nio.ByteBuffer; + +import org.junit.Test; + +import org.apache.cassandra.io.util.DataInputBuffer; +import org.apache.cassandra.io.util.DataOutputBuffer; +import org.apache.cassandra.locator.InetAddressAndPort; + +import static org.junit.Assert.assertEquals; + +public class CompactEndpointSerializationHelperTest +{ + + @Test + public void testRoundtrip() throws Exception + { + InetAddressAndPort ipv4 = InetAddressAndPort.getByName("127.0.0.1:42"); + InetAddressAndPort ipv6 = InetAddressAndPort.getByName("[2001:db8:0:0:0:ff00:42:8329]:42"); + + testAddress(ipv4, MessagingService.VERSION_30); + testAddress(ipv6, MessagingService.VERSION_30); + testAddress(ipv4, MessagingService.current_version); + testAddress(ipv6, MessagingService.current_version); + } + + private void testAddress(InetAddressAndPort address, int version) throws Exception + { + ByteBuffer out; + try (DataOutputBuffer dob = new DataOutputBuffer()) + { + CompactEndpointSerializationHelper.instance.serialize(address, dob, version); + out = dob.buffer(); + } + assertEquals(out.remaining(), CompactEndpointSerializationHelper.instance.serializedSize(address, version)); + + InetAddressAndPort roundtripped; + try (DataInputBuffer dib = new DataInputBuffer(out, false)) + { + roundtripped = CompactEndpointSerializationHelper.instance.deserialize(dib, version); + } + + if (version >= MessagingService.VERSION_40) + { + assertEquals(address, roundtripped); + } + else + { + assertEquals(roundtripped.address, address.address); + assertEquals(7000, roundtripped.port); + } + } +} diff --git a/test/unit/org/apache/cassandra/net/ForwardToContainerTest.java b/test/unit/org/apache/cassandra/net/ForwardToContainerTest.java new file mode 100644 index 0000000000..195d734c1b --- /dev/null +++ b/test/unit/org/apache/cassandra/net/ForwardToContainerTest.java @@ -0,0 +1,100 @@ +/* + * 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.cassandra.net; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import org.junit.Test; + +import org.apache.cassandra.io.util.DataInputBuffer; +import org.apache.cassandra.io.util.DataOutputBuffer; +import org.apache.cassandra.locator.InetAddressAndPort; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ForwardToContainerTest +{ + @Test + public void testCurrent() throws Exception + { + testVersion(MessagingService.current_version); + } + + @Test + public void test30() throws Exception + { + testVersion(MessagingService.VERSION_30); + } + + private void testVersion(int version) throws Exception + { + InetAddressAndPort.initializeDefaultPort(65532); + List addresses = ImmutableList.of(InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", 42), + InetAddressAndPort.getByName("127.0.0.1"), + InetAddressAndPort.getByName("127.0.0.1:7000"), + InetAddressAndPort.getByNameOverrideDefaults("2001:0db8:0000:0000:0000:ff00:0042:8329", 42), + InetAddressAndPort.getByName("2001:0db8:0000:0000:0000:ff00:0042:8329"), + InetAddressAndPort.getByName("[2001:0db8:0000:0000:0000:ff00:0042:8329]:7000")); + + ForwardToContainer ftc = new ForwardToContainer(addresses, new int[] { 44, 45, 46, 47, 48, 49 }); + ByteBuffer buffer; + try (DataOutputBuffer dob = new DataOutputBuffer()) + { + ForwardToSerializer.instance.serialize(ftc, dob, version); + buffer = dob.buffer(); + } + + assertEquals(buffer.remaining(), ForwardToSerializer.instance.serializedSize(ftc, version)); + + ForwardToContainer deserialized; + try (DataInputBuffer dib = new DataInputBuffer(buffer, false)) + { + deserialized = ForwardToSerializer.instance.deserialize(dib, version); + } + + assertTrue(Arrays.equals(ftc.messageIds, deserialized.messageIds)); + + Iterator iterator = deserialized.targets.iterator(); + if (version >= MessagingService.VERSION_40) + { + for (int ii = 0; ii < addresses.size(); ii++) + { + InetAddressAndPort original = addresses.get(ii); + InetAddressAndPort roundtripped = iterator.next(); + assertEquals(original, roundtripped); + } + } + else + { + for (int ii = 0; ii < addresses.size(); ii++) + { + InetAddressAndPort original = addresses.get(ii); + InetAddressAndPort roundtripped = iterator.next(); + assertEquals(original.address, roundtripped.address); + //3.0 can't send port numbers so you get the defaults + assertEquals(65532, roundtripped.port); + } + } + } +} diff --git a/test/unit/org/apache/cassandra/net/Matcher.java b/test/unit/org/apache/cassandra/net/Matcher.java index cd1b667d9d..27b685f6bb 100644 --- a/test/unit/org/apache/cassandra/net/Matcher.java +++ b/test/unit/org/apache/cassandra/net/Matcher.java @@ -17,7 +17,7 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; +import org.apache.cassandra.locator.InetAddressAndPort; /** * Predicate based on intercepted, outgoing messange and the message's destination address. @@ -28,5 +28,5 @@ public interface Matcher * @param obj intercepted outgoing message * @param to destination address */ - public boolean matches(MessageOut obj, InetAddress to); + public boolean matches(MessageOut obj, InetAddressAndPort to); } diff --git a/test/unit/org/apache/cassandra/net/MatcherResponse.java b/test/unit/org/apache/cassandra/net/MatcherResponse.java index 6cd8085bd9..7a1772aec5 100644 --- a/test/unit/org/apache/cassandra/net/MatcherResponse.java +++ b/test/unit/org/apache/cassandra/net/MatcherResponse.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.util.Collections; import java.util.HashSet; import java.util.Queue; @@ -27,6 +26,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Function; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * Sends a response for an incoming message with a matching {@link Matcher}. * The actual behavior by any instance of this class can be inspected by @@ -76,7 +77,7 @@ public class MatcherResponse * Respond with the message created by the provided function that will be called with each intercepted outbound message. * @param fnResponse function to call for creating reply based on intercepted message and target address */ - public MockMessagingSpy respond(BiFunction, InetAddress, MessageIn> fnResponse) + public MockMessagingSpy respond(BiFunction, InetAddressAndPort, MessageIn> fnResponse) { return respondN(fnResponse, Integer.MAX_VALUE); } @@ -101,7 +102,7 @@ public class MatcherResponse */ public MockMessagingSpy respondNWithPayloadForEachReceiver(Function, S> fnResponse, MessagingService.Verb verb, int limit) { - return respondN((MessageOut msg, InetAddress to) -> { + return respondN((MessageOut msg, InetAddressAndPort to) -> { S payload = fnResponse.apply(msg); if (payload == null) return null; @@ -147,7 +148,7 @@ public class MatcherResponse * each intercepted outbound message. * @param fnResponse function to call for creating reply based on intercepted message and target address */ - public MockMessagingSpy respondN(BiFunction, InetAddress, MessageIn> fnResponse, int limit) + public MockMessagingSpy respondN(BiFunction, InetAddressAndPort, MessageIn> fnResponse, int limit) { limitCounter.set(limit); @@ -155,7 +156,7 @@ public class MatcherResponse sink = new IMessageSink() { - public boolean allowOutgoingMessage(MessageOut message, int id, InetAddress to) + public boolean allowOutgoingMessage(MessageOut message, int id, InetAddressAndPort to) { // prevent outgoing message from being send in case matcher indicates a match // and instead send the mocked response diff --git a/test/unit/org/apache/cassandra/net/MessagingServiceTest.java b/test/unit/org/apache/cassandra/net/MessagingServiceTest.java index f0a959e044..4ce342205b 100644 --- a/test/unit/org/apache/cassandra/net/MessagingServiceTest.java +++ b/test/unit/org/apache/cassandra/net/MessagingServiceTest.java @@ -48,6 +48,10 @@ import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.monitoring.ApproximateTime; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus; +import org.apache.cassandra.io.util.DataOutputStreamPlus; +import org.apache.cassandra.io.util.WrappedDataOutputStreamPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService.ServerChannel; import org.apache.cassandra.net.async.NettyFactory; import org.apache.cassandra.net.async.OutboundConnectionIdentifier; @@ -81,7 +85,7 @@ public class MessagingServiceTest }; private static IInternodeAuthenticator originalAuthenticator; private static ServerEncryptionOptions originalServerEncryptionOptions; - private static InetAddress originalListenAddress; + private static InetAddressAndPort originalListenAddress; private final MessagingService messagingService = MessagingService.test(); @@ -93,7 +97,7 @@ public class MessagingServiceTest DatabaseDescriptor.setBroadcastAddress(InetAddress.getByName("127.0.0.1")); originalAuthenticator = DatabaseDescriptor.getInternodeAuthenticator(); originalServerEncryptionOptions = DatabaseDescriptor.getServerEncryptionOptions(); - originalListenAddress = DatabaseDescriptor.getListenAddress(); + originalListenAddress = InetAddressAndPort.getByAddressOverrideDefaults(DatabaseDescriptor.getListenAddress(), DatabaseDescriptor.getStoragePort()); } private static int metricScopeId = 0; @@ -103,8 +107,8 @@ public class MessagingServiceTest { messagingService.resetDroppedMessagesMap(Integer.toString(metricScopeId++)); MockBackPressureStrategy.applied = false; - messagingService.destroyConnectionPool(InetAddress.getByName("127.0.0.2")); - messagingService.destroyConnectionPool(InetAddress.getByName("127.0.0.3")); + messagingService.destroyConnectionPool(InetAddressAndPort.getByName("127.0.0.2")); + messagingService.destroyConnectionPool(InetAddressAndPort.getByName("127.0.0.3")); } @After @@ -113,7 +117,7 @@ public class MessagingServiceTest DatabaseDescriptor.setInternodeAuthenticator(originalAuthenticator); DatabaseDescriptor.setServerEncryptionOptions(originalServerEncryptionOptions); DatabaseDescriptor.setShouldListenOnBroadcastAddress(false); - DatabaseDescriptor.setListenAddress(originalListenAddress); + DatabaseDescriptor.setListenAddress(originalListenAddress.address); FBUtilities.reset(); } @@ -221,44 +225,44 @@ public class MessagingServiceTest @Test public void testUpdatesBackPressureOnSendWhenEnabledAndWithSupportedCallback() throws UnknownHostException { - MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddress.getByName("127.0.0.2")); + MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddressAndPort.getByName("127.0.0.2")); IAsyncCallback bpCallback = new BackPressureCallback(); IAsyncCallback noCallback = new NoBackPressureCallback(); MessageOut ignored = null; DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnSend(InetAddress.getByName("127.0.0.2"), noCallback, ignored); + messagingService.updateBackPressureOnSend(InetAddressAndPort.getByName("127.0.0.2"), noCallback, ignored); assertFalse(backPressureState.onSend); DatabaseDescriptor.setBackPressureEnabled(false); - messagingService.updateBackPressureOnSend(InetAddress.getByName("127.0.0.2"), bpCallback, ignored); + messagingService.updateBackPressureOnSend(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, ignored); assertFalse(backPressureState.onSend); DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnSend(InetAddress.getByName("127.0.0.2"), bpCallback, ignored); + messagingService.updateBackPressureOnSend(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, ignored); assertTrue(backPressureState.onSend); } @Test public void testUpdatesBackPressureOnReceiveWhenEnabledAndWithSupportedCallback() throws UnknownHostException { - MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddress.getByName("127.0.0.2")); + MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddressAndPort.getByName("127.0.0.2")); IAsyncCallback bpCallback = new BackPressureCallback(); IAsyncCallback noCallback = new NoBackPressureCallback(); boolean timeout = false; DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), noCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), noCallback, timeout); assertFalse(backPressureState.onReceive); assertFalse(backPressureState.onTimeout); DatabaseDescriptor.setBackPressureEnabled(false); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), bpCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, timeout); assertFalse(backPressureState.onReceive); assertFalse(backPressureState.onTimeout); DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), bpCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, timeout); assertTrue(backPressureState.onReceive); assertFalse(backPressureState.onTimeout); } @@ -266,23 +270,23 @@ public class MessagingServiceTest @Test public void testUpdatesBackPressureOnTimeoutWhenEnabledAndWithSupportedCallback() throws UnknownHostException { - MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddress.getByName("127.0.0.2")); + MockBackPressureStrategy.MockBackPressureState backPressureState = (MockBackPressureStrategy.MockBackPressureState) messagingService.getBackPressureState(InetAddressAndPort.getByName("127.0.0.2")); IAsyncCallback bpCallback = new BackPressureCallback(); IAsyncCallback noCallback = new NoBackPressureCallback(); boolean timeout = true; DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), noCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), noCallback, timeout); assertFalse(backPressureState.onReceive); assertFalse(backPressureState.onTimeout); DatabaseDescriptor.setBackPressureEnabled(false); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), bpCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, timeout); assertFalse(backPressureState.onReceive); assertFalse(backPressureState.onTimeout); DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.updateBackPressureOnReceive(InetAddress.getByName("127.0.0.2"), bpCallback, timeout); + messagingService.updateBackPressureOnReceive(InetAddressAndPort.getByName("127.0.0.2"), bpCallback, timeout); assertFalse(backPressureState.onReceive); assertTrue(backPressureState.onTimeout); } @@ -291,11 +295,11 @@ public class MessagingServiceTest public void testAppliesBackPressureWhenEnabled() throws UnknownHostException { DatabaseDescriptor.setBackPressureEnabled(false); - messagingService.applyBackPressure(Arrays.asList(InetAddress.getByName("127.0.0.2")), ONE_SECOND); + messagingService.applyBackPressure(Arrays.asList(InetAddressAndPort.getByName("127.0.0.2")), ONE_SECOND); assertFalse(MockBackPressureStrategy.applied); DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.applyBackPressure(Arrays.asList(InetAddress.getByName("127.0.0.2")), ONE_SECOND); + messagingService.applyBackPressure(Arrays.asList(InetAddressAndPort.getByName("127.0.0.2")), ONE_SECOND); assertTrue(MockBackPressureStrategy.applied); } @@ -303,13 +307,13 @@ public class MessagingServiceTest public void testDoesntApplyBackPressureToBroadcastAddress() throws UnknownHostException { DatabaseDescriptor.setBackPressureEnabled(true); - messagingService.applyBackPressure(Arrays.asList(InetAddress.getByName("127.0.0.1")), ONE_SECOND); + messagingService.applyBackPressure(Arrays.asList(InetAddressAndPort.getByName("127.0.0.1")), ONE_SECOND); assertFalse(MockBackPressureStrategy.applied); } private static void addDCLatency(long sentAt, long nowTime) throws IOException { - MessageIn.deriveConstructionTime(InetAddress.getLocalHost(), (int)sentAt, nowTime); + MessageIn.deriveConstructionTime(InetAddressAndPort.getLocalHost(), (int)sentAt, nowTime); } public static class MockBackPressureStrategy implements BackPressureStrategy @@ -328,19 +332,19 @@ public class MessagingServiceTest } @Override - public MockBackPressureState newState(InetAddress host) + public MockBackPressureState newState(InetAddressAndPort host) { return new MockBackPressureState(host); } public static class MockBackPressureState implements BackPressureState { - private final InetAddress host; + private final InetAddressAndPort host; public volatile boolean onSend = false; public volatile boolean onReceive = false; public volatile boolean onTimeout = false; - private MockBackPressureState(InetAddress host) + private MockBackPressureState(InetAddressAndPort host) { this.host = host; } @@ -370,7 +374,7 @@ public class MessagingServiceTest } @Override - public InetAddress getHost() + public InetAddressAndPort getHost() { return host; } @@ -429,7 +433,7 @@ public class MessagingServiceTest { MessagingService ms = MessagingService.instance(); DatabaseDescriptor.setInternodeAuthenticator(ALLOW_NOTHING_AUTHENTICATOR); - InetAddress address = InetAddress.getByName("127.0.0.250"); + InetAddressAndPort address = InetAddressAndPort.getByName("127.0.0.250"); //Should return null MessageOut messageOut = new MessageOut(MessagingService.Verb.GOSSIP_DIGEST_ACK); @@ -444,20 +448,20 @@ public class MessagingServiceTest public void testOutboundMessagingConnectionCleansUp() throws Exception { MessagingService ms = MessagingService.instance(); - InetSocketAddress local = new InetSocketAddress("127.0.0.1", 9876); - InetSocketAddress remote = new InetSocketAddress("127.0.0.2", 9876); + InetAddressAndPort local = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", 9876); + InetAddressAndPort remote = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", 9876); - OutboundMessagingPool pool = new OutboundMessagingPool(remote, local, null, new MockBackPressureStrategy(null).newState(remote.getAddress()), ALLOW_NOTHING_AUTHENTICATOR); - ms.channelManagers.put(remote.getAddress(), pool); + OutboundMessagingPool pool = new OutboundMessagingPool(remote, local, null, new MockBackPressureStrategy(null).newState(remote), ALLOW_NOTHING_AUTHENTICATOR); + ms.channelManagers.put(remote, pool); pool.sendMessage(new MessageOut(MessagingService.Verb.GOSSIP_DIGEST_ACK), 0); - assertFalse(ms.channelManagers.containsKey(remote.getAddress())); + assertFalse(ms.channelManagers.containsKey(remote)); } @Test - public void reconnectWithNewIp() throws UnknownHostException + public void reconnectWithNewIp() throws Exception { - InetAddress publicIp = InetAddress.getByName("127.0.0.2"); - InetAddress privateIp = InetAddress.getByName("127.0.0.3"); + InetAddressAndPort publicIp = InetAddressAndPort.getByName("127.0.0.2"); + InetAddressAndPort privateIp = InetAddressAndPort.getByName("127.0.0.3"); // reset the preferred IP value, for good test hygene SystemKeyspace.updatePreferredIP(publicIp, publicIp); @@ -485,8 +489,8 @@ public class MessagingServiceTest Assert.assertEquals(0, serverChannel.size()); // now, create a connection and make sure it's in a channel group - InetSocketAddress server = new InetSocketAddress(FBUtilities.getBroadcastAddress(), DatabaseDescriptor.getStoragePort()); - OutboundConnectionIdentifier id = OutboundConnectionIdentifier.small(new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 0), server); + InetAddressAndPort server = FBUtilities.getBroadcastAddressAndPort(); + OutboundConnectionIdentifier id = OutboundConnectionIdentifier.small(InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", 0), server); CountDownLatch latch = new CountDownLatch(1); OutboundConnectionParams params = OutboundConnectionParams.builder() @@ -596,7 +600,7 @@ public class MessagingServiceTest if (listenOnBroadcastAddr) { DatabaseDescriptor.setShouldListenOnBroadcastAddress(true); - listenAddress = InetAddresses.increment(FBUtilities.getBroadcastAddress()); + listenAddress = InetAddresses.increment(FBUtilities.getBroadcastAddressAndPort().address); DatabaseDescriptor.setListenAddress(listenAddress); FBUtilities.reset(); } @@ -627,7 +631,7 @@ public class MessagingServiceTest if (serverEncryptionOptions.enable_legacy_ssl_storage_port) { - if (legacySslPort == serverChannel.getAddress().getPort()) + if (legacySslPort == serverChannel.getAddress().port) { foundLegacyListenSslAddress = true; Assert.assertEquals(ServerChannel.SecurityLevel.REQUIRED, serverChannel.getSecurityLevel()); @@ -646,7 +650,7 @@ public class MessagingServiceTest int found = 0; for (ServerChannel serverChannel : messagingService.serverChannels) { - if (serverChannel.getAddress().getAddress().equals(listenAddress)) + if (serverChannel.getAddress().address.equals(listenAddress)) found++; } diff --git a/test/unit/org/apache/cassandra/net/MockMessagingService.java b/test/unit/org/apache/cassandra/net/MockMessagingService.java index 04127590b3..4ea1bf5aee 100644 --- a/test/unit/org/apache/cassandra/net/MockMessagingService.java +++ b/test/unit/org/apache/cassandra/net/MockMessagingService.java @@ -17,14 +17,15 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.function.Predicate; +import org.apache.cassandra.locator.InetAddressAndPort; + /** * Starting point for mocking {@link MessagingService} interactions. Outgoing messages can be * intercepted by first creating a {@link MatcherResponse} by calling {@link MockMessagingService#when(Matcher)}. - * Alternatively {@link Matcher}s can be created by using helper methods such as {@link #to(InetAddress)}, + * Alternatively {@link Matcher}s can be created by using helper methods such as {@link #to(InetAddressAndPort)}, * {@link #verb(MessagingService.Verb)} or {@link #payload(Predicate)} and may also be * nested using {@link MockMessagingService#all(Matcher[])} or {@link MockMessagingService#any(Matcher[])}. * After each test, {@link MockMessagingService#cleanup()} must be called for free listeners registered @@ -58,11 +59,11 @@ public class MockMessagingService * Creates a matcher that will indicate if the target address of the outgoing message equals the * provided address. */ - public static Matcher to(String address) + public static Matcher to(String address) { try { - return to(InetAddress.getByName(address)); + return to(InetAddressAndPort.getByName(address)); } catch (UnknownHostException e) { @@ -74,7 +75,7 @@ public class MockMessagingService * Creates a matcher that will indicate if the target address of the outgoing message equals the * provided address. */ - public static Matcher to(InetAddress address) + public static Matcher to(InetAddressAndPort address) { return (in, to) -> to == address || to.equals(address); } @@ -117,7 +118,7 @@ public class MockMessagingService */ public static Matcher all(Matcher... matchers) { - return (MessageOut out, InetAddress to) -> { + return (MessageOut out, InetAddressAndPort to) -> { for (Matcher matcher : matchers) { if (!matcher.matches(out, to)) @@ -132,7 +133,7 @@ public class MockMessagingService */ public static Matcher any(Matcher... matchers) { - return (MessageOut out, InetAddress to) -> { + return (MessageOut out, InetAddressAndPort to) -> { for (Matcher matcher : matchers) { if (matcher.matches(out, to)) diff --git a/test/unit/org/apache/cassandra/net/MockMessagingServiceTest.java b/test/unit/org/apache/cassandra/net/MockMessagingServiceTest.java index 3f6564e092..8d0f91bf8d 100644 --- a/test/unit/org/apache/cassandra/net/MockMessagingServiceTest.java +++ b/test/unit/org/apache/cassandra/net/MockMessagingServiceTest.java @@ -55,7 +55,7 @@ public class MockMessagingServiceTest public void testRequestResponse() throws InterruptedException, ExecutionException { // echo message that we like to mock as incoming reply for outgoing echo message - MessageIn echoMessageIn = MessageIn.create(FBUtilities.getBroadcastAddress(), + MessageIn echoMessageIn = MessageIn.create(FBUtilities.getBroadcastAddressAndPort(), EchoMessage.instance, Collections.emptyMap(), MessagingService.Verb.ECHO, @@ -63,14 +63,14 @@ public class MockMessagingServiceTest MockMessagingSpy spy = MockMessagingService .when( all( - to(FBUtilities.getBroadcastAddress()), + to(FBUtilities.getBroadcastAddressAndPort()), verb(MessagingService.Verb.ECHO) ) ) .respond(echoMessageIn); MessageOut echoMessageOut = new MessageOut<>(MessagingService.Verb.ECHO, EchoMessage.instance, EchoMessage.serializer); - MessagingService.instance().sendRR(echoMessageOut, FBUtilities.getBroadcastAddress(), new IAsyncCallback() + MessagingService.instance().sendRR(echoMessageOut, FBUtilities.getBroadcastAddressAndPort(), new IAsyncCallback() { public void response(MessageIn msg) { diff --git a/test/unit/org/apache/cassandra/net/RateBasedBackPressureTest.java b/test/unit/org/apache/cassandra/net/RateBasedBackPressureTest.java index 93fe32e80c..4d1ae01c88 100644 --- a/test/unit/org/apache/cassandra/net/RateBasedBackPressureTest.java +++ b/test/unit/org/apache/cassandra/net/RateBasedBackPressureTest.java @@ -18,7 +18,6 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -29,6 +28,7 @@ import com.google.common.util.concurrent.RateLimiter; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.TestTimeSource; import org.apache.cassandra.utils.TimeSource; @@ -94,17 +94,17 @@ public class RateBasedBackPressureTest TestTimeSource timeSource = new TestTimeSource(); RateBasedBackPressure strategy = new RateBasedBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state = strategy.newState(InetAddress.getLoopbackAddress()); + RateBasedBackPressureState state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); state.onMessageSent(null); assertEquals(0, state.incomingRate.size()); assertEquals(0, state.outgoingRate.size()); - state = strategy.newState(InetAddress.getLoopbackAddress()); + state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); state.onResponseReceived(); assertEquals(1, state.incomingRate.size()); assertEquals(1, state.outgoingRate.size()); - state = strategy.newState(InetAddress.getLoopbackAddress()); + state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); state.onResponseTimeout(); assertEquals(0, state.incomingRate.size()); assertEquals(1, state.outgoingRate.size()); @@ -116,7 +116,7 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); RateBasedBackPressure strategy = new RateBasedBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state = strategy.newState(InetAddress.getLoopbackAddress()); + RateBasedBackPressureState state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); // Get initial rate: double initialRate = state.rateLimiter.getRate(); @@ -140,7 +140,7 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); RateBasedBackPressure strategy = new RateBasedBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state = strategy.newState(InetAddress.getLoopbackAddress()); + RateBasedBackPressureState state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); // Get initial time: long current = state.getLastIntervalAcquire(); @@ -174,7 +174,7 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); RateBasedBackPressure strategy = new RateBasedBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state = strategy.newState(InetAddress.getLoopbackAddress()); + RateBasedBackPressureState state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); // Update incoming and outgoing rate so that the ratio is 0.5: state.incomingRate.update(50); @@ -194,7 +194,7 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); RateBasedBackPressure strategy = new RateBasedBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state = strategy.newState(InetAddress.getLoopbackAddress()); + RateBasedBackPressureState state = strategy.newState(InetAddressAndPort.getLoopbackAddress()); // Update incoming and outgoing rate so that the ratio is 0.5: state.incomingRate.update(50); @@ -236,9 +236,9 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); TestableBackPressure strategy = new TestableBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "FAST"), timeSource, windowSize); - RateBasedBackPressureState state1 = strategy.newState(InetAddress.getByName("127.0.0.1")); - RateBasedBackPressureState state2 = strategy.newState(InetAddress.getByName("127.0.0.2")); - RateBasedBackPressureState state3 = strategy.newState(InetAddress.getByName("127.0.0.3")); + RateBasedBackPressureState state1 = strategy.newState(InetAddressAndPort.getByName("127.0.0.1")); + RateBasedBackPressureState state2 = strategy.newState(InetAddressAndPort.getByName("127.0.0.2")); + RateBasedBackPressureState state3 = strategy.newState(InetAddressAndPort.getByName("127.0.0.3")); // Update incoming and outgoing rates: state1.incomingRate.update(50); @@ -265,9 +265,9 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); TestableBackPressure strategy = new TestableBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "SLOW"), timeSource, windowSize); - RateBasedBackPressureState state1 = strategy.newState(InetAddress.getByName("127.0.0.1")); - RateBasedBackPressureState state2 = strategy.newState(InetAddress.getByName("127.0.0.2")); - RateBasedBackPressureState state3 = strategy.newState(InetAddress.getByName("127.0.0.3")); + RateBasedBackPressureState state1 = strategy.newState(InetAddressAndPort.getByName("127.0.0.1")); + RateBasedBackPressureState state2 = strategy.newState(InetAddressAndPort.getByName("127.0.0.2")); + RateBasedBackPressureState state3 = strategy.newState(InetAddressAndPort.getByName("127.0.0.3")); // Update incoming and outgoing rates: state1.incomingRate.update(50); @@ -294,10 +294,10 @@ public class RateBasedBackPressureTest long windowSize = 6000; TestTimeSource timeSource = new TestTimeSource(); TestableBackPressure strategy = new TestableBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "SLOW"), timeSource, windowSize); - RateBasedBackPressureState state1 = strategy.newState(InetAddress.getByName("127.0.0.1")); - RateBasedBackPressureState state2 = strategy.newState(InetAddress.getByName("127.0.0.2")); - RateBasedBackPressureState state3 = strategy.newState(InetAddress.getByName("127.0.0.3")); - RateBasedBackPressureState state4 = strategy.newState(InetAddress.getByName("127.0.0.4")); + RateBasedBackPressureState state1 = strategy.newState(InetAddressAndPort.getByName("127.0.0.1")); + RateBasedBackPressureState state2 = strategy.newState(InetAddressAndPort.getByName("127.0.0.2")); + RateBasedBackPressureState state3 = strategy.newState(InetAddressAndPort.getByName("127.0.0.3")); + RateBasedBackPressureState state4 = strategy.newState(InetAddressAndPort.getByName("127.0.0.4")); // Update incoming and outgoing rates: state1.incomingRate.update(50); // this @@ -333,9 +333,9 @@ public class RateBasedBackPressureTest long windowSize = 10000; TestTimeSource timeSource = new TestTimeSource(); TestableBackPressure strategy = new TestableBackPressure(ImmutableMap.of(HIGH_RATIO, "0.9", FACTOR, "10", FLOW, "SLOW"), timeSource, windowSize); - RateBasedBackPressureState state1 = strategy.newState(InetAddress.getByName("127.0.0.1")); - RateBasedBackPressureState state2 = strategy.newState(InetAddress.getByName("127.0.0.2")); - RateBasedBackPressureState state3 = strategy.newState(InetAddress.getByName("127.0.0.3")); + RateBasedBackPressureState state1 = strategy.newState(InetAddressAndPort.getByName("127.0.0.1")); + RateBasedBackPressureState state2 = strategy.newState(InetAddressAndPort.getByName("127.0.0.2")); + RateBasedBackPressureState state3 = strategy.newState(InetAddressAndPort.getByName("127.0.0.3")); // Update incoming and outgoing rates: state1.incomingRate.update(5); // slow diff --git a/test/unit/org/apache/cassandra/net/WriteCallbackInfoTest.java b/test/unit/org/apache/cassandra/net/WriteCallbackInfoTest.java index 2d12bafdfa..c8469a8ec9 100644 --- a/test/unit/org/apache/cassandra/net/WriteCallbackInfoTest.java +++ b/test/unit/org/apache/cassandra/net/WriteCallbackInfoTest.java @@ -18,7 +18,6 @@ */ package org.apache.cassandra.net; -import java.net.InetAddress; import java.util.UUID; import org.junit.BeforeClass; @@ -32,6 +31,7 @@ import org.apache.cassandra.db.Mutation; import org.apache.cassandra.db.RegularAndStaticColumns; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.dht.Murmur3Partitioner; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService.Verb; import org.apache.cassandra.schema.MockSchema; import org.apache.cassandra.schema.TableMetadata; @@ -65,7 +65,7 @@ public class WriteCallbackInfoTest ? new Commit(UUID.randomUUID(), new PartitionUpdate.Builder(metadata, ByteBufferUtil.EMPTY_BYTE_BUFFER, RegularAndStaticColumns.NONE, 1).build()) : new Mutation(PartitionUpdate.simpleBuilder(metadata, "").build()); - WriteCallbackInfo wcbi = new WriteCallbackInfo(InetAddress.getByName("192.168.1.1"), null, new MessageOut(verb, payload, null), null, cl, allowHints); + WriteCallbackInfo wcbi = new WriteCallbackInfo(InetAddressAndPort.getByName("192.168.1.1"), null, new MessageOut(verb, payload, null), null, cl, allowHints); Assert.assertEquals(expectHint, wcbi.shouldHint()); if (expectHint) { diff --git a/test/unit/org/apache/cassandra/net/async/ChannelWriterTest.java b/test/unit/org/apache/cassandra/net/async/ChannelWriterTest.java index 128fe4b8fa..02115124d4 100644 --- a/test/unit/org/apache/cassandra/net/async/ChannelWriterTest.java +++ b/test/unit/org/apache/cassandra/net/async/ChannelWriterTest.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import com.google.common.net.InetAddresses; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -37,6 +38,7 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.embedded.EmbeddedChannel; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.ChannelWriter.CoalescingChannelWriter; @@ -68,8 +70,8 @@ public class ChannelWriterTest @Before public void setup() { - OutboundConnectionIdentifier id = OutboundConnectionIdentifier.small(new InetSocketAddress("127.0.0.1", 0), - new InetSocketAddress("127.0.0.2", 0)); + OutboundConnectionIdentifier id = OutboundConnectionIdentifier.small(InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 0), + InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 0)); channel = new EmbeddedChannel(); omc = new NonSendingOutboundMessagingConnection(id, null, Optional.empty()); channelWriter = ChannelWriter.create(channel, omc::handleMessageResult, Optional.empty()); diff --git a/test/unit/org/apache/cassandra/net/async/HandshakeHandlersTest.java b/test/unit/org/apache/cassandra/net/async/HandshakeHandlersTest.java index 100e1e0697..f92ce5a0bc 100644 --- a/test/unit/org/apache/cassandra/net/async/HandshakeHandlersTest.java +++ b/test/unit/org/apache/cassandra/net/async/HandshakeHandlersTest.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Optional; +import com.google.common.net.InetAddresses; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -38,6 +39,7 @@ import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.marshal.AsciiType; import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.KeyspaceParams; @@ -50,8 +52,8 @@ public class HandshakeHandlersTest private static final String KEYSPACE1 = "NettyPipilineTest"; private static final String STANDARD1 = "Standard1"; - private static final InetSocketAddress LOCAL_ADDR = new InetSocketAddress("127.0.0.1", 9999); - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 9999); + private static final InetAddressAndPort LOCAL_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 9999); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 9999); private static final int MESSAGING_VERSION = MessagingService.current_version; private static final OutboundConnectionIdentifier connectionId = OutboundConnectionIdentifier.small(LOCAL_ADDR, REMOTE_ADDR); @@ -179,7 +181,7 @@ public class HandshakeHandlersTest InboundHandshakeHandler handler = new InboundHandshakeHandler(new TestAuthenticator(true)); EmbeddedChannel inboundChannel = new EmbeddedChannel(handler); - handler.setupMessagingPipeline(inboundChannel.pipeline(), REMOTE_ADDR.getAddress(), compress, MESSAGING_VERSION); + handler.setupMessagingPipeline(inboundChannel.pipeline(), REMOTE_ADDR, compress, MESSAGING_VERSION); return new TestChannels(outboundChannel, inboundChannel); } diff --git a/test/unit/org/apache/cassandra/net/async/HandshakeProtocolTest.java b/test/unit/org/apache/cassandra/net/async/HandshakeProtocolTest.java index a3d646d24d..af486368c6 100644 --- a/test/unit/org/apache/cassandra/net/async/HandshakeProtocolTest.java +++ b/test/unit/org/apache/cassandra/net/async/HandshakeProtocolTest.java @@ -85,7 +85,7 @@ public class HandshakeProtocolTest @Test public void thirdMessageTest() throws Exception { - ThirdHandshakeMessage before = new ThirdHandshakeMessage(MessagingService.current_version, FBUtilities.getBroadcastAddress()); + ThirdHandshakeMessage before = new ThirdHandshakeMessage(MessagingService.current_version, FBUtilities.getBroadcastAddressAndPort()); buf = before.encode(PooledByteBufAllocator.DEFAULT); ThirdHandshakeMessage after = ThirdHandshakeMessage.maybeDecode(buf); assertEquals(before, after); diff --git a/test/unit/org/apache/cassandra/net/async/InboundHandshakeHandlerTest.java b/test/unit/org/apache/cassandra/net/async/InboundHandshakeHandlerTest.java index 4d9829f356..93a1c20455 100644 --- a/test/unit/org/apache/cassandra/net/async/InboundHandshakeHandlerTest.java +++ b/test/unit/org/apache/cassandra/net/async/InboundHandshakeHandlerTest.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -39,6 +40,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.Lz4FrameDecoder; import io.netty.handler.codec.compression.Lz4FrameEncoder; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.CompactEndpointSerializationHelper; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.HandshakeProtocol.FirstHandshakeMessage; @@ -49,7 +51,7 @@ import static org.apache.cassandra.net.async.NettyFactory.Mode.MESSAGING; public class InboundHandshakeHandlerTest { - private static final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 0); + private static final InetAddressAndPort addr = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 0); private static final int MESSAGING_VERSION = MessagingService.current_version; private static final int VERSION_30 = MessagingService.VERSION_30; @@ -84,7 +86,7 @@ public class InboundHandshakeHandlerTest { handler = new InboundHandshakeHandler(new TestAuthenticator(true)); channel = new EmbeddedChannel(handler); - boolean result = handler.handleAuthenticate(addr, channel.pipeline().firstContext()); + boolean result = handler.handleAuthenticate(new InetSocketAddress(addr.address, addr.port), channel.pipeline().firstContext()); Assert.assertTrue(result); Assert.assertTrue(channel.isOpen()); } @@ -92,7 +94,7 @@ public class InboundHandshakeHandlerTest @Test public void handleAuthenticate_Bad() { - boolean result = handler.handleAuthenticate(addr, channel.pipeline().firstContext()); + boolean result = handler.handleAuthenticate(new InetSocketAddress(addr.address, addr.port), channel.pipeline().firstContext()); Assert.assertFalse(result); Assert.assertFalse(channel.isOpen()); Assert.assertFalse(channel.isActive()); @@ -178,7 +180,7 @@ public class InboundHandshakeHandlerTest if (buf.refCnt() > 0) buf.release(); - buf = new ThirdHandshakeMessage(MESSAGING_VERSION, addr.getAddress()).encode(PooledByteBufAllocator.DEFAULT); + buf = new ThirdHandshakeMessage(MESSAGING_VERSION, addr).encode(PooledByteBufAllocator.DEFAULT); state = handler.handleMessagingStartResponse(channel.pipeline().firstContext(), buf); Assert.assertEquals(State.HANDSHAKE_COMPLETE, state); @@ -203,7 +205,7 @@ public class InboundHandshakeHandlerTest { buf = Unpooled.buffer(32, 32); buf.writeInt(MESSAGING_VERSION + 1); - CompactEndpointSerializationHelper.serialize(addr.getAddress(), new ByteBufOutputStream(buf)); + CompactEndpointSerializationHelper.instance.serialize(addr, new ByteBufDataOutputPlus(buf), MESSAGING_VERSION + 1); State state = handler.handleMessagingStartResponse(channel.pipeline().firstContext(), buf); Assert.assertEquals(State.HANDSHAKE_FAIL, state); Assert.assertFalse(channel.isOpen()); @@ -215,7 +217,7 @@ public class InboundHandshakeHandlerTest { buf = Unpooled.buffer(32, 32); buf.writeInt(MESSAGING_VERSION); - CompactEndpointSerializationHelper.serialize(addr.getAddress(), new ByteBufOutputStream(buf)); + CompactEndpointSerializationHelper.instance.serialize(addr, new ByteBufDataOutputPlus(buf), MESSAGING_VERSION); State state = handler.handleMessagingStartResponse(channel.pipeline().firstContext(), buf); Assert.assertEquals(State.HANDSHAKE_COMPLETE, state); Assert.assertTrue(channel.isOpen()); @@ -228,7 +230,7 @@ public class InboundHandshakeHandlerTest ChannelPipeline pipeline = channel.pipeline(); Assert.assertNotNull(pipeline.get(InboundHandshakeHandler.class)); - handler.setupMessagingPipeline(pipeline, addr.getAddress(), false, MESSAGING_VERSION); + handler.setupMessagingPipeline(pipeline, addr, false, MESSAGING_VERSION); Assert.assertNotNull(pipeline.get(MessageInHandler.class)); Assert.assertNull(pipeline.get(Lz4FrameDecoder.class)); Assert.assertNull(pipeline.get(Lz4FrameEncoder.class)); @@ -241,7 +243,7 @@ public class InboundHandshakeHandlerTest ChannelPipeline pipeline = channel.pipeline(); Assert.assertNotNull(pipeline.get(InboundHandshakeHandler.class)); - handler.setupMessagingPipeline(pipeline, addr.getAddress(), true, MESSAGING_VERSION); + handler.setupMessagingPipeline(pipeline, addr, true, MESSAGING_VERSION); Assert.assertNotNull(pipeline.get(MessageInHandler.class)); Assert.assertNotNull(pipeline.get(Lz4FrameDecoder.class)); Assert.assertNull(pipeline.get(Lz4FrameEncoder.class)); diff --git a/test/unit/org/apache/cassandra/net/async/MessageInHandlerTest.java b/test/unit/org/apache/cassandra/net/async/MessageInHandlerTest.java index bb82d2c83e..43cb964b8d 100644 --- a/test/unit/org/apache/cassandra/net/async/MessageInHandlerTest.java +++ b/test/unit/org/apache/cassandra/net/async/MessageInHandlerTest.java @@ -20,40 +20,36 @@ package org.apache.cassandra.net.async; import java.io.EOFException; import java.io.IOException; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.function.BiConsumer; -import com.google.common.base.Charsets; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.concurrent.Future; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; import org.apache.cassandra.net.async.MessageInHandler.MessageHeader; -import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.NanoTimeToCurrentTimeMillis; +import org.apache.cassandra.utils.UUIDGen; public class MessageInHandlerTest { - private static final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 0); + private static final InetAddressAndPort addr = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 0); private static final int MSG_VERSION = MessagingService.current_version; private static final int MSG_ID = 42; @@ -81,7 +77,7 @@ public class MessageInHandlerTest buf.writeInt(-1); buf.writerIndex(len); - MessageInHandler handler = new MessageInHandler(addr.getAddress(), MSG_VERSION, null); + MessageInHandler handler = new MessageInHandler(addr, MSG_VERSION, null); EmbeddedChannel channel = new EmbeddedChannel(handler); Assert.assertTrue(channel.isOpen()); channel.writeInbound(buf); @@ -98,22 +94,25 @@ public class MessageInHandlerTest @Test public void decode_HappyPath_WithParameters() throws Exception { - Map parameters = new HashMap<>(); - parameters.put("p1", "val1".getBytes(Charsets.UTF_8)); - parameters.put("p2", "val2".getBytes(Charsets.UTF_8)); + UUID uuid = UUIDGen.getTimeUUID(); + Map parameters = new HashMap<>(); + parameters.put(ParameterType.FAILURE_REASON, (short)42); + parameters.put(ParameterType.TRACE_SESSION, uuid); MessageInWrapper result = decode_HappyPath(parameters); Assert.assertEquals(2, result.messageIn.parameters.size()); + Assert.assertEquals((short)42, result.messageIn.parameters.get(ParameterType.FAILURE_REASON)); + Assert.assertEquals(uuid, result.messageIn.parameters.get(ParameterType.TRACE_SESSION)); } - private MessageInWrapper decode_HappyPath(Map parameters) throws Exception + private MessageInWrapper decode_HappyPath(Map parameters) throws Exception { MessageOut msgOut = new MessageOut(MessagingService.Verb.ECHO); - for (Map.Entry param : parameters.entrySet()) + for (Map.Entry param : parameters.entrySet()) msgOut = msgOut.withParameter(param.getKey(), param.getValue()); serialize(msgOut); MessageInWrapper wrapper = new MessageInWrapper(); - MessageInHandler handler = new MessageInHandler(addr.getAddress(), MSG_VERSION, wrapper.messageConsumer); + MessageInHandler handler = new MessageInHandler(addr, MSG_VERSION, wrapper.messageConsumer); List out = new ArrayList<>(); handler.decode(null, buf, out); @@ -140,7 +139,7 @@ public class MessageInHandlerTest public void decode_WithHalfReceivedParameters() throws Exception { MessageOut msgOut = new MessageOut(MessagingService.Verb.ECHO); - msgOut = msgOut.withParameter("p3", "val1".getBytes(Charsets.UTF_8)); + msgOut = msgOut.withParameter(ParameterType.FAILURE_REASON, (short)42); serialize(msgOut); @@ -150,7 +149,7 @@ public class MessageInHandlerTest buf.writerIndex(originalWriterIndex - 6); MessageInWrapper wrapper = new MessageInWrapper(); - MessageInHandler handler = new MessageInHandler(addr.getAddress(), MSG_VERSION, wrapper.messageConsumer); + MessageInHandler handler = new MessageInHandler(addr, MSG_VERSION, wrapper.messageConsumer); List out = new ArrayList<>(); handler.decode(null, buf, out); @@ -221,7 +220,7 @@ public class MessageInHandlerTest @Test public void exceptionHandled() { - MessageInHandler handler = new MessageInHandler(addr.getAddress(), MSG_VERSION, null); + MessageInHandler handler = new MessageInHandler(addr, MSG_VERSION, null); EmbeddedChannel channel = new EmbeddedChannel(handler); Assert.assertTrue(channel.isOpen()); handler.exceptionCaught(channel.pipeline().firstContext(), new EOFException()); diff --git a/test/unit/org/apache/cassandra/net/async/MessageOutHandlerTest.java b/test/unit/org/apache/cassandra/net/async/MessageOutHandlerTest.java index 566dfdbe0e..86112ae3cf 100644 --- a/test/unit/org/apache/cassandra/net/async/MessageOutHandlerTest.java +++ b/test/unit/org/apache/cassandra/net/async/MessageOutHandlerTest.java @@ -44,8 +44,10 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; +import org.apache.cassandra.net.ParameterType; import org.apache.cassandra.tracing.Tracing; import org.apache.cassandra.utils.UUIDGen; @@ -65,15 +67,15 @@ public class MessageOutHandlerTest } @Before - public void setup() + public void setup() throws Exception { setup(MessageOutHandler.AUTO_FLUSH_THRESHOLD); } - private void setup(int flushThreshold) + private void setup(int flushThreshold) throws Exception { - OutboundConnectionIdentifier connectionId = OutboundConnectionIdentifier.small(new InetSocketAddress("127.0.0.1", 0), - new InetSocketAddress("127.0.0.2", 0)); + OutboundConnectionIdentifier connectionId = OutboundConnectionIdentifier.small(InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", 0), + InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", 0)); OutboundMessagingConnection omc = new NonSendingOutboundMessagingConnection(connectionId, null, Optional.empty()); channel = new EmbeddedChannel(); channelWriter = ChannelWriter.create(channel, omc::handleMessageResult, Optional.empty()); @@ -91,7 +93,7 @@ public class MessageOutHandlerTest } @Test - public void write_WithFlush() throws ExecutionException, InterruptedException, TimeoutException + public void write_WithFlush() throws Exception { setup(1); MessageOut message = new MessageOut(MessagingService.Verb.ECHO); @@ -217,7 +219,7 @@ public class MessageOutHandlerTest public void captureTracingInfo_ForceException() { MessageOut message = new MessageOut(MessagingService.Verb.INTERNAL_RESPONSE) - .withParameter(Tracing.TRACE_HEADER, new byte[9]); + .withParameter(ParameterType.TRACE_SESSION, new byte[9]); handler.captureTracingInfo(new QueuedMessage(message, 42)); } @@ -226,7 +228,7 @@ public class MessageOutHandlerTest { UUID uuid = UUID.randomUUID(); MessageOut message = new MessageOut(MessagingService.Verb.INTERNAL_RESPONSE) - .withParameter(Tracing.TRACE_HEADER, UUIDGen.decompose(uuid)); + .withParameter(ParameterType.TRACE_SESSION, uuid); handler.captureTracingInfo(new QueuedMessage(message, 42)); } diff --git a/test/unit/org/apache/cassandra/net/async/NettyFactoryTest.java b/test/unit/org/apache/cassandra/net/async/NettyFactoryTest.java index 055049070d..4607d5cc9f 100644 --- a/test/unit/org/apache/cassandra/net/async/NettyFactoryTest.java +++ b/test/unit/org/apache/cassandra/net/async/NettyFactoryTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.net.async; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Optional; @@ -47,6 +46,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.NettyFactory.InboundInitializer; import org.apache.cassandra.net.async.NettyFactory.OutboundInitializer; @@ -56,8 +56,8 @@ import org.apache.cassandra.utils.NativeLibrary; public class NettyFactoryTest { - private static final InetSocketAddress LOCAL_ADDR = new InetSocketAddress("127.0.0.1", 9876); - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 9876); + private static final InetAddressAndPort LOCAL_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 9876); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 9876); private static final int receiveBufferSize = 1 << 16; private static final IInternodeAuthenticator AUTHENTICATOR = new AllowAllInternodeAuthenticator(); private static final boolean EPOLL_AVAILABLE = NativeTransportService.useEpoll(); @@ -129,7 +129,6 @@ public class NettyFactoryTest Channel inboundChannel = null; try { - InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 9876); InboundInitializer inboundInitializer = new InboundInitializer(AUTHENTICATOR, null, channelGroup); inboundChannel = NettyFactory.instance.createInboundChannel(LOCAL_ADDR, inboundInitializer, receiveBufferSize); NettyFactory.instance.createInboundChannel(LOCAL_ADDR, inboundInitializer, receiveBufferSize); @@ -144,7 +143,7 @@ public class NettyFactoryTest @Test(expected = ConfigurationException.class) public void createServerChannel_UnbindableAddress() { - InetSocketAddress addr = new InetSocketAddress("1.1.1.1", 9876); + InetAddressAndPort addr = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("1.1.1.1"), 9876); InboundInitializer inboundInitializer = new InboundInitializer(AUTHENTICATOR, null, channelGroup); NettyFactory.instance.createInboundChannel(addr, inboundInitializer, receiveBufferSize); } @@ -162,10 +161,10 @@ public class NettyFactoryTest Assert.assertEquals(2, NettyFactory.determineAcceptGroupSize(serverEncryptionOptions)); serverEncryptionOptions.enable_legacy_ssl_storage_port = false; - InetAddress originalBroadcastAddr = FBUtilities.getBroadcastAddress(); + InetAddressAndPort originalBroadcastAddr = FBUtilities.getBroadcastAddressAndPort(); try { - FBUtilities.setBroadcastInetAddress(InetAddresses.increment(FBUtilities.getLocalAddress())); + FBUtilities.setBroadcastInetAddress(InetAddresses.increment(FBUtilities.getLocalAddressAndPort().address)); DatabaseDescriptor.setListenOnBroadcastAddress(true); serverEncryptionOptions.enabled = false; @@ -178,7 +177,7 @@ public class NettyFactoryTest } finally { - FBUtilities.setBroadcastInetAddress(originalBroadcastAddr); + FBUtilities.setBroadcastInetAddress(originalBroadcastAddr.address); DatabaseDescriptor.setListenOnBroadcastAddress(false); } } diff --git a/test/unit/org/apache/cassandra/net/async/OutboundHandshakeHandlerTest.java b/test/unit/org/apache/cassandra/net/async/OutboundHandshakeHandlerTest.java index f8bfab1a8c..be71fd4f28 100644 --- a/test/unit/org/apache/cassandra/net/async/OutboundHandshakeHandlerTest.java +++ b/test/unit/org/apache/cassandra/net/async/OutboundHandshakeHandlerTest.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -38,6 +39,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.Lz4FrameDecoder; import io.netty.handler.codec.compression.Lz4FrameEncoder; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.async.HandshakeProtocol.SecondHandshakeMessage; import org.apache.cassandra.net.async.OutboundHandshakeHandler.HandshakeResult; @@ -47,8 +49,8 @@ import static org.apache.cassandra.net.async.OutboundHandshakeHandler.HandshakeR public class OutboundHandshakeHandlerTest { private static final int MESSAGING_VERSION = MessagingService.current_version; - private static final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1", 0); - private static final InetSocketAddress remoteAddr = new InetSocketAddress("127.0.0.2", 0); + private static final InetAddressAndPort localAddr = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 0); + private static final InetAddressAndPort remoteAddr = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 0); private static final String HANDLER_NAME = "clientHandshakeHandler"; private EmbeddedChannel channel; diff --git a/test/unit/org/apache/cassandra/net/async/OutboundMessagingConnectionTest.java b/test/unit/org/apache/cassandra/net/async/OutboundMessagingConnectionTest.java index 641c28ce3f..bf6e066d80 100644 --- a/test/unit/org/apache/cassandra/net/async/OutboundMessagingConnectionTest.java +++ b/test/unit/org/apache/cassandra/net/async/OutboundMessagingConnectionTest.java @@ -28,6 +28,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLHandshakeException; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -45,6 +46,7 @@ import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.locator.AbstractEndpointSnitch; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.net.MessagingServiceTest; @@ -59,9 +61,9 @@ import static org.apache.cassandra.net.async.OutboundMessagingConnection.State.R public class OutboundMessagingConnectionTest { - private static final InetSocketAddress LOCAL_ADDR = new InetSocketAddress("127.0.0.1", 9998); - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 9999); - private static final InetSocketAddress RECONNECT_ADDR = new InetSocketAddress("127.0.0.3", 9999); + private static final InetAddressAndPort LOCAL_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 9998); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 9999); + private static final InetAddressAndPort RECONNECT_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.3"), 9999); private static final int MESSAGING_VERSION = MessagingService.current_version; private OutboundConnectionIdentifier connectionId; @@ -131,47 +133,47 @@ public class OutboundMessagingConnectionTest public void shouldCompressConnection_None() { DatabaseDescriptor.setInternodeCompression(Config.InternodeCompression.none); - Assert.assertFalse(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR.getAddress(), REMOTE_ADDR.getAddress())); + Assert.assertFalse(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR, REMOTE_ADDR)); } @Test public void shouldCompressConnection_All() { DatabaseDescriptor.setInternodeCompression(Config.InternodeCompression.all); - Assert.assertTrue(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR.getAddress(), REMOTE_ADDR.getAddress())); + Assert.assertTrue(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR, REMOTE_ADDR)); } @Test public void shouldCompressConnection_SameDc() { TestSnitch snitch = new TestSnitch(); - snitch.add(LOCAL_ADDR.getAddress(), "dc1"); - snitch.add(REMOTE_ADDR.getAddress(), "dc1"); + snitch.add(LOCAL_ADDR, "dc1"); + snitch.add(REMOTE_ADDR, "dc1"); DatabaseDescriptor.setEndpointSnitch(snitch); DatabaseDescriptor.setInternodeCompression(Config.InternodeCompression.dc); - Assert.assertFalse(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR.getAddress(), REMOTE_ADDR.getAddress())); + Assert.assertFalse(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR, REMOTE_ADDR)); } private static class TestSnitch extends AbstractEndpointSnitch { - private Map nodeToDc = new HashMap<>(); + private Map nodeToDc = new HashMap<>(); - void add(InetAddress node, String dc) + void add(InetAddressAndPort node, String dc) { nodeToDc.put(node, dc); } - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return null; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { return nodeToDc.get(endpoint); } - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -181,11 +183,11 @@ public class OutboundMessagingConnectionTest public void shouldCompressConnection_DifferentDc() { TestSnitch snitch = new TestSnitch(); - snitch.add(LOCAL_ADDR.getAddress(), "dc1"); - snitch.add(REMOTE_ADDR.getAddress(), "dc2"); + snitch.add(LOCAL_ADDR, "dc1"); + snitch.add(REMOTE_ADDR, "dc2"); DatabaseDescriptor.setEndpointSnitch(snitch); DatabaseDescriptor.setInternodeCompression(Config.InternodeCompression.dc); - Assert.assertTrue(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR.getAddress(), REMOTE_ADDR.getAddress())); + Assert.assertTrue(OutboundMessagingConnection.shouldCompressConnection(LOCAL_ADDR, REMOTE_ADDR)); } @Test @@ -247,7 +249,7 @@ public class OutboundMessagingConnectionTest MessageOut messageOut = new MessageOut(MessagingService.Verb.GOSSIP_DIGEST_ACK); OutboundMessagingPool pool = new OutboundMessagingPool(REMOTE_ADDR, LOCAL_ADDR, null, - new MessagingServiceTest.MockBackPressureStrategy(null).newState(REMOTE_ADDR.getAddress()), auth); + new MessagingServiceTest.MockBackPressureStrategy(null).newState(REMOTE_ADDR), auth); omc = pool.getConnection(messageOut); Assert.assertSame(State.NOT_READY, omc.getState()); Assert.assertFalse(omc.connect()); @@ -371,7 +373,7 @@ public class OutboundMessagingConnectionTest Assert.assertFalse(channelWriter.isClosed()); Assert.assertEquals(channelWriter, omc.getChannelWriter()); Assert.assertEquals(READY, omc.getState()); - Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR.getAddress())); + Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR)); Assert.assertNull(omc.getConnectionTimeoutFuture()); Assert.assertTrue(connectionTimeoutFuture.isCancelled()); } @@ -391,7 +393,7 @@ public class OutboundMessagingConnectionTest Assert.assertTrue(channelWriter.isClosed()); Assert.assertNull(omc.getChannelWriter()); Assert.assertEquals(CLOSED, omc.getState()); - Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR.getAddress())); + Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR)); Assert.assertNull(omc.getConnectionTimeoutFuture()); Assert.assertTrue(connectionTimeoutFuture.isCancelled()); } @@ -408,7 +410,7 @@ public class OutboundMessagingConnectionTest omc.finishHandshake(result); Assert.assertNotNull(omc.getChannelWriter()); Assert.assertEquals(CREATING_CHANNEL, omc.getState()); - Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR.getAddress())); + Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR)); Assert.assertEquals(count, omc.backlogSize()); } @@ -423,7 +425,7 @@ public class OutboundMessagingConnectionTest HandshakeResult result = HandshakeResult.failed(); omc.finishHandshake(result); Assert.assertEquals(NOT_READY, omc.getState()); - Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR.getAddress())); + Assert.assertEquals(MESSAGING_VERSION, MessagingService.instance().getVersion(REMOTE_ADDR)); Assert.assertEquals(0, omc.backlogSize()); } @@ -512,8 +514,8 @@ public class OutboundMessagingConnectionTest OutboundConnectionIdentifier connectionId = omc.getConnectionId(); omc.maybeUpdateConnectionId(); Assert.assertNotEquals(connectionId, omc.getConnectionId()); - Assert.assertEquals(new InetSocketAddress(REMOTE_ADDR.getAddress(), DatabaseDescriptor.getSSLStoragePort()), omc.getConnectionId().remoteAddress()); - Assert.assertEquals(new InetSocketAddress(REMOTE_ADDR.getAddress(), DatabaseDescriptor.getSSLStoragePort()), omc.getConnectionId().connectionAddress()); + Assert.assertEquals(InetAddressAndPort.getByAddressOverrideDefaults(REMOTE_ADDR.address, DatabaseDescriptor.getSSLStoragePort()), omc.getConnectionId().remote()); + Assert.assertEquals(InetAddressAndPort.getByAddressOverrideDefaults(REMOTE_ADDR.address, DatabaseDescriptor.getSSLStoragePort()), omc.getConnectionId().connectionAddress()); Assert.assertEquals(peerVersion, omc.getTargetVersion()); } } diff --git a/test/unit/org/apache/cassandra/net/async/OutboundMessagingPoolTest.java b/test/unit/org/apache/cassandra/net/async/OutboundMessagingPoolTest.java index 655cd15cc2..ecd8697ff3 100644 --- a/test/unit/org/apache/cassandra/net/async/OutboundMessagingPoolTest.java +++ b/test/unit/org/apache/cassandra/net/async/OutboundMessagingPoolTest.java @@ -18,10 +18,10 @@ package org.apache.cassandra.net.async; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,6 +35,7 @@ import org.apache.cassandra.gms.GossipDigestSyn; import org.apache.cassandra.io.IVersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.BackPressureState; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; @@ -42,9 +43,9 @@ import org.apache.cassandra.net.async.OutboundConnectionIdentifier.ConnectionTyp public class OutboundMessagingPoolTest { - private static final InetSocketAddress LOCAL_ADDR = new InetSocketAddress("127.0.0.1", 9476); - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 9476); - private static final InetSocketAddress RECONNECT_ADDR = new InetSocketAddress("127.0.0.3", 9476); + private static final InetAddressAndPort LOCAL_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.1"), 9476); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 9476); + private static final InetAddressAndPort RECONNECT_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.3"), 9476); private static final List INTERNODE_MESSAGING_CONN_TYPES = new ArrayList() {{ add(ConnectionType.GOSSIP); add(ConnectionType.LARGE_MESSAGE); add(ConnectionType.SMALL_MESSAGE); }}; @@ -59,7 +60,7 @@ public class OutboundMessagingPoolTest @Before public void setup() { - BackPressureState backPressureState = DatabaseDescriptor.getBackPressureStrategy().newState(REMOTE_ADDR.getAddress()); + BackPressureState backPressureState = DatabaseDescriptor.getBackPressureStrategy().newState(REMOTE_ADDR); pool = new OutboundMessagingPool(REMOTE_ADDR, LOCAL_ADDR, null, backPressureState, new AllowAllInternodeAuthenticator()); } diff --git a/test/unit/org/apache/cassandra/repair/AbstractRepairTest.java b/test/unit/org/apache/cassandra/repair/AbstractRepairTest.java index 21c51c6992..970e648bde 100644 --- a/test/unit/org/apache/cassandra/repair/AbstractRepairTest.java +++ b/test/unit/org/apache/cassandra/repair/AbstractRepairTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Set; import java.util.UUID; @@ -33,6 +32,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.ActiveRepairService; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.utils.ByteBufferUtil; @@ -41,19 +41,19 @@ import org.apache.cassandra.utils.UUIDGen; @Ignore public abstract class AbstractRepairTest { - protected static final InetAddress COORDINATOR; - protected static final InetAddress PARTICIPANT1; - protected static final InetAddress PARTICIPANT2; - protected static final InetAddress PARTICIPANT3; + protected static final InetAddressAndPort COORDINATOR; + protected static final InetAddressAndPort PARTICIPANT1; + protected static final InetAddressAndPort PARTICIPANT2; + protected static final InetAddressAndPort PARTICIPANT3; static { try { - COORDINATOR = InetAddress.getByName("10.0.0.1"); - PARTICIPANT1 = InetAddress.getByName("10.0.0.1"); - PARTICIPANT2 = InetAddress.getByName("10.0.0.2"); - PARTICIPANT3 = InetAddress.getByName("10.0.0.3"); + COORDINATOR = InetAddressAndPort.getByName("10.0.0.1"); + PARTICIPANT1 = InetAddressAndPort.getByName("10.0.0.1"); + PARTICIPANT2 = InetAddressAndPort.getByName("10.0.0.2"); + PARTICIPANT3 = InetAddressAndPort.getByName("10.0.0.3"); } catch (UnknownHostException e) { @@ -64,7 +64,7 @@ public abstract class AbstractRepairTest DatabaseDescriptor.daemonInitialization(); } - protected static final Set PARTICIPANTS = ImmutableSet.of(PARTICIPANT1, PARTICIPANT2, PARTICIPANT3); + protected static final Set PARTICIPANTS = ImmutableSet.of(PARTICIPANT1, PARTICIPANT2, PARTICIPANT3); protected static Token t(int v) { diff --git a/test/unit/org/apache/cassandra/repair/LocalSyncTaskTest.java b/test/unit/org/apache/cassandra/repair/LocalSyncTaskTest.java index 7f3dbffa78..95046bd462 100644 --- a/test/unit/org/apache/cassandra/repair/LocalSyncTaskTest.java +++ b/test/unit/org/apache/cassandra/repair/LocalSyncTaskTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -36,6 +35,7 @@ import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.TableId; @@ -78,8 +78,8 @@ public class LocalSyncTaskTest extends AbstractRepairTest @Test public void testNoDifference() throws Throwable { - final InetAddress ep1 = InetAddress.getByName("127.0.0.1"); - final InetAddress ep2 = InetAddress.getByName("127.0.0.1"); + final InetAddressAndPort ep1 = InetAddressAndPort.getByName("127.0.0.1"); + final InetAddressAndPort ep2 = InetAddressAndPort.getByName("127.0.0.1"); Range range = new Range<>(partitioner.getMinimumToken(), partitioner.getRandomToken()); RepairJobDesc desc = new RepairJobDesc(UUID.randomUUID(), UUID.randomUUID(), KEYSPACE1, "Standard1", Arrays.asList(range)); @@ -106,7 +106,7 @@ public class LocalSyncTaskTest extends AbstractRepairTest Keyspace keyspace = Keyspace.open(KEYSPACE1); ColumnFamilyStore cfs = keyspace.getColumnFamilyStore("Standard1"); - ActiveRepairService.instance.registerParentRepairSession(parentRepairSession, FBUtilities.getBroadcastAddress(), + ActiveRepairService.instance.registerParentRepairSession(parentRepairSession, FBUtilities.getBroadcastAddressAndPort(), Arrays.asList(cfs), Arrays.asList(range), false, ActiveRepairService.UNREPAIRED_SSTABLE, false, PreviewKind.NONE); @@ -128,8 +128,8 @@ public class LocalSyncTaskTest extends AbstractRepairTest // difference the trees // note: we reuse the same endpoint which is bogus in theory but fine here - TreeResponse r1 = new TreeResponse(InetAddress.getByName("127.0.0.1"), tree1); - TreeResponse r2 = new TreeResponse(InetAddress.getByName("127.0.0.2"), tree2); + TreeResponse r1 = new TreeResponse(InetAddressAndPort.getByName("127.0.0.1"), tree1); + TreeResponse r2 = new TreeResponse(InetAddressAndPort.getByName("127.0.0.2"), tree2); LocalSyncTask task = new LocalSyncTask(desc, r1, r2, NO_PENDING_REPAIR, false, PreviewKind.NONE); task.run(); diff --git a/test/unit/org/apache/cassandra/repair/RepairRunnableTest.java b/test/unit/org/apache/cassandra/repair/RepairRunnableTest.java index db76f73ee5..2044106882 100644 --- a/test/unit/org/apache/cassandra/repair/RepairRunnableTest.java +++ b/test/unit/org/apache/cassandra/repair/RepairRunnableTest.java @@ -28,6 +28,7 @@ import com.google.common.collect.Sets; import org.junit.Assert; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.RepairRunnable.CommonRange; import static org.apache.cassandra.repair.RepairRunnable.filterCommonRanges; @@ -53,7 +54,7 @@ public class RepairRunnableTest extends AbstractRepairTest { CommonRange cr1 = new CommonRange(Sets.newHashSet(PARTICIPANT1, PARTICIPANT2), Sets.newHashSet(RANGE1, RANGE2)); CommonRange cr2 = new CommonRange(Sets.newHashSet(PARTICIPANT1, PARTICIPANT2, PARTICIPANT3), Sets.newHashSet(RANGE3)); - Set liveEndpoints = Sets.newHashSet(PARTICIPANT2, PARTICIPANT3); // PARTICIPANT1 is excluded + Set liveEndpoints = Sets.newHashSet(PARTICIPANT2, PARTICIPANT3); // PARTICIPANT1 is excluded List initial = Lists.newArrayList(cr1, cr2); List expected = Lists.newArrayList(new CommonRange(Sets.newHashSet(PARTICIPANT2), Sets.newHashSet(RANGE1, RANGE2)), diff --git a/test/unit/org/apache/cassandra/repair/RepairSessionTest.java b/test/unit/org/apache/cassandra/repair/RepairSessionTest.java index 984218d9cd..54f0511f05 100644 --- a/test/unit/org/apache/cassandra/repair/RepairSessionTest.java +++ b/test/unit/org/apache/cassandra/repair/RepairSessionTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.repair; import java.io.IOException; -import java.net.InetAddress; import java.util.Arrays; import java.util.Set; import java.util.UUID; @@ -35,7 +34,7 @@ import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.gms.Gossiper; -import org.apache.cassandra.service.ActiveRepairService; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.UUIDGen; @@ -54,7 +53,7 @@ public class RepairSessionTest @Test public void testConviction() throws Exception { - InetAddress remote = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort remote = InetAddressAndPort.getByName("127.0.0.2"); Gossiper.instance.initializeNodeUnsafe(remote, UUID.randomUUID(), 1); // Set up RepairSession @@ -62,7 +61,7 @@ public class RepairSessionTest UUID sessionId = UUID.randomUUID(); IPartitioner p = Murmur3Partitioner.instance; Range repairRange = new Range<>(p.getToken(ByteBufferUtil.bytes(0)), p.getToken(ByteBufferUtil.bytes(100))); - Set endpoints = Sets.newHashSet(remote); + Set endpoints = Sets.newHashSet(remote); RepairSession session = new RepairSession(parentSessionId, sessionId, Arrays.asList(repairRange), "Keyspace1", RepairParallelism.SEQUENTIAL, endpoints, false, false, false, diff --git a/test/unit/org/apache/cassandra/repair/ValidatorTest.java b/test/unit/org/apache/cassandra/repair/ValidatorTest.java index b45edc10ca..322772abd8 100644 --- a/test/unit/org/apache/cassandra/repair/ValidatorTest.java +++ b/test/unit/org/apache/cassandra/repair/ValidatorTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.repair; -import java.net.InetAddress; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -34,6 +33,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.BufferDecoratedKey; import org.apache.cassandra.db.ColumnFamilyStore; @@ -95,7 +95,7 @@ public class ValidatorTest final CompletableFuture outgoingMessageSink = registerOutgoingMessageSink(); - InetAddress remote = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort remote = InetAddressAndPort.getByName("127.0.0.2"); ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(columnFamily); @@ -134,7 +134,7 @@ public class ValidatorTest final CompletableFuture outgoingMessageSink = registerOutgoingMessageSink(); - InetAddress remote = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort remote = InetAddressAndPort.getByName("127.0.0.2"); Validator validator = new Validator(desc, remote, 0, PreviewKind.NONE); validator.fail(); @@ -189,12 +189,12 @@ public class ValidatorTest cfs.getTableName(), Collections.singletonList(new Range<>(sstable.first.getToken(), sstable.last.getToken()))); - ActiveRepairService.instance.registerParentRepairSession(repairSessionId, FBUtilities.getBroadcastAddress(), + ActiveRepairService.instance.registerParentRepairSession(repairSessionId, FBUtilities.getBroadcastAddressAndPort(), Collections.singletonList(cfs), desc.ranges, false, ActiveRepairService.UNREPAIRED_SSTABLE, false, PreviewKind.NONE); final CompletableFuture outgoingMessageSink = registerOutgoingMessageSink(); - Validator validator = new Validator(desc, FBUtilities.getBroadcastAddress(), 0, true, false, PreviewKind.NONE); + Validator validator = new Validator(desc, FBUtilities.getBroadcastAddressAndPort(), 0, true, false, PreviewKind.NONE); CompactionManager.instance.submitValidation(cfs, validator); MessageOut message = outgoingMessageSink.get(TEST_TIMEOUT, TimeUnit.SECONDS); @@ -218,7 +218,7 @@ public class ValidatorTest final CompletableFuture future = new CompletableFuture<>(); MessagingService.instance().addMessageSink(new IMessageSink() { - public boolean allowOutgoingMessage(MessageOut message, int id, InetAddress to) + public boolean allowOutgoingMessage(MessageOut message, int id, InetAddressAndPort to) { future.complete(message); return false; diff --git a/test/unit/org/apache/cassandra/repair/asymmetric/DifferenceHolderTest.java b/test/unit/org/apache/cassandra/repair/asymmetric/DifferenceHolderTest.java index 52a43e6684..96930108d6 100644 --- a/test/unit/org/apache/cassandra/repair/asymmetric/DifferenceHolderTest.java +++ b/test/unit/org/apache/cassandra/repair/asymmetric/DifferenceHolderTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Iterator; @@ -30,6 +29,7 @@ import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.TreeResponse; import org.apache.cassandra.utils.MerkleTree; import org.apache.cassandra.utils.MerkleTrees; @@ -43,8 +43,8 @@ public class DifferenceHolderTest @Test public void testFromEmptyMerkleTrees() throws UnknownHostException { - InetAddress a1 = InetAddress.getByName("127.0.0.1"); - InetAddress a2 = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort a1 = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort a2 = InetAddressAndPort.getByName("127.0.0.2"); MerkleTrees mt1 = new MerkleTrees(Murmur3Partitioner.instance); MerkleTrees mt2 = new MerkleTrees(Murmur3Partitioner.instance); @@ -64,8 +64,8 @@ public class DifferenceHolderTest IPartitioner partitioner = Murmur3Partitioner.instance; Range fullRange = new Range<>(partitioner.getMinimumToken(), partitioner.getMinimumToken()); int maxsize = 16; - InetAddress a1 = InetAddress.getByName("127.0.0.1"); - InetAddress a2 = InetAddress.getByName("127.0.0.2"); + InetAddressAndPort a1 = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort a2 = InetAddressAndPort.getByName("127.0.0.2"); // merkle tree building stolen from MerkleTreesTest: MerkleTrees mt1 = new MerkleTrees(partitioner); MerkleTrees mt2 = new MerkleTrees(partitioner); diff --git a/test/unit/org/apache/cassandra/repair/asymmetric/ReduceHelperTest.java b/test/unit/org/apache/cassandra/repair/asymmetric/ReduceHelperTest.java index 19c42fb9cb..6c64b1af72 100644 --- a/test/unit/org/apache/cassandra/repair/asymmetric/ReduceHelperTest.java +++ b/test/unit/org/apache/cassandra/repair/asymmetric/ReduceHelperTest.java @@ -19,11 +19,9 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -33,13 +31,13 @@ import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import org.junit.Test; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import static junit.framework.TestCase.fail; import static org.junit.Assert.assertEquals; @@ -47,24 +45,24 @@ import static org.junit.Assert.assertTrue; public class ReduceHelperTest { - private static final InetAddress[] addresses; - private static final InetAddress A; - private static final InetAddress B; - private static final InetAddress C; - private static final InetAddress D; - private static final InetAddress E; + private static final InetAddressAndPort[] addresses; + private static final InetAddressAndPort A; + private static final InetAddressAndPort B; + private static final InetAddressAndPort C; + private static final InetAddressAndPort D; + private static final InetAddressAndPort E; static { try { - A = InetAddress.getByName("127.0.0.0"); - B = InetAddress.getByName("127.0.0.1"); - C = InetAddress.getByName("127.0.0.2"); - D = InetAddress.getByName("127.0.0.3"); - E = InetAddress.getByName("127.0.0.4"); + A = InetAddressAndPort.getByName("127.0.0.0"); + B = InetAddressAndPort.getByName("127.0.0.1"); + C = InetAddressAndPort.getByName("127.0.0.2"); + D = InetAddressAndPort.getByName("127.0.0.3"); + E = InetAddressAndPort.getByName("127.0.0.4"); // for diff creation in loops: - addresses = new InetAddress[]{ A, B, C, D, E }; + addresses = new InetAddressAndPort[]{ A, B, C, D, E }; } catch (UnknownHostException e) { @@ -89,7 +87,7 @@ public class ReduceHelperTest C x x D = */ - Map differences = new HashMap<>(); + Map differences = new HashMap<>(); for (int i = 0; i < 4; i++) { HostDifferences hostDiffs = new HostDifferences(); @@ -105,7 +103,7 @@ public class ReduceHelperTest } DifferenceHolder differenceHolder = new DifferenceHolder(differences); - Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); + Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); assertEquals(set(set(C), set(E,D)), streams(tracker.get(A))); assertEquals(set(set(C), set(E,D)), streams(tracker.get(B))); @@ -113,7 +111,7 @@ public class ReduceHelperTest assertEquals(set(set(A,B), set(C)), streams(tracker.get(D))); assertEquals(set(set(A,B), set(C)), streams(tracker.get(E))); - ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x,y) -> y); + ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x, y) -> y); HostDifferences n0 = reduced.get(A); assertEquals(0, n0.get(A).size()); @@ -163,7 +161,7 @@ public class ReduceHelperTest C x x D = */ - Map differences = new HashMap<>(); + Map differences = new HashMap<>(); for (int i = 0; i < 4; i++) { HostDifferences hostDifferences = new HostDifferences(); @@ -179,7 +177,7 @@ public class ReduceHelperTest } DifferenceHolder differenceHolder = new DifferenceHolder(differences); - Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); + Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); assertEquals(set(set(C), set(E, D)), streams(tracker.get(A))); assertEquals(set(set(C), set(E, D)), streams(tracker.get(B))); assertEquals(set(set(A, B), set(E, D)), streams(tracker.get(C))); @@ -187,7 +185,7 @@ public class ReduceHelperTest assertEquals(set(set(A, B), set(C)), streams(tracker.get(E))); // if there is an option, never stream from node 1: - ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x,y) -> Sets.difference(y, set(B))); + ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x,y) -> Sets.difference(y, set(B))); HostDifferences n0 = reduced.get(A); assertEquals(0, n0.get(A).size()); @@ -223,7 +221,7 @@ public class ReduceHelperTest assertEquals(0, n4.get(E).size()); } - private Iterable> streams(IncomingRepairStreamTracker incomingRepairStreamTracker) + private Iterable> streams(IncomingRepairStreamTracker incomingRepairStreamTracker) { return incomingRepairStreamTracker.getIncoming().values().iterator().next().allStreams(); } @@ -248,12 +246,12 @@ public class ReduceHelperTest B streams (0, 50] from {C}, (50, 100] from {A, C} C streams (0, 50] from {A, B}, (50, 100] from B */ - Map differences = new HashMap<>(); + Map differences = new HashMap<>(); addDifference(A, differences, B, list(range(50, 100))); addDifference(A, differences, C, list(range(0, 50))); addDifference(B, differences, C, list(range(0, 100))); DifferenceHolder differenceHolder = new DifferenceHolder(differences); - Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); + Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); assertEquals(set(set(C)), tracker.get(A).getIncoming().get(range(0, 50)).allStreams()); assertEquals(set(set(B)), tracker.get(A).getIncoming().get(range(50, 100)).allStreams()); assertEquals(set(set(C)), tracker.get(B).getIncoming().get(range(0, 50)).allStreams()); @@ -261,7 +259,7 @@ public class ReduceHelperTest assertEquals(set(set(A,B)), tracker.get(C).getIncoming().get(range(0, 50)).allStreams()); assertEquals(set(set(B)), tracker.get(C).getIncoming().get(range(50, 100)).allStreams()); - ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x, y) -> y); + ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x, y) -> y); HostDifferences n0 = reduced.get(A); @@ -270,7 +268,7 @@ public class ReduceHelperTest HostDifferences n1 = reduced.get(B); assertEquals(0, n1.get(B).size()); - if (n1.get(A) != null) + if (!n1.get(A).isEmpty()) { assertTrue(n1.get(C).equals(list(range(0, 50)))); assertTrue(n1.get(A).equals(list(range(50, 100)))); @@ -281,7 +279,7 @@ public class ReduceHelperTest } HostDifferences n2 = reduced.get(C); assertEquals(0, n2.get(C).size()); - if (n2.get(A) != null) + if (!n2.get(A).isEmpty()) { assertTrue(n2.get(A).equals(list(range(0,50)))); assertTrue(n2.get(B).equals(list(range(50, 100)))); @@ -312,13 +310,13 @@ public class ReduceHelperTest B == C on (5, 10], (40, 45] */ - Map differences = new HashMap<>(); + Map differences = new HashMap<>(); addDifference(A, differences, B, list(range(5, 45))); addDifference(A, differences, C, list(range(0, 10), range(40,50))); addDifference(B, differences, C, list(range(0, 5), range(10,40), range(45,50))); DifferenceHolder differenceHolder = new DifferenceHolder(differences); - Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); + Map tracker = ReduceHelper.createIncomingRepairStreamTrackers(differenceHolder); Map, StreamFromOptions> ranges = tracker.get(A).getIncoming(); assertEquals(5, ranges.size()); @@ -344,21 +342,21 @@ public class ReduceHelperTest assertEquals(set(set(B)), ranges.get(range(10, 40)).allStreams()); assertEquals(set(set(A)), ranges.get(range(40, 45)).allStreams()); assertEquals(set(set(A,B)), ranges.get(range(45, 50)).allStreams()); - ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x, y) -> y); + ImmutableMap reduced = ReduceHelper.reduce(differenceHolder, (x, y) -> y); assertNoOverlap(A, reduced.get(A), list(range(0, 50))); assertNoOverlap(B, reduced.get(B), list(range(0, 50))); assertNoOverlap(C, reduced.get(C), list(range(0, 50))); } - private void assertNoOverlap(InetAddress incomingNode, HostDifferences node, List> expectedAfterNormalize) + private void assertNoOverlap(InetAddressAndPort incomingNode, HostDifferences node, List> expectedAfterNormalize) { Set> allRanges = new HashSet<>(); - Set remoteNodes = Sets.newHashSet(A,B,C); + Set remoteNodes = Sets.newHashSet(A,B,C); remoteNodes.remove(incomingNode); - Iterator iter = remoteNodes.iterator(); + Iterator iter = remoteNodes.iterator(); allRanges.addAll(node.get(iter.next())); - InetAddress i = iter.next(); + InetAddressAndPort i = iter.next(); for (Range r : node.get(i)) { for (Range existing : allRanges) @@ -379,14 +377,14 @@ public class ReduceHelperTest return ranges; } - private static Set set(InetAddress ... elem) + private static Set set(InetAddressAndPort ... elem) { return Sets.newHashSet(elem); } @SafeVarargs - private static Set> set(Set ... elem) + private static Set> set(Set ... elem) { - Set> ret = Sets.newHashSet(); + Set> ret = Sets.newHashSet(); ret.addAll(Arrays.asList(elem)); return ret; } @@ -418,7 +416,7 @@ public class ReduceHelperTest assertTrue(r1.size() > 0 ^ r2.size() > 0); } - private void addDifference(InetAddress host1, Map differences, InetAddress host2, List> ranges) + private void addDifference(InetAddressAndPort host1, Map differences, InetAddressAndPort host2, List> ranges) { differences.computeIfAbsent(host1, (x) -> new HostDifferences()).add(host2, ranges); } diff --git a/test/unit/org/apache/cassandra/repair/asymmetric/StreamFromOptionsTest.java b/test/unit/org/apache/cassandra/repair/asymmetric/StreamFromOptionsTest.java index 3ba3cfed87..e2a7700dce 100644 --- a/test/unit/org/apache/cassandra/repair/asymmetric/StreamFromOptionsTest.java +++ b/test/unit/org/apache/cassandra/repair/asymmetric/StreamFromOptionsTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.asymmetric; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.HashSet; @@ -30,6 +29,7 @@ import org.junit.Test; import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -41,16 +41,16 @@ public class StreamFromOptionsTest public void addAllDiffingTest() throws UnknownHostException { StreamFromOptions sfo = new StreamFromOptions(new MockDiffs(true), range(0, 10)); - Set toAdd = new HashSet<>(); - toAdd.add(InetAddress.getByName("127.0.0.1")); - toAdd.add(InetAddress.getByName("127.0.0.2")); - toAdd.add(InetAddress.getByName("127.0.0.3")); + Set toAdd = new HashSet<>(); + toAdd.add(InetAddressAndPort.getByName("127.0.0.1")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.2")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.3")); toAdd.forEach(sfo::add); // if all added have differences, each set will contain a single host assertEquals(3, Iterables.size(sfo.allStreams())); - Set allStreams = new HashSet<>(); - for (Set streams : sfo.allStreams()) + Set allStreams = new HashSet<>(); + for (Set streams : sfo.allStreams()) { assertEquals(1, streams.size()); allStreams.addAll(streams); @@ -62,10 +62,10 @@ public class StreamFromOptionsTest public void addAllMatchingTest() throws UnknownHostException { StreamFromOptions sfo = new StreamFromOptions(new MockDiffs(false), range(0, 10)); - Set toAdd = new HashSet<>(); - toAdd.add(InetAddress.getByName("127.0.0.1")); - toAdd.add(InetAddress.getByName("127.0.0.2")); - toAdd.add(InetAddress.getByName("127.0.0.3")); + Set toAdd = new HashSet<>(); + toAdd.add(InetAddressAndPort.getByName("127.0.0.1")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.2")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.3")); toAdd.forEach(sfo::add); // if all added match, the set will contain all hosts @@ -83,10 +83,10 @@ public class StreamFromOptionsTest private void splitTestHelper(boolean diffing) throws UnknownHostException { StreamFromOptions sfo = new StreamFromOptions(new MockDiffs(diffing), range(0, 10)); - Set toAdd = new HashSet<>(); - toAdd.add(InetAddress.getByName("127.0.0.1")); - toAdd.add(InetAddress.getByName("127.0.0.2")); - toAdd.add(InetAddress.getByName("127.0.0.3")); + Set toAdd = new HashSet<>(); + toAdd.add(InetAddressAndPort.getByName("127.0.0.1")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.2")); + toAdd.add(InetAddressAndPort.getByName("127.0.0.3")); toAdd.forEach(sfo::add); StreamFromOptions sfo1 = sfo.copy(range(0, 5)); StreamFromOptions sfo2 = sfo.copy(range(5, 10)); @@ -95,8 +95,8 @@ public class StreamFromOptionsTest assertEquals(range(5, 10), sfo2.range); assertTrue(Iterables.elementsEqual(sfo1.allStreams(), sfo2.allStreams())); // verify the backing set is not shared between the copies: - sfo1.add(InetAddress.getByName("127.0.0.4")); - sfo2.add(InetAddress.getByName("127.0.0.5")); + sfo1.add(InetAddressAndPort.getByName("127.0.0.4")); + sfo2.add(InetAddressAndPort.getByName("127.0.0.5")); assertFalse(Iterables.elementsEqual(sfo1.allStreams(), sfo2.allStreams())); } @@ -116,7 +116,7 @@ public class StreamFromOptionsTest } @Override - public boolean hasDifferenceBetween(InetAddress node1, InetAddress node2, Range range) + public boolean hasDifferenceBetween(InetAddressAndPort node1, InetAddressAndPort node2, Range range) { return hasDifference; } diff --git a/test/unit/org/apache/cassandra/repair/consistent/AbstractConsistentSessionTest.java b/test/unit/org/apache/cassandra/repair/consistent/AbstractConsistentSessionTest.java index 367fea990a..45703289f2 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/AbstractConsistentSessionTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/AbstractConsistentSessionTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Set; import java.util.UUID; @@ -33,6 +32,7 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.service.ActiveRepairService; import org.apache.cassandra.utils.ByteBufferUtil; @@ -41,19 +41,19 @@ import org.apache.cassandra.utils.UUIDGen; @Ignore public abstract class AbstractConsistentSessionTest { - protected static final InetAddress COORDINATOR; - protected static final InetAddress PARTICIPANT1; - protected static final InetAddress PARTICIPANT2; - protected static final InetAddress PARTICIPANT3; + protected static final InetAddressAndPort COORDINATOR; + protected static final InetAddressAndPort PARTICIPANT1; + protected static final InetAddressAndPort PARTICIPANT2; + protected static final InetAddressAndPort PARTICIPANT3; static { try { - COORDINATOR = InetAddress.getByName("10.0.0.1"); - PARTICIPANT1 = InetAddress.getByName("10.0.0.1"); - PARTICIPANT2 = InetAddress.getByName("10.0.0.2"); - PARTICIPANT3 = InetAddress.getByName("10.0.0.3"); + COORDINATOR = InetAddressAndPort.getByName("10.0.0.1"); + PARTICIPANT1 = InetAddressAndPort.getByName("10.0.0.1"); + PARTICIPANT2 = InetAddressAndPort.getByName("10.0.0.2"); + PARTICIPANT3 = InetAddressAndPort.getByName("10.0.0.3"); } catch (UnknownHostException e) { @@ -64,7 +64,7 @@ public abstract class AbstractConsistentSessionTest DatabaseDescriptor.daemonInitialization(); } - protected static final Set PARTICIPANTS = ImmutableSet.of(PARTICIPANT1, PARTICIPANT2, PARTICIPANT3); + protected static final Set PARTICIPANTS = ImmutableSet.of(PARTICIPANT1, PARTICIPANT2, PARTICIPANT3); protected static Token t(int v) { diff --git a/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionTest.java b/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionTest.java index fb312c38df..5d054d3dfb 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -35,6 +34,7 @@ import com.google.common.util.concurrent.SettableFuture; import org.junit.Assert; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.AbstractRepairTest; import org.apache.cassandra.repair.RepairSessionResult; import org.apache.cassandra.repair.messages.FailSession; @@ -77,7 +77,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest return new RepairSessionResult(coordinator.sessionID, "ks", coordinator.ranges, null, false); } - private static void assertMessageSent(InstrumentedCoordinatorSession coordinator, InetAddress participant, RepairMessage expected) + private static void assertMessageSent(InstrumentedCoordinatorSession coordinator, InetAddressAndPort participant, RepairMessage expected) { Assert.assertTrue(coordinator.sentMessages.containsKey(participant)); Assert.assertEquals(1, coordinator.sentMessages.get(participant).size()); @@ -91,9 +91,9 @@ public class CoordinatorSessionTest extends AbstractRepairTest super(builder); } - Map> sentMessages = new HashMap<>(); + Map> sentMessages = new HashMap<>(); - protected void sendMessage(InetAddress destination, RepairMessage message) + protected void sendMessage(InetAddressAndPort destination, RepairMessage message) { if (!sentMessages.containsKey(destination)) { @@ -189,7 +189,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest coordinator.fail(); Assert.assertEquals(FAILED, coordinator.getState()); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { assertMessageSent(coordinator, participant, new FailSession(coordinator.sessionID)); } @@ -221,7 +221,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertTrue(coordinator.sentMessages.isEmpty()); ListenableFuture sessionResult = coordinator.execute(sessionSupplier, hasFailures); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new PrepareConsistentRequest(coordinator.sessionID, COORDINATOR, new HashSet<>(PARTICIPANTS)); @@ -254,7 +254,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest repairFuture.set(results); // propose messages should have been sent once all repair sessions completed successfully - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FinalizePropose(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); @@ -277,7 +277,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertTrue(coordinator.finalizeCommitCalled); Assert.assertEquals(ConsistentSession.State.FINALIZED, coordinator.getState()); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FinalizeCommit(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); @@ -304,7 +304,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertFalse(repairSubmitted.get()); Assert.assertTrue(coordinator.sentMessages.isEmpty()); ListenableFuture sessionResult = coordinator.execute(sessionSupplier, hasFailures); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { PrepareConsistentRequest expected = new PrepareConsistentRequest(coordinator.sessionID, COORDINATOR, new HashSet<>(PARTICIPANTS)); assertMessageSent(coordinator, participant, expected); @@ -339,7 +339,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertTrue(coordinator.failCalled); // all participants should have been notified of session failure - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FailSession(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); @@ -366,7 +366,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertFalse(repairSubmitted.get()); Assert.assertTrue(coordinator.sentMessages.isEmpty()); ListenableFuture sessionResult = coordinator.execute(sessionSupplier, hasFailures); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { PrepareConsistentRequest expected = new PrepareConsistentRequest(coordinator.sessionID, COORDINATOR, new HashSet<>(PARTICIPANTS)); assertMessageSent(coordinator, participant, expected); @@ -394,7 +394,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertFalse(repairSubmitted.get()); // all participants should have been notified of session failure - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FailSession(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); @@ -422,7 +422,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertTrue(coordinator.sentMessages.isEmpty()); ListenableFuture sessionResult = coordinator.execute(sessionSupplier, hasFailures); - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new PrepareConsistentRequest(coordinator.sessionID, COORDINATOR, new HashSet<>(PARTICIPANTS)); @@ -455,7 +455,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest repairFuture.set(results); // propose messages should have been sent once all repair sessions completed successfully - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FinalizePropose(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); @@ -481,7 +481,7 @@ public class CoordinatorSessionTest extends AbstractRepairTest Assert.assertEquals(ConsistentSession.State.FAILED, coordinator.getState()); // failure messages should have been sent to all participants - for (InetAddress participant : PARTICIPANTS) + for (InetAddressAndPort participant : PARTICIPANTS) { RepairMessage expected = new FailSession(coordinator.sessionID); assertMessageSent(coordinator, participant, expected); diff --git a/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionsTest.java b/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionsTest.java index b40e1850e6..9bf4270111 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionsTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/CoordinatorSessionsTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.Set; import java.util.UUID; @@ -29,6 +28,7 @@ import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.cql3.statements.CreateTableStatement; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.AbstractRepairTest; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.Schema; @@ -55,9 +55,9 @@ public class CoordinatorSessionsTest extends AbstractRepairTest } int prepareResponseCalls = 0; - InetAddress preparePeer = null; + InetAddressAndPort preparePeer = null; boolean prepareSuccess = false; - public synchronized void handlePrepareResponse(InetAddress participant, boolean success) + public synchronized void handlePrepareResponse(InetAddressAndPort participant, boolean success) { prepareResponseCalls++; preparePeer = participant; @@ -65,9 +65,9 @@ public class CoordinatorSessionsTest extends AbstractRepairTest } int finalizePromiseCalls = 0; - InetAddress promisePeer = null; + InetAddressAndPort promisePeer = null; boolean promiseSuccess = false; - public synchronized void handleFinalizePromise(InetAddress participant, boolean success) + public synchronized void handleFinalizePromise(InetAddressAndPort participant, boolean success) { finalizePromiseCalls++; promisePeer = participant; @@ -93,7 +93,7 @@ public class CoordinatorSessionsTest extends AbstractRepairTest return (InstrumentedCoordinatorSession) super.getSession(sessionId); } - public InstrumentedCoordinatorSession registerSession(UUID sessionId, Set peers) + public InstrumentedCoordinatorSession registerSession(UUID sessionId, Set peers) { return (InstrumentedCoordinatorSession) super.registerSession(sessionId, peers); } diff --git a/test/unit/org/apache/cassandra/repair/consistent/LocalSessionAccessor.java b/test/unit/org/apache/cassandra/repair/consistent/LocalSessionAccessor.java index 6808efe1f9..3ea888d5a6 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/LocalSessionAccessor.java +++ b/test/unit/org/apache/cassandra/repair/consistent/LocalSessionAccessor.java @@ -18,10 +18,10 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.Set; import java.util.UUID; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.service.ActiveRepairService; /** @@ -36,7 +36,7 @@ public class LocalSessionAccessor ARS.consistent.local.start(); } - public static void prepareUnsafe(UUID sessionID, InetAddress coordinator, Set peers) + public static void prepareUnsafe(UUID sessionID, InetAddressAndPort coordinator, Set peers) { ActiveRepairService.ParentRepairSession prs = ARS.getParentRepairSession(sessionID); assert prs != null; diff --git a/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java b/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java index 6e6d22223b..5fa43a90fa 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -42,6 +41,7 @@ import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.cql3.statements.CreateTableStatement; import org.apache.cassandra.repair.AbstractRepairTest; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.SchemaConstants; @@ -106,20 +106,20 @@ public class LocalSessionTest extends AbstractRepairTest } } - private static void assertNoMessagesSent(InstrumentedLocalSessions sessions, InetAddress to) + private static void assertNoMessagesSent(InstrumentedLocalSessions sessions, InetAddressAndPort to) { Assert.assertNull(sessions.sentMessages.get(to)); } - private static void assertMessagesSent(InstrumentedLocalSessions sessions, InetAddress to, RepairMessage... expected) + private static void assertMessagesSent(InstrumentedLocalSessions sessions, InetAddressAndPort to, RepairMessage... expected) { Assert.assertEquals(Lists.newArrayList(expected), sessions.sentMessages.get(to)); } static class InstrumentedLocalSessions extends LocalSessions { - Map> sentMessages = new HashMap<>(); - protected void sendMessage(InetAddress destination, RepairMessage message) + Map> sentMessages = new HashMap<>(); + protected void sendMessage(InetAddressAndPort destination, RepairMessage message) { if (!sentMessages.containsKey(destination)) { @@ -159,12 +159,13 @@ public class LocalSessionTest extends AbstractRepairTest return getSession(sessionID); } - protected InetAddress getBroadcastAddress() + @Override + protected InetAddressAndPort getBroadcastAddressAndPort() { return PARTICIPANT1; } - protected boolean isAlive(InetAddress address) + protected boolean isAlive(InetAddressAndPort address) { return true; } @@ -811,7 +812,7 @@ public class LocalSessionTest extends AbstractRepairTest sessions.start(); Assert.assertNotNull(sessions.getSession(session.sessionID)); - QueryProcessor.instance.executeInternal("DELETE participants FROM system.repairs WHERE parent_id=?", session.sessionID); + QueryProcessor.instance.executeInternal("DELETE participants, participants_wp FROM system.repairs WHERE parent_id=?", session.sessionID); sessions = new LocalSessions(); sessions.start(); diff --git a/test/unit/org/apache/cassandra/repair/consistent/PendingAntiCompactionTest.java b/test/unit/org/apache/cassandra/repair/consistent/PendingAntiCompactionTest.java index 5aeab3e01d..213cdd3c63 100644 --- a/test/unit/org/apache/cassandra/repair/consistent/PendingAntiCompactionTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/PendingAntiCompactionTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.repair.consistent; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -43,6 +42,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.statements.CreateTableStatement; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableMetadata; @@ -148,7 +148,7 @@ public class PendingAntiCompactionTest // create a session so the anti compaction can fine it UUID sessionID = UUIDGen.getTimeUUID(); - ActiveRepairService.instance.registerParentRepairSession(sessionID, InetAddress.getLocalHost(), Lists.newArrayList(cfs), ranges, true, 1, true, PreviewKind.NONE); + ActiveRepairService.instance.registerParentRepairSession(sessionID, InetAddressAndPort.getLocalHost(), Lists.newArrayList(cfs), ranges, true, 1, true, PreviewKind.NONE); PendingAntiCompaction pac; ExecutorService executor = Executors.newSingleThreadExecutor(); @@ -352,7 +352,7 @@ public class PendingAntiCompactionTest PendingAntiCompaction.AcquireResult result = acquisitionCallable.call(); UUID sessionID = UUIDGen.getTimeUUID(); ActiveRepairService.instance.registerParentRepairSession(sessionID, - InetAddress.getByName("127.0.0.1"), + InetAddressAndPort.getByName("127.0.0.1"), Lists.newArrayList(cfs), FULL_RANGE, true,0, diff --git a/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializationsTest.java b/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializationsTest.java index 2ef9446e70..e57ab94ae3 100644 --- a/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializationsTest.java +++ b/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializationsTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -44,6 +43,7 @@ import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputBufferFixed; import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.repair.NodePair; import org.apache.cassandra.streaming.PreviewKind; @@ -148,9 +148,9 @@ public class RepairMessageSerializationsTest @Test public void syncRequestMessage() throws IOException { - InetAddress initiator = InetAddress.getByName("127.0.0.1"); - InetAddress src = InetAddress.getByName("127.0.0.2"); - InetAddress dst = InetAddress.getByName("127.0.0.3"); + InetAddressAndPort initiator = InetAddressAndPort.getByName("127.0.0.1"); + InetAddressAndPort src = InetAddressAndPort.getByName("127.0.0.2"); + InetAddressAndPort dst = InetAddressAndPort.getByName("127.0.0.3"); SyncRequest msg = new SyncRequest(buildRepairJobDesc(), initiator, src, dst, buildTokenRanges(), PreviewKind.NONE); serializeRoundTrip(msg, SyncRequest.serializer); @@ -159,8 +159,8 @@ public class RepairMessageSerializationsTest @Test public void syncCompleteMessage() throws IOException { - InetAddress src = InetAddress.getByName("127.0.0.2"); - InetAddress dst = InetAddress.getByName("127.0.0.3"); + InetAddressAndPort src = InetAddressAndPort.getByName("127.0.0.2"); + InetAddressAndPort dst = InetAddressAndPort.getByName("127.0.0.3"); List summaries = new ArrayList<>(); summaries.add(new SessionSummary(src, dst, Lists.newArrayList(new StreamSummary(TableId.fromUUID(UUIDGen.getTimeUUID()), 5, 100)), diff --git a/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializerTest.java b/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializerTest.java index 9789b38f25..d876139a3f 100644 --- a/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializerTest.java +++ b/test/unit/org/apache/cassandra/repair/messages/RepairMessageSerializerTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.repair.messages; import java.io.IOException; -import java.net.InetAddress; import com.google.common.collect.Sets; import org.junit.Assert; @@ -27,6 +26,7 @@ import org.junit.Test; import org.apache.cassandra.io.util.DataInputBuffer; import org.apache.cassandra.io.util.DataOutputBuffer; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.utils.UUIDGen; @@ -58,10 +58,10 @@ public class RepairMessageSerializerTest @Test public void prepareConsistentRequest() throws Exception { - InetAddress coordinator = InetAddress.getByName("10.0.0.1"); - InetAddress peer1 = InetAddress.getByName("10.0.0.2"); - InetAddress peer2 = InetAddress.getByName("10.0.0.3"); - InetAddress peer3 = InetAddress.getByName("10.0.0.4"); + InetAddressAndPort coordinator = InetAddressAndPort.getByName("10.0.0.1"); + InetAddressAndPort peer1 = InetAddressAndPort.getByName("10.0.0.2"); + InetAddressAndPort peer2 = InetAddressAndPort.getByName("10.0.0.3"); + InetAddressAndPort peer3 = InetAddressAndPort.getByName("10.0.0.4"); RepairMessage expected = new PrepareConsistentRequest(UUIDGen.getTimeUUID(), coordinator, Sets.newHashSet(peer1, peer2, peer3)); @@ -73,7 +73,7 @@ public class RepairMessageSerializerTest public void prepareConsistentResponse() throws Exception { RepairMessage expected = new PrepareConsistentResponse(UUIDGen.getTimeUUID(), - InetAddress.getByName("10.0.0.2"), + InetAddressAndPort.getByName("10.0.0.2"), true); RepairMessage actual = serdes(expected); Assert.assertEquals(expected, actual); @@ -99,7 +99,7 @@ public class RepairMessageSerializerTest public void finalizePromise() throws Exception { RepairMessage expected = new FinalizePromise(UUIDGen.getTimeUUID(), - InetAddress.getByName("10.0.0.2"), + InetAddressAndPort.getByName("10.0.0.2"), true); RepairMessage actual = serdes(expected); Assert.assertEquals(expected, actual); diff --git a/test/unit/org/apache/cassandra/service/ActiveRepairServiceTest.java b/test/unit/org/apache/cassandra/service/ActiveRepairServiceTest.java index cbacaec7cb..76f915e92a 100644 --- a/test/unit/org/apache/cassandra/service/ActiveRepairServiceTest.java +++ b/test/unit/org/apache/cassandra/service/ActiveRepairServiceTest.java @@ -18,7 +18,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.*; import javax.xml.crypto.Data; @@ -43,6 +42,7 @@ import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.locator.AbstractReplicationStrategy; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.streaming.PreviewKind; @@ -69,7 +69,7 @@ public class ActiveRepairServiceTest public String cfname; public ColumnFamilyStore store; - public InetAddress LOCAL, REMOTE; + public InetAddressAndPort LOCAL, REMOTE; private boolean initialized; @@ -91,9 +91,9 @@ public class ActiveRepairServiceTest SchemaLoader.startGossiper(); initialized = true; - LOCAL = FBUtilities.getBroadcastAddress(); + LOCAL = FBUtilities.getBroadcastAddressAndPort(); // generate a fake endpoint for which we can spoof receiving/sending trees - REMOTE = InetAddress.getByName("127.0.0.2"); + REMOTE = InetAddressAndPort.getByName("127.0.0.2"); } TokenMetadata tmd = StorageService.instance.getTokenMetadata(); @@ -107,10 +107,10 @@ public class ActiveRepairServiceTest public void testGetNeighborsPlusOne() throws Throwable { // generate rf+1 nodes, and ensure that all nodes are returned - Set expected = addTokens(1 + Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); - expected.remove(FBUtilities.getBroadcastAddress()); + Set expected = addTokens(1 + Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); + expected.remove(FBUtilities.getBroadcastAddressAndPort()); Collection> ranges = StorageService.instance.getLocalRanges(KEYSPACE5); - Set neighbors = new HashSet<>(); + Set neighbors = new HashSet<>(); for (Range range : ranges) { neighbors.addAll(ActiveRepairService.getNeighbors(KEYSPACE5, ranges, range, null, null)); @@ -126,14 +126,14 @@ public class ActiveRepairServiceTest // generate rf*2 nodes, and ensure that only neighbors specified by the ARS are returned addTokens(2 * Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); AbstractReplicationStrategy ars = Keyspace.open(KEYSPACE5).getReplicationStrategy(); - Set expected = new HashSet<>(); - for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddress())) + Set expected = new HashSet<>(); + for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddressAndPort())) { expected.addAll(ars.getRangeAddresses(tmd.cloneOnlyTokenMap()).get(replicaRange)); } - expected.remove(FBUtilities.getBroadcastAddress()); + expected.remove(FBUtilities.getBroadcastAddressAndPort()); Collection> ranges = StorageService.instance.getLocalRanges(KEYSPACE5); - Set neighbors = new HashSet<>(); + Set neighbors = new HashSet<>(); for (Range range : ranges) { neighbors.addAll(ActiveRepairService.getNeighbors(KEYSPACE5, ranges, range, null, null)); @@ -147,15 +147,15 @@ public class ActiveRepairServiceTest TokenMetadata tmd = StorageService.instance.getTokenMetadata(); // generate rf+1 nodes, and ensure that all nodes are returned - Set expected = addTokens(1 + Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); - expected.remove(FBUtilities.getBroadcastAddress()); + Set expected = addTokens(1 + Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); + expected.remove(FBUtilities.getBroadcastAddressAndPort()); // remove remote endpoints TokenMetadata.Topology topology = tmd.cloneOnlyTokenMap().getTopology(); - HashSet localEndpoints = Sets.newHashSet(topology.getDatacenterEndpoints().get(DatabaseDescriptor.getLocalDataCenter())); + HashSet localEndpoints = Sets.newHashSet(topology.getDatacenterEndpoints().get(DatabaseDescriptor.getLocalDataCenter())); expected = Sets.intersection(expected, localEndpoints); Collection> ranges = StorageService.instance.getLocalRanges(KEYSPACE5); - Set neighbors = new HashSet<>(); + Set neighbors = new HashSet<>(); for (Range range : ranges) { neighbors.addAll(ActiveRepairService.getNeighbors(KEYSPACE5, ranges, range, Arrays.asList(DatabaseDescriptor.getLocalDataCenter()), null)); @@ -171,19 +171,19 @@ public class ActiveRepairServiceTest // generate rf*2 nodes, and ensure that only neighbors specified by the ARS are returned addTokens(2 * Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); AbstractReplicationStrategy ars = Keyspace.open(KEYSPACE5).getReplicationStrategy(); - Set expected = new HashSet<>(); - for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddress())) + Set expected = new HashSet<>(); + for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddressAndPort())) { expected.addAll(ars.getRangeAddresses(tmd.cloneOnlyTokenMap()).get(replicaRange)); } - expected.remove(FBUtilities.getBroadcastAddress()); + expected.remove(FBUtilities.getBroadcastAddressAndPort()); // remove remote endpoints TokenMetadata.Topology topology = tmd.cloneOnlyTokenMap().getTopology(); - HashSet localEndpoints = Sets.newHashSet(topology.getDatacenterEndpoints().get(DatabaseDescriptor.getLocalDataCenter())); + HashSet localEndpoints = Sets.newHashSet(topology.getDatacenterEndpoints().get(DatabaseDescriptor.getLocalDataCenter())); expected = Sets.intersection(expected, localEndpoints); Collection> ranges = StorageService.instance.getLocalRanges(KEYSPACE5); - Set neighbors = new HashSet<>(); + Set neighbors = new HashSet<>(); for (Range range : ranges) { neighbors.addAll(ActiveRepairService.getNeighbors(KEYSPACE5, ranges, range, Arrays.asList(DatabaseDescriptor.getLocalDataCenter()), null)); @@ -199,14 +199,14 @@ public class ActiveRepairServiceTest // generate rf*2 nodes, and ensure that only neighbors specified by the hosts are returned addTokens(2 * Keyspace.open(KEYSPACE5).getReplicationStrategy().getReplicationFactor()); AbstractReplicationStrategy ars = Keyspace.open(KEYSPACE5).getReplicationStrategy(); - List expected = new ArrayList<>(); - for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddress())) + List expected = new ArrayList<>(); + for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getBroadcastAddressAndPort())) { expected.addAll(ars.getRangeAddresses(tmd.cloneOnlyTokenMap()).get(replicaRange)); } - expected.remove(FBUtilities.getBroadcastAddress()); - Collection hosts = Arrays.asList(FBUtilities.getBroadcastAddress().getCanonicalHostName(),expected.get(0).getCanonicalHostName()); + expected.remove(FBUtilities.getBroadcastAddressAndPort()); + Collection hosts = Arrays.asList(FBUtilities.getBroadcastAddressAndPort().toString(),expected.get(0).toString()); Collection> ranges = StorageService.instance.getLocalRanges(KEYSPACE5); assertEquals(expected.get(0), ActiveRepairService.getNeighbors(KEYSPACE5, ranges, @@ -245,13 +245,13 @@ public class ActiveRepairServiceTest } - Set addTokens(int max) throws Throwable + Set addTokens(int max) throws Throwable { TokenMetadata tmd = StorageService.instance.getTokenMetadata(); - Set endpoints = new HashSet<>(); + Set endpoints = new HashSet<>(); for (int i = 1; i <= max; i++) { - InetAddress endpoint = InetAddress.getByName("127.0.0." + i); + InetAddressAndPort endpoint = InetAddressAndPort.getByName("127.0.0." + i); tmd.updateNormalToken(tmd.partitioner.getRandomToken(), endpoint); endpoints.add(endpoint); } @@ -264,14 +264,14 @@ public class ActiveRepairServiceTest ColumnFamilyStore store = prepareColumnFamilyStore(); UUID prsId = UUID.randomUUID(); Set original = Sets.newHashSet(store.select(View.select(SSTableSet.CANONICAL, (s) -> !s.isRepaired())).sstables); - ActiveRepairService.instance.registerParentRepairSession(prsId, FBUtilities.getBroadcastAddress(), Collections.singletonList(store), + ActiveRepairService.instance.registerParentRepairSession(prsId, FBUtilities.getBroadcastAddressAndPort(), Collections.singletonList(store), Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(), store.getPartitioner().getMinimumToken())), true, System.currentTimeMillis(), true, PreviewKind.NONE); ActiveRepairService.instance.getParentRepairSession(prsId).maybeSnapshot(store.metadata.id, prsId); UUID prsId2 = UUID.randomUUID(); - ActiveRepairService.instance.registerParentRepairSession(prsId2, FBUtilities.getBroadcastAddress(), + ActiveRepairService.instance.registerParentRepairSession(prsId2, FBUtilities.getBroadcastAddressAndPort(), Collections.singletonList(store), Collections.singleton(new Range<>(store.getPartitioner().getMinimumToken(), store.getPartitioner().getMinimumToken())), diff --git a/test/unit/org/apache/cassandra/service/DataResolverTest.java b/test/unit/org/apache/cassandra/service/DataResolverTest.java index ec25fefcee..f406582db2 100644 --- a/test/unit/org/apache/cassandra/service/DataResolverTest.java +++ b/test/unit/org/apache/cassandra/service/DataResolverTest.java @@ -18,7 +18,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.*; @@ -29,6 +28,7 @@ import org.junit.*; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.config.DatabaseDescriptor; @@ -150,11 +150,11 @@ public class DataResolverTest public void testResolveNewerSingleRow() throws UnknownHostException { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("1") .add("c1", "v1") .buildUpdate()))); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).clustering("1") .add("c1", "v2") .buildUpdate()))); @@ -182,12 +182,12 @@ public class DataResolverTest public void testResolveDisjointSingleRow() { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("1") .add("c1", "v1") .buildUpdate()))); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).clustering("1") .add("c2", "v2") .buildUpdate()))); @@ -220,11 +220,11 @@ public class DataResolverTest { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("1") .add("c1", "v1") .buildUpdate()))); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).clustering("2") .add("c2", "v2") .buildUpdate()))); @@ -274,25 +274,25 @@ public class DataResolverTest .addRangeTombstone(tombstone2) .buildUpdate(); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); UnfilteredPartitionIterator iter1 = iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1) .addRangeTombstone(tombstone2) .buildUpdate()); resolver.preprocess(readResponseMessage(peer1, iter1)); // not covered by any range tombstone - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); UnfilteredPartitionIterator iter2 = iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("0") .add("c1", "v0") .buildUpdate()); resolver.preprocess(readResponseMessage(peer2, iter2)); // covered by a range tombstone - InetAddress peer3 = peer(); + InetAddressAndPort peer3 = peer(); UnfilteredPartitionIterator iter3 = iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("10") .add("c2", "v1") .buildUpdate()); resolver.preprocess(readResponseMessage(peer3, iter3)); // range covered by rt, but newer - InetAddress peer4 = peer(); + InetAddressAndPort peer4 = peer(); UnfilteredPartitionIterator iter4 = iter(new RowUpdateBuilder(cfm, nowInSec, 2L, dk).clustering("3") .add("one", "A") .buildUpdate()); @@ -348,11 +348,11 @@ public class DataResolverTest public void testResolveWithOneEmpty() { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).clustering("1") .add("c2", "v2") .buildUpdate()))); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, EmptyIterators.unfilteredPartition(cfm))); try(PartitionIterator data = resolver.resolve()) @@ -395,11 +395,11 @@ public class DataResolverTest { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); // one response with columns timestamped before a delete in another response - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(new RowUpdateBuilder(cfm, nowInSec, 0L, dk).clustering("1") .add("one", "A") .buildUpdate()))); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, fullPartitionDelete(cfm, dk, 1, nowInSec))); try (PartitionIterator data = resolver.resolve()) @@ -421,20 +421,20 @@ public class DataResolverTest { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 4, System.nanoTime()); // deletes and columns with interleaved timestamp, with out of order return sequence - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, fullPartitionDelete(cfm, dk, 0, nowInSec))); // these columns created after the previous deletion - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(new RowUpdateBuilder(cfm, nowInSec, 1L, dk).clustering("1") .add("one", "A") .add("two", "A") .buildUpdate()))); //this column created after the next delete - InetAddress peer3 = peer(); + InetAddressAndPort peer3 = peer(); resolver.preprocess(readResponseMessage(peer3, iter(new RowUpdateBuilder(cfm, nowInSec, 3L, dk).clustering("1") .add("two", "B") .buildUpdate()))); - InetAddress peer4 = peer(); + InetAddressAndPort peer4 = peer(); resolver.preprocess(readResponseMessage(peer4, fullPartitionDelete(cfm, dk, 2, nowInSec))); try(PartitionIterator data = resolver.resolve()) @@ -505,8 +505,8 @@ public class DataResolverTest private void resolveRangeTombstonesOnBoundary(long timestamp1, long timestamp2) { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); - InetAddress peer2 = peer(); + InetAddressAndPort peer1 = peer(); + InetAddressAndPort peer2 = peer(); // 1st "stream" RangeTombstone one_two = tombstone("1", true , "2", false, timestamp1, nowInSec); @@ -579,8 +579,8 @@ public class DataResolverTest private void testRepairRangeTombstoneBoundary(int timestamp1, int timestamp2, int timestamp3) throws UnknownHostException { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); - InetAddress peer2 = peer(); + InetAddressAndPort peer1 = peer(); + InetAddressAndPort peer2 = peer(); // 1st "stream" RangeTombstone one_nine = tombstone("0", true , "9", true, timestamp1, nowInSec); @@ -631,8 +631,8 @@ public class DataResolverTest public void testRepairRangeTombstoneWithPartitionDeletion() { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); - InetAddress peer2 = peer(); + InetAddressAndPort peer1 = peer(); + InetAddressAndPort peer2 = peer(); // 1st "stream": just a partition deletion UnfilteredPartitionIterator iter1 = iter(PartitionUpdate.fullPartitionDelete(cfm, dk, 10, nowInSec)); @@ -670,8 +670,8 @@ public class DataResolverTest public void testRepairRangeTombstoneWithPartitionDeletion2() { DataResolver resolver = new DataResolver(ks, command, ConsistencyLevel.ALL, 2, System.nanoTime()); - InetAddress peer1 = peer(); - InetAddress peer2 = peer(); + InetAddressAndPort peer1 = peer(); + InetAddressAndPort peer2 = peer(); // 1st "stream": a partition deletion and a range tombstone RangeTombstone rt1 = tombstone("0", true , "9", true, 11, nowInSec); @@ -761,7 +761,7 @@ public class DataResolverTest builder.addComplexDeletion(m, new DeletionTime(ts[0] - 1, nowInSec)); builder.addCell(mapCell(0, 0, ts[0])); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); builder.newRow(Clustering.EMPTY); @@ -770,7 +770,7 @@ public class DataResolverTest Cell expectedCell = mapCell(1, 1, ts[1]); builder.addCell(expectedCell); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); try(PartitionIterator data = resolver.resolve()) @@ -814,14 +814,14 @@ public class DataResolverTest builder.addComplexDeletion(m, new DeletionTime(ts[0] - 1, nowInSec)); builder.addCell(mapCell(0, 0, ts[0])); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); builder.newRow(Clustering.EMPTY); DeletionTime expectedCmplxDelete = new DeletionTime(ts[1] - 1, nowInSec); builder.addComplexDeletion(m, expectedCmplxDelete); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); try(PartitionIterator data = resolver.resolve()) @@ -862,10 +862,10 @@ public class DataResolverTest builder.addCell(expectedCell); // empty map column - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(PartitionUpdate.emptyUpdate(cfm2, dk)))); try(PartitionIterator data = resolver.resolve()) @@ -908,7 +908,7 @@ public class DataResolverTest builder.newRow(Clustering.EMPTY); builder.addComplexDeletion(m, new DeletionTime(ts[0] - 1, nowInSec)); - InetAddress peer1 = peer(); + InetAddressAndPort peer1 = peer(); resolver.preprocess(readResponseMessage(peer1, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); // newer, overwritten map column @@ -918,7 +918,7 @@ public class DataResolverTest Cell expectedCell = mapCell(1, 1, ts[1]); builder.addCell(expectedCell); - InetAddress peer2 = peer(); + InetAddressAndPort peer2 = peer(); resolver.preprocess(readResponseMessage(peer2, iter(PartitionUpdate.singleRowUpdate(cfm2, dk, builder.build())), cmd)); try(PartitionIterator data = resolver.resolve()) @@ -945,11 +945,11 @@ public class DataResolverTest Assert.assertNull(messageRecorder.sent.get(peer2)); } - private InetAddress peer() + private InetAddressAndPort peer() { try { - return InetAddress.getByAddress(new byte[]{ 127, 0, 0, (byte) addressSuffix++ }); + return InetAddressAndPort.getByAddress(new byte[]{ 127, 0, 0, (byte) addressSuffix++ }); } catch (UnknownHostException e) { @@ -957,7 +957,7 @@ public class DataResolverTest } } - private MessageOut getSentMessage(InetAddress target) + private MessageOut getSentMessage(InetAddressAndPort target) { MessageOut message = messageRecorder.sent.get(target); assertNotNull(String.format("No repair message was sent to %s", target), message); @@ -1018,12 +1018,12 @@ public class DataResolverTest } - public MessageIn readResponseMessage(InetAddress from, UnfilteredPartitionIterator partitionIterator) + public MessageIn readResponseMessage(InetAddressAndPort from, UnfilteredPartitionIterator partitionIterator) { return readResponseMessage(from, partitionIterator, command); } - public MessageIn readResponseMessage(InetAddress from, UnfilteredPartitionIterator partitionIterator, ReadCommand cmd) + public MessageIn readResponseMessage(InetAddressAndPort from, UnfilteredPartitionIterator partitionIterator, ReadCommand cmd) { return MessageIn.create(from, ReadResponse.createRemoteDataResponse(partitionIterator, cmd), @@ -1080,8 +1080,8 @@ public class DataResolverTest private static class MessageRecorder implements IMessageSink { - Map sent = new HashMap<>(); - public boolean allowOutgoingMessage(MessageOut message, int id, InetAddress to) + Map sent = new HashMap<>(); + public boolean allowOutgoingMessage(MessageOut message, int id, InetAddressAndPort to) { sent.put(to, message); return false; diff --git a/test/unit/org/apache/cassandra/service/LeaveAndBootstrapTest.java b/test/unit/org/apache/cassandra/service/LeaveAndBootstrapTest.java index aaa259479c..8ddc4f03df 100644 --- a/test/unit/org/apache/cassandra/service/LeaveAndBootstrapTest.java +++ b/test/unit/org/apache/cassandra/service/LeaveAndBootstrapTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.service; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; @@ -34,6 +33,7 @@ import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.Util.PartitionerSwitcher; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.dht.IPartitioner; @@ -92,17 +92,17 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, RING_SIZE); - Map> expectedEndpoints = new HashMap>(); + Map> expectedEndpoints = new HashMap>(); for (String keyspaceName : Schema.instance.getNonLocalStrategyKeyspaces()) { for (Token token : keyTokens) { - List endpoints = new ArrayList(); + List endpoints = new ArrayList<>(); Iterator tokenIter = TokenMetadata.ringIterator(tmd.sortedTokens(), token, false); while (tokenIter.hasNext()) { @@ -113,6 +113,9 @@ public class LeaveAndBootstrapTest } // Third node leaves + ss.onChange(hosts.get(LEAVING_NODE), + ApplicationState.STATUS_WITH_PORT, + valueFactory.leaving(Collections.singleton(endpointTokens.get(LEAVING_NODE)))); ss.onChange(hosts.get(LEAVING_NODE), ApplicationState.STATUS, valueFactory.leaving(Collections.singleton(endpointTokens.get(LEAVING_NODE)))); @@ -129,8 +132,8 @@ public class LeaveAndBootstrapTest { int replicationFactor = strategy.getReplicationFactor(); - HashSet actual = new HashSet(tmd.getWriteEndpoints(token, keyspaceName, strategy.calculateNaturalEndpoints(token, tmd.cloneOnlyTokenMap()))); - HashSet expected = new HashSet(); + HashSet actual = new HashSet<>(tmd.getWriteEndpoints(token, keyspaceName, strategy.calculateNaturalEndpoints(token, tmd.cloneOnlyTokenMap()))); + HashSet expected = new HashSet<>(); for (int i = 0; i < replicationFactor; i++) { @@ -163,34 +166,39 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 10 nodes Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, RING_SIZE); // nodes 6, 8 and 9 leave - final int[] LEAVING = new int[] {6, 8, 9}; + final int[] LEAVING = new int[]{ 6, 8, 9 }; for (int leaving : LEAVING) + { + ss.onChange(hosts.get(leaving), + ApplicationState.STATUS_WITH_PORT, + valueFactory.leaving(Collections.singleton(endpointTokens.get(leaving)))); ss.onChange(hosts.get(leaving), ApplicationState.STATUS, valueFactory.leaving(Collections.singleton(endpointTokens.get(leaving)))); + } // boot two new nodes with keyTokens.get(5) and keyTokens.get(7) - InetAddress boot1 = InetAddress.getByName("127.0.1.1"); + InetAddressAndPort boot1 = InetAddressAndPort.getByName("127.0.1.1"); Gossiper.instance.initializeNodeUnsafe(boot1, UUID.randomUUID(), 1); Gossiper.instance.injectApplicationState(boot1, ApplicationState.TOKENS, valueFactory.tokens(Collections.singleton(keyTokens.get(5)))); ss.onChange(boot1, ApplicationState.STATUS, valueFactory.bootstrapping(Collections.singleton(keyTokens.get(5)))); - InetAddress boot2 = InetAddress.getByName("127.0.1.2"); + InetAddressAndPort boot2 = InetAddressAndPort.getByName("127.0.1.2"); Gossiper.instance.initializeNodeUnsafe(boot2, UUID.randomUUID(), 1); Gossiper.instance.injectApplicationState(boot2, ApplicationState.TOKENS, valueFactory.tokens(Collections.singleton(keyTokens.get(7)))); ss.onChange(boot2, ApplicationState.STATUS, valueFactory.bootstrapping(Collections.singleton(keyTokens.get(7)))); - Collection endpoints = null; + Collection endpoints = null; /* don't require test update every time a new keyspace is added to test/conf/cassandra.yaml */ Map keyspaceStrategyMap = new HashMap(); @@ -200,8 +208,8 @@ public class LeaveAndBootstrapTest } // pre-calculate the results. - Map> expectedEndpoints = new HashMap>(); - expectedEndpoints.put(KEYSPACE1, HashMultimap.create()); + Map> expectedEndpoints = new HashMap>(); + expectedEndpoints.put(KEYSPACE1, HashMultimap.create()); expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2")); expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3")); expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4")); @@ -212,7 +220,7 @@ public class LeaveAndBootstrapTest expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.1.2", "127.0.0.1")); expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.10", "127.0.0.1")); expectedEndpoints.get(KEYSPACE1).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.1")); - expectedEndpoints.put(KEYSPACE2, HashMultimap.create()); + expectedEndpoints.put(KEYSPACE2, HashMultimap.create()); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4")); @@ -223,7 +231,7 @@ public class LeaveAndBootstrapTest expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.1.2", "127.0.0.1")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.10", "127.0.0.1")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.1")); - expectedEndpoints.put(KEYSPACE3, HashMultimap.create()); + expectedEndpoints.put(KEYSPACE3, HashMultimap.create()); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.1.1", "127.0.0.8")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.0.8", "127.0.1.2", "127.0.0.1", "127.0.1.1")); @@ -234,7 +242,7 @@ public class LeaveAndBootstrapTest expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.1.2", "127.0.0.4", "127.0.0.5")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5")); - expectedEndpoints.put(KEYSPACE4, HashMultimap.create()); + expectedEndpoints.put(KEYSPACE4, HashMultimap.create()); expectedEndpoints.get(KEYSPACE4).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2", "127.0.0.3", "127.0.0.4")); expectedEndpoints.get(KEYSPACE4).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3", "127.0.0.4", "127.0.0.5")); expectedEndpoints.get(KEYSPACE4).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4", "127.0.0.5", "127.0.0.6")); @@ -463,7 +471,7 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 5 nodes @@ -540,7 +548,7 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 5 nodes @@ -582,7 +590,7 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 5 nodes @@ -632,7 +640,7 @@ public class LeaveAndBootstrapTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring of 6 nodes @@ -673,10 +681,10 @@ public class LeaveAndBootstrapTest // create a ring of 2 nodes ArrayList endpointTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); Util.createInitialRing(ss, partitioner, endpointTokens, new ArrayList(), hosts, new ArrayList(), 2); - InetAddress toRemove = hosts.get(1); + InetAddressAndPort toRemove = hosts.get(1); SystemKeyspace.updatePeerInfo(toRemove, "data_center", "dc42"); SystemKeyspace.updatePeerInfo(toRemove, "rack", "rack42"); assertEquals("rack42", SystemKeyspace.loadDcRackInfo().get(toRemove).get("rack")); @@ -698,18 +706,19 @@ public class LeaveAndBootstrapTest // create a ring of 1 node StorageService ss = StorageService.instance; VersionedValue.VersionedValueFactory valueFactory = new VersionedValue.VersionedValueFactory(partitioner); - Util.createInitialRing(ss, partitioner, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), 1); + Util.createInitialRing(ss, partitioner, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), 1); // make a REMOVING state change on a non-member endpoint; without the CASSANDRA-6564 fix, this // would result in an ArrayIndexOutOfBoundsException - ss.onChange(InetAddress.getByName("192.168.1.42"), ApplicationState.STATUS, valueFactory.removingNonlocal(UUID.randomUUID())); + ss.onChange(InetAddressAndPort.getByName("192.168.1.42"), ApplicationState.STATUS_WITH_PORT, valueFactory.removingNonlocal(UUID.randomUUID())); + ss.onChange(InetAddressAndPort.getByName("192.168.1.42"), ApplicationState.STATUS, valueFactory.removingNonlocal(UUID.randomUUID())); } - private static Collection makeAddrs(String... hosts) throws UnknownHostException + private static Collection makeAddrs(String... hosts) throws UnknownHostException { - ArrayList addrs = new ArrayList(hosts.length); + ArrayList addrs = new ArrayList<>(hosts.length); for (String host : hosts) - addrs.add(InetAddress.getByName(host)); + addrs.add(InetAddressAndPort.getByName(host)); return addrs; } diff --git a/test/unit/org/apache/cassandra/service/MoveTest.java b/test/unit/org/apache/cassandra/service/MoveTest.java index df4e29403a..7321fbaf8d 100644 --- a/test/unit/org/apache/cassandra/service/MoveTest.java +++ b/test/unit/org/apache/cassandra/service/MoveTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.service; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; @@ -30,6 +29,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.MigrationManager; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; @@ -115,7 +115,7 @@ public class MoveTest //Odd IPs are in DC1 and Even are in DC2. Endpoints upto .14 will have unique racks and // then will be same for a set of three. @Override - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { int ipLastPart = getIPLastPart(endpoint); if (ipLastPart <= 14) @@ -125,7 +125,7 @@ public class MoveTest } @Override - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { if (getIPLastPart(endpoint) % 2 == 0) return "DC2"; @@ -133,19 +133,19 @@ public class MoveTest return "DC1"; } - private int getIPLastPart(InetAddress endpoint) + private int getIPLastPart(InetAddressAndPort endpoint) { String str = endpoint.toString(); int index = str.lastIndexOf("."); - return Integer.parseInt(str.substring(index + 1).trim()); + return Integer.parseInt(str.substring(index + 1).trim().split(":")[0]); } }); final TokenMetadata tmd = StorageService.instance.getTokenMetadata(); tmd.clearUnsafe(); - tmd.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.0.0.1")); - tmd.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.0.0.2")); + tmd.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.2")); KeyspaceMetadata keyspace = KeyspaceMetadata.create(keyspaceName, KeyspaceParams.nts(configOptions(replicas)), @@ -181,7 +181,7 @@ public class MoveTest VersionedValue.VersionedValueFactory valueFactory = new VersionedValue.VersionedValueFactory(partitioner); ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); for(int i=0; i < RING_SIZE/2; i++) @@ -221,7 +221,7 @@ public class MoveTest VersionedValue.VersionedValueFactory valueFactory = new VersionedValue.VersionedValueFactory(partitioner); ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); for(int i=0; i < RING_SIZE/2; i++) @@ -312,7 +312,7 @@ public class MoveTest VersionedValue.VersionedValueFactory valueFactory = new VersionedValue.VersionedValueFactory(partitioner); ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, RING_SIZE); @@ -376,7 +376,7 @@ public class MoveTest VersionedValue.VersionedValueFactory valueFactory = new VersionedValue.VersionedValueFactory(partitioner); ArrayList endpointTokens = new ArrayList<>(); ArrayList keyTokens = new ArrayList<>(); - List hosts = new ArrayList<>(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList<>(); Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, RING_SIZE); @@ -478,38 +478,38 @@ public class MoveTest finishMove(hosts.get(MOVING_NODE), 30, tmd); } - private void moveHost(InetAddress host, int token, TokenMetadata tmd, VersionedValue.VersionedValueFactory valueFactory ) + private void moveHost(InetAddressAndPort host, int token, TokenMetadata tmd, VersionedValue.VersionedValueFactory valueFactory ) { StorageService.instance.onChange(host, ApplicationState.STATUS, valueFactory.moving(new BigIntegerToken(String.valueOf(token)))); PendingRangeCalculatorService.instance.blockUntilFinished(); assertTrue(tmd.isMoving(host)); } - private void finishMove(InetAddress host, int token, TokenMetadata tmd) + private void finishMove(InetAddressAndPort host, int token, TokenMetadata tmd) { tmd.removeFromMoving(host); assertTrue(!tmd.isMoving(host)); tmd.updateNormalToken(new BigIntegerToken(String.valueOf(token)), host); } - private Map.Entry, Collection> generatePendingMapEntry(int start, int end, String... endpoints) throws UnknownHostException + private Map.Entry, Collection> generatePendingMapEntry(int start, int end, String... endpoints) throws UnknownHostException { - Map, Collection> pendingRanges = new HashMap<>(); + Map, Collection> pendingRanges = new HashMap<>(); pendingRanges.put(generateRange(start, end), makeAddrs(endpoints)); return pendingRanges.entrySet().iterator().next(); } - private Map, Collection> generatePendingRanges(Map.Entry, Collection>... entries) + private Map, Collection> generatePendingRanges(Map.Entry, Collection>... entries) { - Map, Collection> pendingRanges = new HashMap<>(); - for(Map.Entry, Collection> entry : entries) + Map, Collection> pendingRanges = new HashMap<>(); + for(Map.Entry, Collection> entry : entries) { pendingRanges.put(entry.getKey(), entry.getValue()); } return pendingRanges; } - private void assertPendingRanges(TokenMetadata tmd, Map, Collection> pendingRanges, String keyspaceName) throws ConfigurationException + private void assertPendingRanges(TokenMetadata tmd, Map, Collection> pendingRanges, String keyspaceName) throws ConfigurationException { boolean keyspaceFound = false; for (String nonSystemKeyspaceName : Schema.instance.getNonLocalStrategyKeyspaces()) @@ -523,13 +523,13 @@ public class MoveTest assert keyspaceFound; } - private void assertMaps(Map, Collection> expected, PendingRangeMaps actual) + private void assertMaps(Map, Collection> expected, PendingRangeMaps actual) { int sizeOfActual = 0; - Iterator, List>> iterator = actual.iterator(); + Iterator, List>> iterator = actual.iterator(); while(iterator.hasNext()) { - Map.Entry, List> actualEntry = iterator.next(); + Map.Entry, List> actualEntry = iterator.next(); assertNotNull(expected.get(actualEntry.getKey())); assertEquals(new HashSet<>(expected.get(actualEntry.getKey())), new HashSet<>(actualEntry.getValue())); sizeOfActual++; @@ -554,15 +554,15 @@ public class MoveTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, hostIds, RING_SIZE); - Map> expectedEndpoints = new HashMap>(); + Map> expectedEndpoints = new HashMap<>(); for (Token token : keyTokens) { - List endpoints = new ArrayList(); + List endpoints = new ArrayList<>(); Iterator tokenIter = TokenMetadata.ringIterator(tmd.sortedTokens(), token, false); while (tokenIter.hasNext()) { @@ -591,8 +591,8 @@ public class MoveTest { int replicationFactor = strategy.getReplicationFactor(); - HashSet actual = new HashSet(tmd.getWriteEndpoints(token, keyspaceName, strategy.calculateNaturalEndpoints(token, tmd.cloneOnlyTokenMap()))); - HashSet expected = new HashSet(); + HashSet actual = new HashSet<>(tmd.getWriteEndpoints(token, keyspaceName, strategy.calculateNaturalEndpoints(token, tmd.cloneOnlyTokenMap()))); + HashSet expected = new HashSet<>(); for (int i = 0; i < replicationFactor; i++) { @@ -628,7 +628,7 @@ public class MoveTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 10 nodes @@ -648,13 +648,13 @@ public class MoveTest newTokens.put(movingIndex, newToken); } - Collection endpoints; + Collection endpoints; tmd = tmd.cloneAfterAllSettled(); ss.setTokenMetadataUnsafe(tmd); // boot two new nodes with keyTokens.get(5) and keyTokens.get(7) - InetAddress boot1 = InetAddress.getByName("127.0.1.1"); + InetAddressAndPort boot1 = InetAddressAndPort.getByName("127.0.1.1"); Gossiper.instance.initializeNodeUnsafe(boot1, UUID.randomUUID(), 1); Gossiper.instance.injectApplicationState(boot1, ApplicationState.TOKENS, valueFactory.tokens(Collections.singleton(keyTokens.get(5)))); ss.onChange(boot1, @@ -662,7 +662,7 @@ public class MoveTest valueFactory.bootstrapping(Collections.singleton(keyTokens.get(5)))); PendingRangeCalculatorService.instance.blockUntilFinished(); - InetAddress boot2 = InetAddress.getByName("127.0.1.2"); + InetAddressAndPort boot2 = InetAddressAndPort.getByName("127.0.1.2"); Gossiper.instance.initializeNodeUnsafe(boot2, UUID.randomUUID(), 1); Gossiper.instance.injectApplicationState(boot2, ApplicationState.TOKENS, valueFactory.tokens(Collections.singleton(keyTokens.get(7)))); ss.onChange(boot2, @@ -693,35 +693,35 @@ public class MoveTest * } */ - Multimap> keyspace1ranges = keyspaceStrategyMap.get(Simple_RF1_KeyspaceName).getAddressRanges(); - Collection> ranges1 = keyspace1ranges.get(InetAddress.getByName("127.0.0.1")); + Multimap> keyspace1ranges = keyspaceStrategyMap.get(Simple_RF1_KeyspaceName).getAddressRanges(); + Collection> ranges1 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.1")); assertEquals(1, collectionSize(ranges1)); assertEquals(generateRange(97, 0), ranges1.iterator().next()); - Collection> ranges2 = keyspace1ranges.get(InetAddress.getByName("127.0.0.2")); + Collection> ranges2 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.2")); assertEquals(1, collectionSize(ranges2)); assertEquals(generateRange(0, 10), ranges2.iterator().next()); - Collection> ranges3 = keyspace1ranges.get(InetAddress.getByName("127.0.0.3")); + Collection> ranges3 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.3")); assertEquals(1, collectionSize(ranges3)); assertEquals(generateRange(10, 20), ranges3.iterator().next()); - Collection> ranges4 = keyspace1ranges.get(InetAddress.getByName("127.0.0.4")); + Collection> ranges4 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.4")); assertEquals(1, collectionSize(ranges4)); assertEquals(generateRange(20, 30), ranges4.iterator().next()); - Collection> ranges5 = keyspace1ranges.get(InetAddress.getByName("127.0.0.5")); + Collection> ranges5 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.5")); assertEquals(1, collectionSize(ranges5)); assertEquals(generateRange(30, 40), ranges5.iterator().next()); - Collection> ranges6 = keyspace1ranges.get(InetAddress.getByName("127.0.0.6")); + Collection> ranges6 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.6")); assertEquals(1, collectionSize(ranges6)); assertEquals(generateRange(40, 50), ranges6.iterator().next()); - Collection> ranges7 = keyspace1ranges.get(InetAddress.getByName("127.0.0.7")); + Collection> ranges7 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.7")); assertEquals(1, collectionSize(ranges7)); assertEquals(generateRange(50, 67), ranges7.iterator().next()); - Collection> ranges8 = keyspace1ranges.get(InetAddress.getByName("127.0.0.8")); + Collection> ranges8 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.8")); assertEquals(1, collectionSize(ranges8)); assertEquals(generateRange(67, 70), ranges8.iterator().next()); - Collection> ranges9 = keyspace1ranges.get(InetAddress.getByName("127.0.0.9")); + Collection> ranges9 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.9")); assertEquals(1, collectionSize(ranges9)); assertEquals(generateRange(70, 87), ranges9.iterator().next()); - Collection> ranges10 = keyspace1ranges.get(InetAddress.getByName("127.0.0.10")); + Collection> ranges10 = keyspace1ranges.get(InetAddressAndPort.getByName("127.0.0.10")); assertEquals(1, collectionSize(ranges10)); assertEquals(generateRange(87, 97), ranges10.iterator().next()); @@ -742,35 +742,35 @@ public class MoveTest * } */ - Multimap> keyspace3ranges = keyspaceStrategyMap.get(KEYSPACE3).getAddressRanges(); - ranges1 = keyspace3ranges.get(InetAddress.getByName("127.0.0.1")); + Multimap> keyspace3ranges = keyspaceStrategyMap.get(KEYSPACE3).getAddressRanges(); + ranges1 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.1")); assertEquals(collectionSize(ranges1), 5); assertTrue(ranges1.equals(generateRanges(97, 0, 70, 87, 50, 67, 87, 97, 67, 70))); - ranges2 = keyspace3ranges.get(InetAddress.getByName("127.0.0.2")); + ranges2 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.2")); assertEquals(collectionSize(ranges2), 5); assertTrue(ranges2.equals(generateRanges(97, 0, 70, 87, 87, 97, 0, 10, 67, 70))); - ranges3 = keyspace3ranges.get(InetAddress.getByName("127.0.0.3")); + ranges3 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.3")); assertEquals(collectionSize(ranges3), 5); assertTrue(ranges3.equals(generateRanges(97, 0, 70, 87, 87, 97, 0, 10, 10, 20))); - ranges4 = keyspace3ranges.get(InetAddress.getByName("127.0.0.4")); + ranges4 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.4")); assertEquals(collectionSize(ranges4), 5); assertTrue(ranges4.equals(generateRanges(97, 0, 20, 30, 87, 97, 0, 10, 10, 20))); - ranges5 = keyspace3ranges.get(InetAddress.getByName("127.0.0.5")); + ranges5 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.5")); assertEquals(collectionSize(ranges5), 5); assertTrue(ranges5.equals(generateRanges(97, 0, 30, 40, 20, 30, 0, 10, 10, 20))); - ranges6 = keyspace3ranges.get(InetAddress.getByName("127.0.0.6")); + ranges6 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.6")); assertEquals(collectionSize(ranges6), 5); assertTrue(ranges6.equals(generateRanges(40, 50, 30, 40, 20, 30, 0, 10, 10, 20))); - ranges7 = keyspace3ranges.get(InetAddress.getByName("127.0.0.7")); + ranges7 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.7")); assertEquals(collectionSize(ranges7), 5); assertTrue(ranges7.equals(generateRanges(40, 50, 30, 40, 50, 67, 20, 30, 10, 20))); - ranges8 = keyspace3ranges.get(InetAddress.getByName("127.0.0.8")); + ranges8 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.8")); assertEquals(collectionSize(ranges8), 5); assertTrue(ranges8.equals(generateRanges(40, 50, 30, 40, 50, 67, 20, 30, 67, 70))); - ranges9 = keyspace3ranges.get(InetAddress.getByName("127.0.0.9")); + ranges9 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.9")); assertEquals(collectionSize(ranges9), 5); assertTrue(ranges9.equals(generateRanges(40, 50, 70, 87, 30, 40, 50, 67, 67, 70))); - ranges10 = keyspace3ranges.get(InetAddress.getByName("127.0.0.10")); + ranges10 = keyspace3ranges.get(InetAddressAndPort.getByName("127.0.0.10")); assertEquals(collectionSize(ranges10), 5); assertTrue(ranges10.equals(generateRanges(40, 50, 70, 87, 50, 67, 87, 97, 67, 70))); @@ -790,41 +790,41 @@ public class MoveTest * /127.0.0.10=[(70,87], (87,97], (67,70]] * } */ - Multimap> keyspace4ranges = keyspaceStrategyMap.get(Simple_RF3_KeyspaceName).getAddressRanges(); - ranges1 = keyspace4ranges.get(InetAddress.getByName("127.0.0.1")); + Multimap> keyspace4ranges = keyspaceStrategyMap.get(Simple_RF3_KeyspaceName).getAddressRanges(); + ranges1 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.1")); assertEquals(collectionSize(ranges1), 3); assertTrue(ranges1.equals(generateRanges(97, 0, 70, 87, 87, 97))); - ranges2 = keyspace4ranges.get(InetAddress.getByName("127.0.0.2")); + ranges2 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.2")); assertEquals(collectionSize(ranges2), 3); assertTrue(ranges2.equals(generateRanges(97, 0, 87, 97, 0, 10))); - ranges3 = keyspace4ranges.get(InetAddress.getByName("127.0.0.3")); + ranges3 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.3")); assertEquals(collectionSize(ranges3), 3); assertTrue(ranges3.equals(generateRanges(97, 0, 0, 10, 10, 20))); - ranges4 = keyspace4ranges.get(InetAddress.getByName("127.0.0.4")); + ranges4 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.4")); assertEquals(collectionSize(ranges4), 3); assertTrue(ranges4.equals(generateRanges(20, 30, 0, 10, 10, 20))); - ranges5 = keyspace4ranges.get(InetAddress.getByName("127.0.0.5")); + ranges5 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.5")); assertEquals(collectionSize(ranges5), 3); assertTrue(ranges5.equals(generateRanges(30, 40, 20, 30, 10, 20))); - ranges6 = keyspace4ranges.get(InetAddress.getByName("127.0.0.6")); + ranges6 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.6")); assertEquals(collectionSize(ranges6), 3); assertTrue(ranges6.equals(generateRanges(40, 50, 30, 40, 20, 30))); - ranges7 = keyspace4ranges.get(InetAddress.getByName("127.0.0.7")); + ranges7 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.7")); assertEquals(collectionSize(ranges7), 3); assertTrue(ranges7.equals(generateRanges(40, 50, 30, 40, 50, 67))); - ranges8 = keyspace4ranges.get(InetAddress.getByName("127.0.0.8")); + ranges8 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.8")); assertEquals(collectionSize(ranges8), 3); assertTrue(ranges8.equals(generateRanges(40, 50, 50, 67, 67, 70))); - ranges9 = keyspace4ranges.get(InetAddress.getByName("127.0.0.9")); + ranges9 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.9")); assertEquals(collectionSize(ranges9), 3); assertTrue(ranges9.equals(generateRanges(70, 87, 50, 67, 67, 70))); - ranges10 = keyspace4ranges.get(InetAddress.getByName("127.0.0.10")); + ranges10 = keyspace4ranges.get(InetAddressAndPort.getByName("127.0.0.10")); assertEquals(collectionSize(ranges10), 3); assertTrue(ranges10.equals(generateRanges(70, 87, 87, 97, 67, 70))); // pre-calculate the results. - Map> expectedEndpoints = new HashMap>(); - expectedEndpoints.put(Simple_RF1_KeyspaceName, HashMultimap.create()); + Map> expectedEndpoints = new HashMap<>(); + expectedEndpoints.put(Simple_RF1_KeyspaceName, HashMultimap.create()); expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2")); expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3")); expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4")); @@ -835,7 +835,7 @@ public class MoveTest expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.1.2")); expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.9")); expectedEndpoints.get(Simple_RF1_KeyspaceName).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.10")); - expectedEndpoints.put(KEYSPACE2, HashMultimap.create()); + expectedEndpoints.put(KEYSPACE2, HashMultimap.create()); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4")); @@ -846,7 +846,7 @@ public class MoveTest expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.1.2")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.9")); expectedEndpoints.get(KEYSPACE2).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.10")); - expectedEndpoints.put(KEYSPACE3, HashMultimap.create()); + expectedEndpoints.put(KEYSPACE3, HashMultimap.create()); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.1.1")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.0.8", "127.0.1.1")); @@ -857,7 +857,7 @@ public class MoveTest expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("75"), makeAddrs("127.0.0.9", "127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.1.2")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.9", "127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3")); expectedEndpoints.get(KEYSPACE3).putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4")); - expectedEndpoints.put(Simple_RF3_KeyspaceName, HashMultimap.create()); + expectedEndpoints.put(Simple_RF3_KeyspaceName, HashMultimap.create()); expectedEndpoints.get(Simple_RF3_KeyspaceName).putAll(new BigIntegerToken("5"), makeAddrs("127.0.0.2", "127.0.0.3", "127.0.0.4")); expectedEndpoints.get(Simple_RF3_KeyspaceName).putAll(new BigIntegerToken("15"), makeAddrs("127.0.0.3", "127.0.0.4", "127.0.0.5")); expectedEndpoints.get(Simple_RF3_KeyspaceName).putAll(new BigIntegerToken("25"), makeAddrs("127.0.0.4", "127.0.0.5", "127.0.0.6")); @@ -970,7 +970,7 @@ public class MoveTest ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); // create a ring or 6 nodes @@ -1001,11 +1001,11 @@ public class MoveTest assertEquals(newToken, tmd.getToken(hosts.get(2))); } - private static Collection makeAddrs(String... hosts) throws UnknownHostException + private static Collection makeAddrs(String... hosts) throws UnknownHostException { - ArrayList addrs = new ArrayList(hosts.length); + ArrayList addrs = new ArrayList<>(hosts.length); for (String host : hosts) - addrs.add(InetAddress.getByName(host)); + addrs.add(InetAddressAndPort.getByName(host)); return addrs; } diff --git a/test/unit/org/apache/cassandra/service/ProtocolBetaVersionTest.java b/test/unit/org/apache/cassandra/service/ProtocolBetaVersionTest.java index 4ade4adcfb..a7551f42b3 100644 --- a/test/unit/org/apache/cassandra/service/ProtocolBetaVersionTest.java +++ b/test/unit/org/apache/cassandra/service/ProtocolBetaVersionTest.java @@ -110,7 +110,7 @@ public class ProtocolBetaVersionTest extends CQLTester } catch (Exception e) { - assertEquals("Beta version of server used (5/v5-beta), but USE_BETA flag is not set", + assertEquals("Beta version of server used (6/v6-beta), but USE_BETA flag is not set", e.getMessage()); } } diff --git a/test/unit/org/apache/cassandra/service/ReadExecutorTest.java b/test/unit/org/apache/cassandra/service/ReadExecutorTest.java index 7630cc6b65..f21e2410b8 100644 --- a/test/unit/org/apache/cassandra/service/ReadExecutorTest.java +++ b/test/unit/org/apache/cassandra/service/ReadExecutorTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.List; import java.util.concurrent.TimeUnit; @@ -36,6 +35,7 @@ import org.apache.cassandra.db.SinglePartitionReadCommand; import org.apache.cassandra.exceptions.ReadFailureException; import org.apache.cassandra.exceptions.ReadTimeoutException; import org.apache.cassandra.exceptions.RequestFailureReason; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; import org.apache.cassandra.schema.KeyspaceParams; @@ -47,7 +47,7 @@ public class ReadExecutorTest { static Keyspace ks; static ColumnFamilyStore cfs; - static List targets; + static List targets; @BeforeClass public static void setUpClass() throws Throwable @@ -56,7 +56,7 @@ public class ReadExecutorTest SchemaLoader.createKeyspace("Foo", KeyspaceParams.simple(3), SchemaLoader.standardCFMD("Foo", "Bar")); ks = Keyspace.open("Foo"); cfs = ks.getColumnFamilyStore("Bar"); - targets = ImmutableList.of(InetAddress.getByName("127.0.0.255"), InetAddress.getByName("127.0.0.254"), InetAddress.getByName("127.0.0.253")); + targets = ImmutableList.of(InetAddressAndPort.getByName("127.0.0.255"), InetAddressAndPort.getByName("127.0.0.254"), InetAddressAndPort.getByName("127.0.0.253")); cfs.sampleLatencyNanos = 0; } diff --git a/test/unit/org/apache/cassandra/service/RemoveTest.java b/test/unit/org/apache/cassandra/service/RemoveTest.java index afc5b2562f..6714a83a0c 100644 --- a/test/unit/org/apache/cassandra/service/RemoveTest.java +++ b/test/unit/org/apache/cassandra/service/RemoveTest.java @@ -20,7 +20,6 @@ package org.apache.cassandra.service; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -40,6 +39,7 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.gms.ApplicationState; import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.gms.VersionedValue.VersionedValueFactory; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.net.MessageOut; import org.apache.cassandra.net.MessagingService; @@ -61,9 +61,9 @@ public class RemoveTest static IPartitioner oldPartitioner; ArrayList endpointTokens = new ArrayList(); ArrayList keyTokens = new ArrayList(); - List hosts = new ArrayList(); + List hosts = new ArrayList<>(); List hostIds = new ArrayList(); - InetAddress removalhost; + InetAddressAndPort removalhost; UUID removalId; @BeforeClass @@ -120,7 +120,7 @@ public class RemoveTest VersionedValueFactory valueFactory = new VersionedValueFactory(DatabaseDescriptor.getPartitioner()); Collection tokens = Collections.singleton(DatabaseDescriptor.getPartitioner().getRandomToken()); - InetAddress joininghost = hosts.get(4); + InetAddressAndPort joininghost = hosts.get(4); UUID joiningId = hostIds.get(4); hosts.remove(joininghost); @@ -159,10 +159,10 @@ public class RemoveTest assertTrue(tmd.isLeaving(removalhost)); assertEquals(1, tmd.getSizeOfLeavingEndpoints()); - for (InetAddress host : hosts) + for (InetAddressAndPort host : hosts) { - MessageOut msg = new MessageOut(host, MessagingService.Verb.REPLICATION_FINISHED, null, null, Collections.emptyMap()); - MessagingService.instance().sendRR(msg, FBUtilities.getBroadcastAddress()); + MessageOut msg = new MessageOut(host, MessagingService.Verb.REPLICATION_FINISHED, null, null, Collections.emptyList()); + MessagingService.instance().sendRR(msg, FBUtilities.getBroadcastAddressAndPort()); } remover.join(); diff --git a/test/unit/org/apache/cassandra/service/SerializationsTest.java b/test/unit/org/apache/cassandra/service/SerializationsTest.java index d943bb7f96..c29e7a81fb 100644 --- a/test/unit/org/apache/cassandra/service/SerializationsTest.java +++ b/test/unit/org/apache/cassandra/service/SerializationsTest.java @@ -19,7 +19,6 @@ package org.apache.cassandra.service; import java.io.IOException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -41,6 +40,7 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.util.DataInputPlus.DataInputStreamPlus; import org.apache.cassandra.io.util.DataOutputStreamPlus; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.repair.NodePair; import org.apache.cassandra.repair.RepairJobDesc; @@ -61,6 +61,8 @@ public class SerializationsTest extends AbstractSerializationsTester private static Range FULL_RANGE; private static RepairJobDesc DESC; + private static final int PORT = 7010; + @BeforeClass public static void defineSchema() throws Exception { @@ -123,7 +125,7 @@ public class SerializationsTest extends AbstractSerializationsTester // empty validation mt.addMerkleTree((int) Math.pow(2, 15), FULL_RANGE); - Validator v0 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1, PreviewKind.NONE); + Validator v0 = new Validator(DESC, FBUtilities.getBroadcastAddressAndPort(), -1, PreviewKind.NONE); ValidationComplete c0 = new ValidationComplete(DESC, mt); // validation with a tree @@ -131,7 +133,7 @@ public class SerializationsTest extends AbstractSerializationsTester mt.addMerkleTree(Integer.MAX_VALUE, FULL_RANGE); for (int i = 0; i < 10; i++) mt.split(p.getRandomToken()); - Validator v1 = new Validator(DESC, FBUtilities.getBroadcastAddress(), -1, PreviewKind.NONE); + Validator v1 = new Validator(DESC, FBUtilities.getBroadcastAddressAndPort(), -1, PreviewKind.NONE); ValidationComplete c1 = new ValidationComplete(DESC, mt); // validation failed @@ -180,9 +182,9 @@ public class SerializationsTest extends AbstractSerializationsTester private void testSyncRequestWrite() throws IOException { - InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); - InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2}); - InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3}); + InetAddressAndPort local = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", PORT); + InetAddressAndPort src = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", PORT); + InetAddressAndPort dest = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.3", PORT); SyncRequest message = new SyncRequest(DESC, local, src, dest, Collections.singleton(FULL_RANGE), PreviewKind.NONE); testRepairMessageWrite("service.SyncRequest.bin", message); @@ -194,9 +196,9 @@ public class SerializationsTest extends AbstractSerializationsTester if (EXECUTE_WRITES) testSyncRequestWrite(); - InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); - InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2}); - InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3}); + InetAddressAndPort local = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.1", PORT); + InetAddressAndPort src = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", PORT); + InetAddressAndPort dest = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.3", PORT); try (DataInputStreamPlus in = getInput("service.SyncRequest.bin")) { @@ -214,8 +216,8 @@ public class SerializationsTest extends AbstractSerializationsTester private void testSyncCompleteWrite() throws IOException { - InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2}); - InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3}); + InetAddressAndPort src = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", PORT); + InetAddressAndPort dest = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.3", PORT); // sync success List summaries = new ArrayList<>(); summaries.add(new SessionSummary(src, dest, @@ -235,8 +237,8 @@ public class SerializationsTest extends AbstractSerializationsTester if (EXECUTE_WRITES) testSyncCompleteWrite(); - InetAddress src = InetAddress.getByAddress(new byte[]{127, 0, 0, 2}); - InetAddress dest = InetAddress.getByAddress(new byte[]{127, 0, 0, 3}); + InetAddressAndPort src = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.2", PORT); + InetAddressAndPort dest = InetAddressAndPort.getByNameOverrideDefaults("127.0.0.3", PORT); NodePair nodes = new NodePair(src, dest); try (DataInputStreamPlus in = getInput("service.SyncComplete.bin")) @@ -246,6 +248,8 @@ public class SerializationsTest extends AbstractSerializationsTester assert message.messageType == RepairMessage.Type.SYNC_COMPLETE; assert DESC.equals(message.desc); + System.out.println(nodes); + System.out.println(((SyncComplete) message).nodes); assert nodes.equals(((SyncComplete) message).nodes); assert ((SyncComplete) message).success; diff --git a/test/unit/org/apache/cassandra/service/StorageProxyTest.java b/test/unit/org/apache/cassandra/service/StorageProxyTest.java index bdf45fe541..590cfeb1a4 100644 --- a/test/unit/org/apache/cassandra/service/StorageProxyTest.java +++ b/test/unit/org/apache/cassandra/service/StorageProxyTest.java @@ -18,7 +18,6 @@ */ package org.apache.cassandra.service; -import java.net.InetAddress; import java.util.List; import org.junit.BeforeClass; @@ -27,6 +26,7 @@ import org.junit.Test; import org.apache.cassandra.db.PartitionPosition; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.dht.*; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import static org.apache.cassandra.Util.rp; @@ -81,8 +81,8 @@ public class StorageProxyTest DatabaseDescriptor.daemonInitialization(); DatabaseDescriptor.getHintsDirectory().mkdir(); TokenMetadata tmd = StorageService.instance.getTokenMetadata(); - tmd.updateNormalToken(token("1"), InetAddress.getByName("127.0.0.1")); - tmd.updateNormalToken(token("6"), InetAddress.getByName("127.0.0.6")); + tmd.updateNormalToken(token("1"), InetAddressAndPort.getByName("127.0.0.1")); + tmd.updateNormalToken(token("6"), InetAddressAndPort.getByName("127.0.0.6")); } // test getRestrictedRanges for token diff --git a/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java b/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java index 3ef7bbb6de..3884f5adb7 100644 --- a/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java +++ b/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java @@ -23,7 +23,6 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; -import java.net.InetAddress; import java.util.*; import com.google.common.collect.HashMultimap; @@ -35,6 +34,7 @@ import org.junit.runner.RunWith; import org.apache.cassandra.OrderedJUnit4ClassRunner; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.schema.KeyspaceMetadata; import org.apache.cassandra.schema.Schema; @@ -192,12 +192,12 @@ public class StorageServiceServerTest metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map configOptions = new HashMap<>(); configOptions.put("DC1", "1"); @@ -209,22 +209,22 @@ public class StorageServiceServerTest Schema.instance.load(meta); Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, - InetAddress.getByName("127.0.0.1")); + InetAddressAndPort.getByName("127.0.0.1")); assertEquals(2, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("D"), new StringToken("A")))); assertTrue(primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D")))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assertEquals(2, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B")))); assertTrue(primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C")))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assertEquals(2, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("D"), new StringToken("A")))); assertTrue(primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B")))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assertEquals(2, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C")))); assertTrue(primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D")))); @@ -236,11 +236,11 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map configOptions = new HashMap<>(); configOptions.put("DC1", "1"); @@ -251,19 +251,19 @@ public class StorageServiceServerTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.create(false, configOptions)); Schema.instance.load(meta); - Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("D"), new StringToken("A"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D"))); } @@ -274,11 +274,11 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map configOptions = new HashMap<>(); configOptions.put("DC2", "2"); @@ -289,19 +289,19 @@ public class StorageServiceServerTest Schema.instance.load(meta); // endpoints in DC1 should not have primary range - Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assert primaryRanges.isEmpty(); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assert primaryRanges.isEmpty(); // endpoints in DC2 should have primary ranges which also cover DC1 - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assert primaryRanges.size() == 2; assert primaryRanges.contains(new Range(new StringToken("D"), new StringToken("A"))); assert primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assert primaryRanges.size() == 2; assert primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D"))); assert primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C"))); @@ -313,11 +313,11 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); // DC1 - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.2")); // DC2 - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.4")); - metadata.updateNormalToken(new StringToken("D"), InetAddress.getByName("127.0.0.5")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.4")); + metadata.updateNormalToken(new StringToken("D"), InetAddressAndPort.getByName("127.0.0.5")); Map configOptions = new HashMap<>(); configOptions.put("DC2", "2"); @@ -328,20 +328,20 @@ public class StorageServiceServerTest Schema.instance.load(meta); // endpoints in DC1 should not have primary range - Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assertTrue(primaryRanges.isEmpty()); primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, - InetAddress.getByName("127.0.0.2")); + InetAddressAndPort.getByName("127.0.0.2")); assertTrue(primaryRanges.isEmpty()); // endpoints in DC2 should have primary ranges which also cover DC1 - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assertTrue(primaryRanges.size() == 2); assertTrue(primaryRanges.contains(new Range(new StringToken("D"), new StringToken("A")))); assertTrue(primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B")))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assertTrue(primaryRanges.size() == 2); assertTrue(primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D")))); assertTrue(primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C")))); @@ -353,22 +353,22 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); // DC1 - Multimap dc1 = HashMultimap.create(); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("A")); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("E")); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("H")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("C")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("I")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("J")); + Multimap dc1 = HashMultimap.create(); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("A")); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("E")); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("H")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("C")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("I")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("J")); metadata.updateNormalTokens(dc1); // DC2 - Multimap dc2 = HashMultimap.create(); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("B")); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("G")); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("L")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("D")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("F")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("K")); + Multimap dc2 = HashMultimap.create(); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("B")); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("G")); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("L")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("D")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("F")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("K")); metadata.updateNormalTokens(dc2); Map configOptions = new HashMap<>(); @@ -380,14 +380,14 @@ public class StorageServiceServerTest Schema.instance.load(meta); // endpoints in DC1 should not have primary range - Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assert primaryRanges.isEmpty(); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assert primaryRanges.isEmpty(); // endpoints in DC2 should have primary ranges which also cover DC1 - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assert primaryRanges.size() == 4; assert primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B"))); assert primaryRanges.contains(new Range(new StringToken("F"), new StringToken("G"))); @@ -396,7 +396,7 @@ public class StorageServiceServerTest // the node covers range (L, A] assert primaryRanges.contains(new Range(new StringToken("L"), new StringToken("A"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assert primaryRanges.size() == 8; assert primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D"))); assert primaryRanges.contains(new Range(new StringToken("E"), new StringToken("F"))); @@ -418,23 +418,23 @@ public class StorageServiceServerTest metadata.clearUnsafe(); // DC1 - Multimap dc1 = HashMultimap.create(); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("A")); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("E")); - dc1.put(InetAddress.getByName("127.0.0.1"), new StringToken("H")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("C")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("I")); - dc1.put(InetAddress.getByName("127.0.0.2"), new StringToken("J")); + Multimap dc1 = HashMultimap.create(); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("A")); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("E")); + dc1.put(InetAddressAndPort.getByName("127.0.0.1"), new StringToken("H")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("C")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("I")); + dc1.put(InetAddressAndPort.getByName("127.0.0.2"), new StringToken("J")); metadata.updateNormalTokens(dc1); // DC2 - Multimap dc2 = HashMultimap.create(); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("B")); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("G")); - dc2.put(InetAddress.getByName("127.0.0.4"), new StringToken("L")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("D")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("F")); - dc2.put(InetAddress.getByName("127.0.0.5"), new StringToken("K")); + Multimap dc2 = HashMultimap.create(); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("B")); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("G")); + dc2.put(InetAddressAndPort.getByName("127.0.0.4"), new StringToken("L")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("D")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("F")); + dc2.put(InetAddressAndPort.getByName("127.0.0.5"), new StringToken("K")); metadata.updateNormalTokens(dc2); Map configOptions = new HashMap<>(); @@ -447,7 +447,7 @@ public class StorageServiceServerTest Schema.instance.load(meta); // endpoints in DC1 should have primary ranges which also cover DC2 - Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assertEquals(8, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("J"), new StringToken("K")))); assertTrue(primaryRanges.contains(new Range(new StringToken("K"), new StringToken("L")))); @@ -459,7 +459,7 @@ public class StorageServiceServerTest assertTrue(primaryRanges.contains(new Range(new StringToken("G"), new StringToken("H")))); // endpoints in DC1 should have primary ranges which also cover DC2 - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assertEquals(4, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C")))); assertTrue(primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B")))); @@ -467,7 +467,7 @@ public class StorageServiceServerTest assertTrue(primaryRanges.contains(new Range(new StringToken("I"), new StringToken("J")))); // endpoints in DC2 should have primary ranges which also cover DC1 - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.4")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.4")); assertEquals(4, primaryRanges.size()); assertTrue(primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B")))); assertTrue(primaryRanges.contains(new Range(new StringToken("F"), new StringToken("G")))); @@ -476,7 +476,7 @@ public class StorageServiceServerTest // the node covers range (L, A] assertTrue(primaryRanges.contains(new Range(new StringToken("L"), new StringToken("A")))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.5")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.5")); assertTrue(primaryRanges.size() == 8); assertTrue(primaryRanges.contains(new Range(new StringToken("C"), new StringToken("D")))); assertTrue(primaryRanges.contains(new Range(new StringToken("E"), new StringToken("F")))); @@ -497,23 +497,23 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.2")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.3")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.3")); Keyspace.clear("Keyspace1"); KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.simpleTransient(2)); Schema.instance.load(meta); - Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("C"), new StringToken("A"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B"))); - primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddress.getByName("127.0.0.3")); + primaryRanges = StorageService.instance.getPrimaryRangesForEndpoint(meta.name, InetAddressAndPort.getByName("127.0.0.3")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C"))); } @@ -525,9 +525,9 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); - metadata.updateNormalToken(new StringToken("A"), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new StringToken("B"), InetAddress.getByName("127.0.0.2")); - metadata.updateNormalToken(new StringToken("C"), InetAddress.getByName("127.0.0.3")); + metadata.updateNormalToken(new StringToken("A"), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new StringToken("B"), InetAddressAndPort.getByName("127.0.0.2")); + metadata.updateNormalToken(new StringToken("C"), InetAddressAndPort.getByName("127.0.0.3")); Map configOptions = new HashMap<>(); configOptions.put("replication_factor", "2"); @@ -536,15 +536,15 @@ public class StorageServiceServerTest KeyspaceMetadata meta = KeyspaceMetadata.create("Keyspace1", KeyspaceParams.simpleTransient(2)); Schema.instance.load(meta); - Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.1")); + Collection> primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.1")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("C"), new StringToken("A"))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.2")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.2")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("A"), new StringToken("B"))); - primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddress.getByName("127.0.0.3")); + primaryRanges = StorageService.instance.getPrimaryRangeForEndpointWithinDC(meta.name, InetAddressAndPort.getByName("127.0.0.3")); assert primaryRanges.size() == 1; assert primaryRanges.contains(new Range(new StringToken("B"), new StringToken("C"))); } @@ -557,10 +557,10 @@ public class StorageServiceServerTest TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); - metadata.updateNormalToken(new LongToken(1000L), InetAddress.getByName("127.0.0.1")); - metadata.updateNormalToken(new LongToken(2000L), InetAddress.getByName("127.0.0.2")); - metadata.updateNormalToken(new LongToken(3000L), InetAddress.getByName("127.0.0.3")); - metadata.updateNormalToken(new LongToken(4000L), InetAddress.getByName("127.0.0.4")); + metadata.updateNormalToken(new LongToken(1000L), InetAddressAndPort.getByName("127.0.0.1")); + metadata.updateNormalToken(new LongToken(2000L), InetAddressAndPort.getByName("127.0.0.2")); + metadata.updateNormalToken(new LongToken(3000L), InetAddressAndPort.getByName("127.0.0.3")); + metadata.updateNormalToken(new LongToken(4000L), InetAddressAndPort.getByName("127.0.0.4")); Collection> repairRangeFrom = StorageService.instance.createRepairRangeFrom("1500", "3700"); assert repairRangeFrom.size() == 3; diff --git a/test/unit/org/apache/cassandra/service/WriteResponseHandlerTest.java b/test/unit/org/apache/cassandra/service/WriteResponseHandlerTest.java index 817246309f..f8567e8210 100644 --- a/test/unit/org/apache/cassandra/service/WriteResponseHandlerTest.java +++ b/test/unit/org/apache/cassandra/service/WriteResponseHandlerTest.java @@ -37,6 +37,7 @@ import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.WriteType; import org.apache.cassandra.locator.IEndpointSnitch; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.schema.KeyspaceParams; @@ -48,7 +49,7 @@ public class WriteResponseHandlerTest { static Keyspace ks; static ColumnFamilyStore cfs; - static List targets; + static List targets; @BeforeClass public static void setUpClass() throws Throwable @@ -57,36 +58,36 @@ public class WriteResponseHandlerTest // Register peers with expected DC for NetworkTopologyStrategy. TokenMetadata metadata = StorageService.instance.getTokenMetadata(); metadata.clearUnsafe(); - metadata.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.1.0.255")); - metadata.updateHostId(UUID.randomUUID(), InetAddress.getByName("127.2.0.255")); + metadata.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.1.0.255")); + metadata.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.2.0.255")); DatabaseDescriptor.setEndpointSnitch(new IEndpointSnitch() { - public String getRack(InetAddress endpoint) + public String getRack(InetAddressAndPort endpoint) { return null; } - public String getDatacenter(InetAddress endpoint) + public String getDatacenter(InetAddressAndPort endpoint) { - byte[] address = endpoint.getAddress(); + byte[] address = endpoint.address.getAddress(); if (address[1] == 1) return "datacenter1"; else return "datacenter2"; } - public List getSortedListByProximity(InetAddress address, Collection unsortedAddress) + public List getSortedListByProximity(InetAddressAndPort address, Collection unsortedAddress) { return null; } - public void sortByProximity(InetAddress address, List addresses) + public void sortByProximity(InetAddressAndPort address, List addresses) { } - public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) + public int compareEndpoints(InetAddressAndPort target, InetAddressAndPort a1, InetAddressAndPort a2) { return 0; } @@ -96,7 +97,7 @@ public class WriteResponseHandlerTest } - public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) + public boolean isWorthMergingForRangeQuery(List merged, List l1, List l2) { return false; } @@ -105,8 +106,8 @@ public class WriteResponseHandlerTest SchemaLoader.createKeyspace("Foo", KeyspaceParams.nts("datacenter1", 3, "datacenter2", 3), SchemaLoader.standardCFMD("Foo", "Bar")); ks = Keyspace.open("Foo"); cfs = ks.getColumnFamilyStore("Bar"); - targets = ImmutableList.of(InetAddress.getByName("127.1.0.255"), InetAddress.getByName("127.1.0.254"), InetAddress.getByName("127.1.0.253"), - InetAddress.getByName("127.2.0.255"), InetAddress.getByName("127.2.0.254"), InetAddress.getByName("127.2.0.253")); + targets = ImmutableList.of(InetAddressAndPort.getByName("127.1.0.255"), InetAddressAndPort.getByName("127.1.0.254"), InetAddressAndPort.getByName("127.1.0.253"), + InetAddressAndPort.getByName("127.2.0.255"), InetAddressAndPort.getByName("127.2.0.254"), InetAddressAndPort.getByName("127.2.0.253")); } @Before diff --git a/test/unit/org/apache/cassandra/streaming/SessionInfoTest.java b/test/unit/org/apache/cassandra/streaming/SessionInfoTest.java index bc24979749..4f0c49468d 100644 --- a/test/unit/org/apache/cassandra/streaming/SessionInfoTest.java +++ b/test/unit/org/apache/cassandra/streaming/SessionInfoTest.java @@ -17,13 +17,13 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import org.junit.Test; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.utils.FBUtilities; @@ -36,7 +36,7 @@ public class SessionInfoTest public void testTotals() { TableId tableId = TableId.generate(); - InetAddress local = FBUtilities.getLocalAddress(); + InetAddressAndPort local = FBUtilities.getLocalAddressAndPort(); Collection summaries = new ArrayList<>(); for (int i = 0; i < 10; i++) diff --git a/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java b/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java index 5c2969898b..ceaaae0e12 100644 --- a/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java +++ b/test/unit/org/apache/cassandra/streaming/StreamTransferTaskTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -41,6 +40,7 @@ import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.io.sstable.format.SSTableReader; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.streaming.messages.OutgoingFileMessage; @@ -74,7 +74,7 @@ public class StreamTransferTaskTest @Test public void testScheduleTimeout() throws Exception { - InetAddress peer = FBUtilities.getBroadcastAddress(); + InetAddressAndPort peer = FBUtilities.getBroadcastAddressAndPort(); StreamSession session = new StreamSession(peer, peer, (connectionId, protocolVersion) -> new EmbeddedChannel(), 0, true, UUID.randomUUID(), PreviewKind.ALL); ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_STANDARD); @@ -120,7 +120,7 @@ public class StreamTransferTaskTest @Test public void testFailSessionDuringTransferShouldNotReleaseReferences() throws Exception { - InetAddress peer = FBUtilities.getBroadcastAddress(); + InetAddressAndPort peer = FBUtilities.getBroadcastAddressAndPort(); StreamCoordinator streamCoordinator = new StreamCoordinator(1, true, new DefaultConnectionFactory(), false, null, PreviewKind.NONE); StreamResultFuture future = StreamResultFuture.init(UUID.randomUUID(), StreamOperation.OTHER, Collections.emptyList(), streamCoordinator); StreamSession session = new StreamSession(peer, peer, null, 0, true, null, PreviewKind.NONE); diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java index 7a51d0c7bf..16c07a09a1 100644 --- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java +++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.streaming; -import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.TimeUnit; @@ -35,6 +34,7 @@ import junit.framework.Assert; import org.apache.cassandra.OrderedJUnit4ClassRunner; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.QueryProcessor; @@ -69,7 +69,7 @@ public class StreamingTransferTest DatabaseDescriptor.daemonInitialization(); } - public static final InetAddress LOCAL = FBUtilities.getBroadcastAddress(); + public static final InetAddressAndPort LOCAL = FBUtilities.getBroadcastAddressAndPort(); public static final String KEYSPACE1 = "StreamingTransferTest1"; public static final String CF_STANDARD = "Standard1"; public static final String CF_COUNTER = "Counter1"; diff --git a/test/unit/org/apache/cassandra/streaming/async/NettyStreamingMessageSenderTest.java b/test/unit/org/apache/cassandra/streaming/async/NettyStreamingMessageSenderTest.java index a9849a3db2..617bae11ad 100644 --- a/test/unit/org/apache/cassandra/streaming/async/NettyStreamingMessageSenderTest.java +++ b/test/unit/org/apache/cassandra/streaming/async/NettyStreamingMessageSenderTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -34,6 +35,7 @@ import org.junit.Test; import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.async.TestScheduledFuture; import org.apache.cassandra.streaming.PreviewKind; import org.apache.cassandra.streaming.StreamOperation; @@ -43,7 +45,7 @@ import org.apache.cassandra.streaming.messages.CompleteMessage; public class NettyStreamingMessageSenderTest { - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 0); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 0); private EmbeddedChannel channel; private StreamSession session; @@ -62,8 +64,8 @@ public class NettyStreamingMessageSenderTest channel = new EmbeddedChannel(); channel.attr(NettyStreamingMessageSender.TRANSFERRING_FILE_ATTR).set(Boolean.FALSE); UUID pendingRepair = UUID.randomUUID(); - session = new StreamSession(REMOTE_ADDR.getAddress(), REMOTE_ADDR.getAddress(), (connectionId, protocolVersion) -> null, 0, true, pendingRepair, PreviewKind.ALL); - StreamResultFuture future = StreamResultFuture.initReceivingSide(0, UUID.randomUUID(), StreamOperation.REPAIR, REMOTE_ADDR.getAddress(), channel, true, pendingRepair, session.getPreviewKind()); + session = new StreamSession(REMOTE_ADDR, REMOTE_ADDR, (connectionId, protocolVersion) -> null, 0, true, pendingRepair, PreviewKind.ALL); + StreamResultFuture future = StreamResultFuture.initReceivingSide(0, UUID.randomUUID(), StreamOperation.REPAIR, REMOTE_ADDR, channel, true, pendingRepair, session.getPreviewKind()); session.init(future); sender = session.getMessageSender(); sender.setControlMessageChannel(channel); diff --git a/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java b/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java index a674e6b6e6..b4a736e67f 100644 --- a/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java +++ b/test/unit/org/apache/cassandra/streaming/async/StreamingInboundHandlerTest.java @@ -24,6 +24,7 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.UUID; +import com.google.common.net.InetAddresses; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,6 +36,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.io.sstable.format.SSTableFormat; import org.apache.cassandra.io.sstable.format.big.BigFormat; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.net.async.RebufferingByteBufDataInputPlus; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.streaming.PreviewKind; @@ -52,7 +54,7 @@ import org.apache.cassandra.streaming.messages.StreamMessage; public class StreamingInboundHandlerTest { private static final int VERSION = StreamMessage.CURRENT_VERSION; - private static final InetSocketAddress REMOTE_ADDR = new InetSocketAddress("127.0.0.2", 0); + private static final InetAddressAndPort REMOTE_ADDR = InetAddressAndPort.getByAddressOverrideDefaults(InetAddresses.forString("127.0.0.2"), 0); private StreamingInboundHandler handler; private EmbeddedChannel channel; @@ -123,7 +125,7 @@ public class StreamingInboundHandlerTest @Test public void StreamDeserializingTask_deriveSession_StreamInitMessage() throws InterruptedException, IOException { - StreamInitMessage msg = new StreamInitMessage(REMOTE_ADDR.getAddress(), 0, UUID.randomUUID(), StreamOperation.REPAIR, true, UUID.randomUUID(), PreviewKind.ALL); + StreamInitMessage msg = new StreamInitMessage(REMOTE_ADDR, 0, UUID.randomUUID(), StreamOperation.REPAIR, true, UUID.randomUUID(), PreviewKind.ALL); StreamingInboundHandler.StreamDeserializingTask task = handler.new StreamDeserializingTask(sid -> createSession(sid), null, channel); StreamSession session = task.deriveSession(msg); Assert.assertNotNull(session); @@ -145,7 +147,7 @@ public class StreamingInboundHandlerTest @Test (expected = IllegalStateException.class) public void StreamDeserializingTask_deriveSession_IFM_NoSession() throws InterruptedException, IOException { - FileMessageHeader header = new FileMessageHeader(TableId.generate(), REMOTE_ADDR.getAddress(), UUID.randomUUID(), 0, 0, + FileMessageHeader header = new FileMessageHeader(TableId.generate(), REMOTE_ADDR, UUID.randomUUID(), 0, 0, BigFormat.latestVersion, SSTableFormat.Type.BIG, 0, new ArrayList<>(), null, 0, UUID.randomUUID(), 0 , null); IncomingFileMessage msg = new IncomingFileMessage(null, header); StreamingInboundHandler.StreamDeserializingTask task = handler.new StreamDeserializingTask(sid -> StreamManager.instance.findSession(sid.from, sid.planId, sid.sessionIndex), null, channel); @@ -156,9 +158,9 @@ public class StreamingInboundHandlerTest public void StreamDeserializingTask_deriveSession_IFM_HasSession() throws InterruptedException, IOException { UUID planId = UUID.randomUUID(); - StreamResultFuture future = StreamResultFuture.initReceivingSide(0, planId, StreamOperation.REPAIR, REMOTE_ADDR.getAddress(), channel, true, UUID.randomUUID(), PreviewKind.ALL); + StreamResultFuture future = StreamResultFuture.initReceivingSide(0, planId, StreamOperation.REPAIR, REMOTE_ADDR, channel, true, UUID.randomUUID(), PreviewKind.ALL); StreamManager.instance.register(future); - FileMessageHeader header = new FileMessageHeader(TableId.generate(), REMOTE_ADDR.getAddress(), planId, 0, 0, + FileMessageHeader header = new FileMessageHeader(TableId.generate(), REMOTE_ADDR, planId, 0, 0, BigFormat.latestVersion, SSTableFormat.Type.BIG, 0, new ArrayList<>(), null, 0, UUID.randomUUID(), 0 , null); IncomingFileMessage msg = new IncomingFileMessage(null, header); StreamingInboundHandler.StreamDeserializingTask task = handler.new StreamDeserializingTask(sid -> StreamManager.instance.findSession(sid.from, sid.planId, sid.sessionIndex), null, channel); diff --git a/test/unit/org/apache/cassandra/tracing/TracingTest.java b/test/unit/org/apache/cassandra/tracing/TracingTest.java index f546496e88..61e08b03dd 100644 --- a/test/unit/org/apache/cassandra/tracing/TracingTest.java +++ b/test/unit/org/apache/cassandra/tracing/TracingTest.java @@ -31,6 +31,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.utils.progress.ProgressEvent; import org.apache.commons.lang3.StringUtils; @@ -197,7 +198,7 @@ public final class TracingTest return super.newSession(sessionId, traceType, customPayload); } - protected TraceState newTraceState(InetAddress ia, UUID uuid, Tracing.TraceType tt) + protected TraceState newTraceState(InetAddressAndPort ia, UUID uuid, Tracing.TraceType tt) { return new TraceState(ia, uuid, tt) { diff --git a/test/unit/org/apache/cassandra/transport/ErrorMessageTest.java b/test/unit/org/apache/cassandra/transport/ErrorMessageTest.java index 2dcd2ac223..3d47ff3fce 100644 --- a/test/unit/org/apache/cassandra/transport/ErrorMessageTest.java +++ b/test/unit/org/apache/cassandra/transport/ErrorMessageTest.java @@ -18,7 +18,6 @@ package org.apache.cassandra.transport; -import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; @@ -33,26 +32,27 @@ import org.apache.cassandra.db.WriteType; import org.apache.cassandra.exceptions.ReadFailureException; import org.apache.cassandra.exceptions.RequestFailureReason; import org.apache.cassandra.exceptions.WriteFailureException; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.transport.messages.ErrorMessage; import static org.junit.Assert.assertEquals; public class ErrorMessageTest { - private static Map failureReasonMap1; - private static Map failureReasonMap2; + private static Map failureReasonMap1; + private static Map failureReasonMap2; @BeforeClass public static void setUpFixtures() throws UnknownHostException { failureReasonMap1 = new HashMap<>(); - failureReasonMap1.put(InetAddress.getByName("127.0.0.1"), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); - failureReasonMap1.put(InetAddress.getByName("127.0.0.2"), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); - failureReasonMap1.put(InetAddress.getByName("127.0.0.3"), RequestFailureReason.UNKNOWN); + failureReasonMap1.put(InetAddressAndPort.getByName("127.0.0.1"), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); + failureReasonMap1.put(InetAddressAndPort.getByName("127.0.0.2"), RequestFailureReason.READ_TOO_MANY_TOMBSTONES); + failureReasonMap1.put(InetAddressAndPort.getByName("127.0.0.3"), RequestFailureReason.UNKNOWN); failureReasonMap2 = new HashMap<>(); - failureReasonMap2.put(InetAddress.getByName("127.0.0.1"), RequestFailureReason.UNKNOWN); - failureReasonMap2.put(InetAddress.getByName("127.0.0.2"), RequestFailureReason.UNKNOWN); + failureReasonMap2.put(InetAddressAndPort.getByName("127.0.0.1"), RequestFailureReason.UNKNOWN); + failureReasonMap2.put(InetAddressAndPort.getByName("127.0.0.2"), RequestFailureReason.UNKNOWN); } @Test @@ -102,11 +102,11 @@ public class ErrorMessageTest @Test public void testRequestFailureExceptionMakesCopy() throws UnknownHostException { - Map modifiableFailureReasons = new HashMap<>(failureReasonMap1); + Map modifiableFailureReasons = new HashMap<>(failureReasonMap1); ReadFailureException rfe = new ReadFailureException(ConsistencyLevel.ALL, 3, 3, false, modifiableFailureReasons); WriteFailureException wfe = new WriteFailureException(ConsistencyLevel.ALL, 3, 3, WriteType.SIMPLE, modifiableFailureReasons); - modifiableFailureReasons.put(InetAddress.getByName("127.0.0.4"), RequestFailureReason.UNKNOWN); + modifiableFailureReasons.put(InetAddressAndPort.getByName("127.0.0.4"), RequestFailureReason.UNKNOWN); assertEquals(failureReasonMap1, rfe.failureReasonByEndpoint); assertEquals(failureReasonMap1, wfe.failureReasonByEndpoint); diff --git a/test/unit/org/apache/cassandra/transport/ProtocolVersionTest.java b/test/unit/org/apache/cassandra/transport/ProtocolVersionTest.java index e287c08de6..6b95c67985 100644 --- a/test/unit/org/apache/cassandra/transport/ProtocolVersionTest.java +++ b/test/unit/org/apache/cassandra/transport/ProtocolVersionTest.java @@ -62,7 +62,8 @@ public class ProtocolVersionTest Assert.assertNotNull(ProtocolVersion.CURRENT); Assert.assertFalse(ProtocolVersion.V4.isBeta()); - Assert.assertTrue(ProtocolVersion.V5.isBeta()); + Assert.assertFalse(ProtocolVersion.V5.isBeta()); + Assert.assertTrue(ProtocolVersion.V6.isBeta()); } @Test @@ -72,24 +73,30 @@ public class ProtocolVersionTest Assert.assertTrue(ProtocolVersion.V2.isSmallerOrEqualTo(ProtocolVersion.V2)); Assert.assertTrue(ProtocolVersion.V3.isSmallerOrEqualTo(ProtocolVersion.V3)); Assert.assertTrue(ProtocolVersion.V4.isSmallerOrEqualTo(ProtocolVersion.V4)); + Assert.assertTrue(ProtocolVersion.V5.isSmallerOrEqualTo(ProtocolVersion.V5)); Assert.assertTrue(ProtocolVersion.V1.isGreaterOrEqualTo(ProtocolVersion.V1)); Assert.assertTrue(ProtocolVersion.V2.isGreaterOrEqualTo(ProtocolVersion.V2)); Assert.assertTrue(ProtocolVersion.V3.isGreaterOrEqualTo(ProtocolVersion.V3)); Assert.assertTrue(ProtocolVersion.V4.isGreaterOrEqualTo(ProtocolVersion.V4)); + Assert.assertTrue(ProtocolVersion.V5.isGreaterOrEqualTo(ProtocolVersion.V5)); Assert.assertTrue(ProtocolVersion.V1.isSmallerThan(ProtocolVersion.V2)); Assert.assertTrue(ProtocolVersion.V2.isSmallerThan(ProtocolVersion.V3)); Assert.assertTrue(ProtocolVersion.V3.isSmallerThan(ProtocolVersion.V4)); + Assert.assertTrue(ProtocolVersion.V4.isSmallerThan(ProtocolVersion.V5)); Assert.assertFalse(ProtocolVersion.V1.isGreaterThan(ProtocolVersion.V2)); Assert.assertFalse(ProtocolVersion.V2.isGreaterThan(ProtocolVersion.V3)); Assert.assertFalse(ProtocolVersion.V3.isGreaterThan(ProtocolVersion.V4)); + Assert.assertFalse(ProtocolVersion.V4.isGreaterThan(ProtocolVersion.V5)); + Assert.assertTrue(ProtocolVersion.V5.isGreaterThan(ProtocolVersion.V4)); Assert.assertTrue(ProtocolVersion.V4.isGreaterThan(ProtocolVersion.V3)); Assert.assertTrue(ProtocolVersion.V3.isGreaterThan(ProtocolVersion.V2)); Assert.assertTrue(ProtocolVersion.V2.isGreaterThan(ProtocolVersion.V1)); + Assert.assertFalse(ProtocolVersion.V5.isSmallerThan(ProtocolVersion.V4)); Assert.assertFalse(ProtocolVersion.V4.isSmallerThan(ProtocolVersion.V3)); Assert.assertFalse(ProtocolVersion.V3.isSmallerThan(ProtocolVersion.V2)); Assert.assertFalse(ProtocolVersion.V2.isSmallerThan(ProtocolVersion.V1)); diff --git a/test/unit/org/apache/cassandra/transport/SerDeserTest.java b/test/unit/org/apache/cassandra/transport/SerDeserTest.java index 2592ae7874..d3b92824e7 100644 --- a/test/unit/org/apache/cassandra/transport/SerDeserTest.java +++ b/test/unit/org/apache/cassandra/transport/SerDeserTest.java @@ -42,12 +42,14 @@ import static org.junit.Assert.assertEquals; import static org.apache.cassandra.utils.ByteBufferUtil.bytes; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; /** * Serialization/deserialization tests for protocol objects and messages. */ public class SerDeserTest { + @BeforeClass public static void setupDD() { @@ -113,12 +115,12 @@ public class SerDeserTest { List events = new ArrayList<>(); - events.add(TopologyChange.newNode(FBUtilities.getBroadcastAddress(), 42)); - events.add(TopologyChange.removedNode(FBUtilities.getBroadcastAddress(), 42)); - events.add(TopologyChange.movedNode(FBUtilities.getBroadcastAddress(), 42)); + events.add(TopologyChange.newNode(FBUtilities.getBroadcastAddressAndPort())); + events.add(TopologyChange.removedNode(FBUtilities.getBroadcastAddressAndPort())); + events.add(TopologyChange.movedNode(FBUtilities.getBroadcastAddressAndPort())); - events.add(StatusChange.nodeUp(FBUtilities.getBroadcastAddress(), 42)); - events.add(StatusChange.nodeDown(FBUtilities.getBroadcastAddress(), 42)); + events.add(StatusChange.nodeUp(FBUtilities.getBroadcastAddressAndPort())); + events.add(StatusChange.nodeDown(FBUtilities.getBroadcastAddressAndPort())); events.add(new SchemaChange(SchemaChange.Change.CREATED, "ks")); events.add(new SchemaChange(SchemaChange.Change.UPDATED, "ks")); diff --git a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java index b8d2633923..a982624448 100644 --- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java +++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java @@ -153,7 +153,7 @@ public class FBUtilitiesTest } @Test - public void testGetBroadcastRpcAddress() throws Exception + public void testGetBroadcastNativeAddress() throws Exception { //When both rpc_address and broadcast_rpc_address are null, it should return the local address (from DD.applyAddressConfig) FBUtilities.reset(); @@ -161,21 +161,21 @@ public class FBUtilitiesTest testConfig.rpc_address = null; testConfig.broadcast_rpc_address = null; DatabaseDescriptor.applyAddressConfig(testConfig); - assertEquals(FBUtilities.getLocalAddress(), FBUtilities.getBroadcastRpcAddress()); + assertEquals(FBUtilities.getJustLocalAddress(), FBUtilities.getJustBroadcastNativeAddress()); //When rpc_address is defined and broadcast_rpc_address is null, it should return the rpc_address FBUtilities.reset(); testConfig.rpc_address = "127.0.0.2"; testConfig.broadcast_rpc_address = null; DatabaseDescriptor.applyAddressConfig(testConfig); - assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getBroadcastRpcAddress()); + assertEquals(InetAddress.getByName("127.0.0.2"), FBUtilities.getJustBroadcastNativeAddress()); //When both rpc_address and broadcast_rpc_address are defined, it should return broadcast_rpc_address FBUtilities.reset(); testConfig.rpc_address = "127.0.0.2"; testConfig.broadcast_rpc_address = "127.0.0.3"; DatabaseDescriptor.applyAddressConfig(testConfig); - assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getBroadcastRpcAddress()); + assertEquals(InetAddress.getByName("127.0.0.3"), FBUtilities.getJustBroadcastNativeAddress()); FBUtilities.reset(); } diff --git a/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java b/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java index 867918aa91..ffc1acedec 100644 --- a/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java +++ b/tools/stress/src/org/apache/cassandra/stress/CompactionStress.java @@ -20,7 +20,6 @@ package org.apache.cassandra.stress; import java.io.File; import java.io.IOError; -import java.net.InetAddress; import java.net.URI; import java.util.*; import java.util.concurrent.*; @@ -44,6 +43,7 @@ import org.apache.cassandra.io.sstable.Component; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.io.util.FileUtils; +import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.TokenMetadata; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.stress.generate.PartitionGenerator; @@ -185,7 +185,7 @@ public abstract class CompactionStress implements Runnable tokenMetadata.clearUnsafe(); for (int i = 1; i <= numTokens; i++) { - InetAddress addr = FBUtilities.getBroadcastAddress(); + InetAddressAndPort addr = FBUtilities.getBroadcastAddressAndPort(); List tokens = Lists.newArrayListWithCapacity(numTokens); for (int j = 0; j < numTokens; ++j) tokens.add(p.getRandomToken(random)); diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsNode.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsNode.java index a0291627c4..24c10bf8d8 100644 --- a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsNode.java +++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsNode.java @@ -29,6 +29,8 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.*; +import com.google.common.net.HostAndPort; + import com.datastax.driver.core.Host; import org.apache.cassandra.stress.util.ResultLogger; @@ -37,6 +39,7 @@ public class SettingsNode implements Serializable public final List nodes; public final boolean isWhiteList; public final String datacenter; + public final boolean allowServerPortDiscovery; public SettingsNode(Options options) { @@ -69,6 +72,7 @@ public class SettingsNode implements Serializable isWhiteList = options.whitelist.setByUser(); datacenter = options.datacenter.value(); + allowServerPortDiscovery = options.allowServerPortDiscovery.setByUser(); } public Set resolveAllPermitted(StressSettings settings) @@ -80,7 +84,7 @@ public class SettingsNode implements Serializable if (!isWhiteList) { for (Host host : settings.getJavaDriverClient().getCluster().getMetadata().getAllHosts()) - r.add(host.getAddress().getHostName()); + r.add(host.getSocketAddress().getHostString() + ":" + host.getSocketAddress().getPort()); break; } case SIMPLE_NATIVE: @@ -97,7 +101,8 @@ public class SettingsNode implements Serializable { try { - r.add(InetAddress.getByName(node)); + HostAndPort hap = HostAndPort.fromString(node); + r.add(InetAddress.getByName(hap.getHost())); } catch (UnknownHostException e) { @@ -114,7 +119,8 @@ public class SettingsNode implements Serializable { try { - r.add(new InetSocketAddress(InetAddress.getByName(node), port)); + HostAndPort hap = HostAndPort.fromString(node).withDefaultPort(port); + r.add(new InetSocketAddress(InetAddress.getByName(hap.getHost()), hap.getPort())); } catch (UnknownHostException e) { @@ -139,12 +145,13 @@ public class SettingsNode implements Serializable final OptionSimple datacenter = new OptionSimple("datacenter=", ".*", null, "Datacenter used for DCAwareRoundRobinLoadPolicy", false); final OptionSimple whitelist = new OptionSimple("whitelist", "", null, "Limit communications to the provided nodes", false); final OptionSimple file = new OptionSimple("file=", ".*", null, "Node file (one per line)", false); + final OptionSimple allowServerPortDiscovery = new OptionSimple("allow_server_port_discovery", "", null, "Allow Java client to discover server client port numbers", false); final OptionSimple list = new OptionSimple("", "[^=,]+(,[^=,]+)*", "localhost", "comma delimited list of nodes", false); @Override public List options() { - return Arrays.asList(datacenter, whitelist, file, list); + return Arrays.asList(datacenter, whitelist, file, allowServerPortDiscovery, list); } } @@ -154,6 +161,7 @@ public class SettingsNode implements Serializable out.println(" Nodes: " + nodes); out.println(" Is White List: " + isWhiteList); out.println(" Datacenter: " + datacenter); + out.println(" Allow server port discovery: " + allowServerPortDiscovery); } public static SettingsNode get(Map clArgs) diff --git a/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java b/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java index 4928cd26b4..fbcab4b957 100644 --- a/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java +++ b/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java @@ -54,6 +54,7 @@ public class JavaDriverClient private Cluster cluster; private Session session; private final LoadBalancingPolicy loadBalancingPolicy; + private final boolean allowServerPortDiscovery; private static final ConcurrentMap stmts = new ConcurrentHashMap<>(); @@ -73,6 +74,7 @@ public class JavaDriverClient this.encryptionOptions = encryptionOptions; this.loadBalancingPolicy = loadBalancingPolicy(settings); this.connectionsPerHost = settings.mode.connectionsPerHost == null ? 8 : settings.mode.connectionsPerHost; + this.allowServerPortDiscovery = settings.node.allowServerPortDiscovery; int maxThreadCount = 0; if (settings.rate.auto) @@ -134,6 +136,9 @@ public class JavaDriverClient .withoutJMXReporting() .withProtocolVersion(protocolVersion) .withoutMetrics(); // The driver uses metrics 3 with conflict with our version + if (allowServerPortDiscovery) + clusterBuilder = clusterBuilder.allowServerPortDiscovery(); + if (loadBalancingPolicy != null) clusterBuilder.withLoadBalancingPolicy(loadBalancingPolicy); clusterBuilder.withCompression(compression); @@ -166,7 +171,7 @@ public class JavaDriverClient for (Host host : metadata.getAllHosts()) { System.out.printf("Datacenter: %s; Host: %s; Rack: %s%n", - host.getDatacenter(), host.getAddress(), host.getRack()); + host.getDatacenter(), host.getAddress() + ":" + host.getSocketAddress().getPort(), host.getRack()); } session = cluster.connect();