/** * Copyright (c) 2011-2012 Optimax Software Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Optimax Software, ElasticInbox, nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.elasticinbox.core.cassandra.utils; import static com.elasticinbox.core.cassandra.CassandraDAOFactory.CF_LABEL_INDEX; import static me.prettyprint.hector.api.factory.HFactory.createColumn; import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertNotNull; import java.util.HashSet; import java.util.UUID; import me.prettyprint.cassandra.serializers.BytesArraySerializer; import me.prettyprint.cassandra.serializers.StringSerializer; import me.prettyprint.cassandra.serializers.UUIDSerializer; import me.prettyprint.cassandra.service.CassandraHostConfigurator; import me.prettyprint.hector.api.Cluster; import me.prettyprint.hector.api.ConsistencyLevelPolicy; import me.prettyprint.hector.api.Keyspace; import me.prettyprint.hector.api.beans.ColumnSlice; import me.prettyprint.hector.api.beans.HColumn; import me.prettyprint.hector.api.factory.HFactory; import me.prettyprint.hector.api.mutation.Mutator; import me.prettyprint.hector.api.query.QueryResult; import me.prettyprint.hector.api.query.SliceQuery; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.elasticinbox.core.cassandra.utils.QuorumConsistencyLevel; import com.elasticinbox.core.cassandra.utils.ThrottlingMutator; import com.elasticinbox.core.message.id.MessageIdBuilder; public class ThrottlingMutatorTest { final static StringSerializer strSe = StringSerializer.get(); final static UUIDSerializer uuidSe = UUIDSerializer.get(); final static BytesArraySerializer byteSe = BytesArraySerializer.get(); final static String KEYSPACE = "ElasticInbox"; final static int LABEL_T1 = 5555; final static int LABEL_T2 = 5000; final static String MAILBOX = "throttling@elasticinbox.com"; final static String KEY_T1 = MAILBOX + ":" + LABEL_T1; final static String KEY_T2 = MAILBOX + ":" + LABEL_T2; Cluster cluster; Keyspace keyspace; @Before public void setupCase() { // Consistency Level Policy ConsistencyLevelPolicy clp = new QuorumConsistencyLevel(); // Host config CassandraHostConfigurator conf = new CassandraHostConfigurator("127.0.0.1:9160"); cluster = HFactory.getOrCreateCluster("TestCluster", conf); keyspace = HFactory.createKeyspace(KEYSPACE, cluster, clp); // clenaup from previous runs Mutator<String> m = new ThrottlingMutator<String>(keyspace, strSe, 50, 500L); m.addDeletion(KEY_T1, CF_LABEL_INDEX, null, strSe); m.addDeletion(KEY_T2, CF_LABEL_INDEX, null, strSe); m.execute(); } @After public void teardownCase() { keyspace = null; cluster = null; } /** * Test throttler's delay functionality. */ @Test public void testThrottlingMutatorDelay() { // throttle at 100 ops/ 500 ms ThrottlingMutator<String> m = new ThrottlingMutator<String>(keyspace, strSe, 100, 500L); long ts = System.currentTimeMillis(); // should take 1 sec to insert 200 cols at 5ms rate for (int i = 0; i < 201; i++) { UUID uuid = new MessageIdBuilder().build(); m.addInsertion(KEY_T1, CF_LABEL_INDEX, createColumn(uuid, new byte[0], uuidSe, byteSe)); m.executeIfFull(); } m.execute(); long elapsed = System.currentTimeMillis() - ts; // check if it took more than 1 sec and no more than 1.2 sec assertThat(elapsed, greaterThan(1000L)); assertThat(elapsed, lessThan(1200L)); } @Test public void testThrottlingMutatorConsistency() { int sampleCount = 250; // throttle at 50 ops/ 100 ms ThrottlingMutator<String> m = new ThrottlingMutator<String>(keyspace, strSe, 100, 500L); HashSet<UUID> messageIds = new HashSet<UUID>(); final byte[] value = "consistent".getBytes(); // STEP: add samples for (int i = 0; i < sampleCount; i++) { UUID uuid = new MessageIdBuilder().build(); m.addInsertion(KEY_T2, CF_LABEL_INDEX, createColumn(uuid, value, uuidSe, byteSe)); messageIds.add(uuid); m.executeIfFull(); } m.execute(); // STEP: validate additions SliceQuery<String, UUID, byte[]> q = createSliceQuery(keyspace, strSe, uuidSe, byteSe); q.setColumnFamily(CF_LABEL_INDEX); q.setKey(KEY_T2); q.setRange(null, null, false, 500); QueryResult<ColumnSlice<UUID, byte[]>> r = q.execute(); for (HColumn<UUID, byte[]> c : r.get().getColumns()) { assertNotNull(c); assertNotNull(c.getValue()); assertThat(messageIds, hasItem(c.getName())); assertThat(value, equalTo(c.getValue())); } assertThat(sampleCount, equalTo(r.get().getColumns().size())); // STEP: delete samples for (UUID uuid : messageIds) { m.addDeletion(KEY_T2, CF_LABEL_INDEX, uuid, uuidSe); m.executeIfFull(); } m.execute(); // STEP: validate deletions r = q.execute(); assertThat(0, equalTo(r.get().getColumns().size())); } }