/* * 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.service; import java.net.InetAddress; import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; 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.TokenMetadata; import org.apache.cassandra.net.MessageIn; import org.apache.cassandra.schema.KeyspaceParams; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class WriteResponseHandlerTest { static Keyspace ks; static ColumnFamilyStore cfs; static List<InetAddress> targets; @BeforeClass public static void setUpClass() throws Throwable { SchemaLoader.loadSchema(); // 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")); DatabaseDescriptor.setEndpointSnitch(new IEndpointSnitch() { public String getRack(InetAddress endpoint) { return null; } public String getDatacenter(InetAddress endpoint) { byte[] address = endpoint.getAddress(); if (address[1] == 1) return "datacenter1"; else return "datacenter2"; } public List<InetAddress> getSortedListByProximity(InetAddress address, Collection<InetAddress> unsortedAddress) { return null; } public void sortByProximity(InetAddress address, List<InetAddress> addresses) { } public int compareEndpoints(InetAddress target, InetAddress a1, InetAddress a2) { return 0; } public void gossiperStarting() { } public boolean isWorthMergingForRangeQuery(List<InetAddress> merged, List<InetAddress> l1, List<InetAddress> l2) { return false; } }); DatabaseDescriptor.setBroadcastAddress(InetAddress.getByName("127.1.0.1")); 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")); } @Before public void resetCounters() { ks.metric.writeFailedIdealCL.dec(ks.metric.writeFailedIdealCL.getCount()); } /** * Validate that a successful write at ideal CL logs latency information. Also validates * DatacenterSyncWriteResponseHandler * @throws Throwable */ @Test public void idealCLLatencyTracked() throws Throwable { long startingCount = ks.metric.idealCLWriteLatency.latency.getCount(); //Specify query start time in past to ensure minimum latency measurement AbstractWriteResponseHandler awr = createWriteResponseHandler(ConsistencyLevel.LOCAL_QUORUM, ConsistencyLevel.EACH_QUORUM, System.nanoTime() - TimeUnit.DAYS.toNanos(1)); //dc1 awr.response(createDummyMessage(0)); awr.response(createDummyMessage(1)); //dc2 awr.response(createDummyMessage(4)); awr.response(createDummyMessage(5)); //Don't need the others awr.expired(); awr.expired(); assertEquals(0, ks.metric.writeFailedIdealCL.getCount()); assertTrue( TimeUnit.DAYS.toMicros(1) < ks.metric.idealCLWriteLatency.totalLatency.getCount()); assertEquals(startingCount + 1, ks.metric.idealCLWriteLatency.latency.getCount()); } /** * Validate that WriteResponseHandler does the right thing on success. * @throws Throwable */ @Test public void idealCLWriteResponeHandlerWorks() throws Throwable { long startingCount = ks.metric.idealCLWriteLatency.latency.getCount(); AbstractWriteResponseHandler awr = createWriteResponseHandler(ConsistencyLevel.LOCAL_QUORUM, ConsistencyLevel.ALL); //dc1 awr.response(createDummyMessage(0)); awr.response(createDummyMessage(1)); awr.response(createDummyMessage(2)); //dc2 awr.response(createDummyMessage(3)); awr.response(createDummyMessage(4)); awr.response(createDummyMessage(5)); assertEquals(0, ks.metric.writeFailedIdealCL.getCount()); assertEquals(startingCount + 1, ks.metric.idealCLWriteLatency.latency.getCount()); } /** * Validate that DatacenterWriteResponseHandler does the right thing on success. * @throws Throwable */ @Test public void idealCLDatacenterWriteResponeHandlerWorks() throws Throwable { long startingCount = ks.metric.idealCLWriteLatency.latency.getCount(); AbstractWriteResponseHandler awr = createWriteResponseHandler(ConsistencyLevel.ONE, ConsistencyLevel.LOCAL_QUORUM); //dc1 awr.response(createDummyMessage(0)); awr.response(createDummyMessage(1)); awr.response(createDummyMessage(2)); //dc2 awr.response(createDummyMessage(3)); awr.response(createDummyMessage(4)); awr.response(createDummyMessage(5)); assertEquals(0, ks.metric.writeFailedIdealCL.getCount()); assertEquals(startingCount + 1, ks.metric.idealCLWriteLatency.latency.getCount()); } /** * Validate that failing to achieve ideal CL increments the failure counter * @throws Throwable */ @Test public void failedIdealCLIncrementsStat() throws Throwable { AbstractWriteResponseHandler awr = createWriteResponseHandler(ConsistencyLevel.LOCAL_QUORUM, ConsistencyLevel.EACH_QUORUM); //Succeed in local DC awr.response(createDummyMessage(0)); awr.response(createDummyMessage(1)); awr.response(createDummyMessage(2)); //Fail in remote DC awr.expired(); awr.expired(); awr.expired(); assertEquals(1, ks.metric.writeFailedIdealCL.getCount()); assertEquals(0, ks.metric.idealCLWriteLatency.totalLatency.getCount()); } private static AbstractWriteResponseHandler createWriteResponseHandler(ConsistencyLevel cl, ConsistencyLevel ideal) { return createWriteResponseHandler(cl, ideal, System.nanoTime()); } private static AbstractWriteResponseHandler createWriteResponseHandler(ConsistencyLevel cl, ConsistencyLevel ideal, long queryStartTime) { return ks.getReplicationStrategy().getWriteResponseHandler(targets, ImmutableList.of(), cl, new Runnable() { public void run() { } }, WriteType.SIMPLE, queryStartTime, ideal); } private static MessageIn createDummyMessage(int target) { return MessageIn.create(targets.get(target), null, null, null, 0, 0L); } }