/*
* 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.hints;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicIntegerArray;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.cassandra.CleanupHelper;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.db.Table;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
import org.junit.Test;
public class HintLogTest extends CleanupHelper
{
@Test
public void testCleanup() throws IOException, ExecutionException, InterruptedException, ConfigurationException
{
HintLog.setSegmentSize(128*1024);
HintLog.setSyncPeiod(100);
HintLog.setHintDelivery(false);
DatabaseDescriptor.setHintedHandoffManager("hintlog");
DatabaseDescriptor.setHintLogPlayBatchSize(7);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
unregisterBean(mbs);
HintLog hl = new HintLog();
unregisterBean(mbs);
InetAddress local = InetAddress.getByName("127.0.0.1");
if (!StorageService.instance.getTokenMetadata().isMember(local))
{
StorageService.instance.getTokenMetadata().updateNormalToken(StorageService.getPartitioner().getTokenFactory().fromString("00"), local);
}
assert hl.getSegmentCount(local) == 1;
Iterator<List<byte[]>> iterator = hl.getHintsToDeliver(local);
assert !iterator.hasNext();
Table table = Table.open("Keyspace1");
ColumnFamilyStore store1 = table.getColumnFamilyStore("Standard1");
ColumnFamilyStore store2 = table.getColumnFamilyStore("Standard2");
RowMutation rm;
// add data. use relatively large values to force quick segment creation since we have a low flush threshold in the test config.
byte[] value = new byte[1024];
rm = new RowMutation("Keyspace1", "key1");
rm.add(new QueryPath("Standard1", null, "Column1".getBytes()), value, 0);
rm.add(new QueryPath("Standard2", null, "Column1".getBytes()), value, 0);
for (int i = 0; i < 1290; i++)
{
value[0]=(byte) (i % 127);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream( bos );
RowMutation.serializer().serialize(rm, dos);
hl.add(local, bos.toByteArray() );
}
hl.forceNewSegment(local);
int segmentCount = hl.getSegmentCount(local);
assert segmentCount > 1;
iterator = hl.getHintsToDeliver(local);
assert iterator.hasNext();
assert segmentCount == hl.getSegmentCount(local) : ""+segmentCount+","+hl.instance().getSegmentCount(local) ;
int ii=0;
while (iterator.hasNext())
{
List<byte[]> batch = iterator.next();
assert !batch.isEmpty();
for (byte[] bb : batch ) {
RowMutation r = RowMutation.serializer().deserialize(new DataInputStream(new ByteArrayInputStream(bb)));
assert r.getTable().equals("Keyspace1");
assert r.key().equals("key1");
assert r.getColumnFamilies().iterator().next().getColumnsMap().values().iterator().next().value()[0]==(byte) (ii % 127);
assert r.getColumnFamilies().iterator().next().getColumnsMap().values().iterator().next().value()[0]==(byte) (ii % 127);
ii++;
}
iterator.remove();
}
assert ii == 1290;
// after all hints served all hint logs must be removed
assert hl.getSegmentCount(local) == 1;
// testing how it reads files from disk
for (int i = 1290; i < 3*1290; i++)
{
value[0]=(byte) (i % 127);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream( bos );
RowMutation.serializer().serialize(rm, dos);
hl.add(local, bos.toByteArray() );
}
segmentCount = hl.forceNewSegment(local).size();
// now it will reread from disk
unregisterBean(mbs);
hl = new HintLog();
mbs = ManagementFactory.getPlatformMBeanServer();
unregisterBean(mbs);
assert hl.getSegmentCount(local) == segmentCount + 1;
iterator = hl.getHintsToDeliver(local);
assert iterator.hasNext();
assert segmentCount + 1 == hl.getSegmentCount(local) : ""+segmentCount+","+hl.getSegmentCount(local) ;
ii=1290;
while (iterator.hasNext())
{
List<byte[]> batch = iterator.next();
assert !batch.isEmpty();
for (byte[] bb : batch ) {
RowMutation r = RowMutation.serializer().deserialize(new DataInputStream(new ByteArrayInputStream(bb)));
assert r.getTable().equals("Keyspace1");
assert r.key().equals("key1");
byte b = r.getColumnFamilies().iterator().next().getColumnsMap().values().iterator().next().value()[0];
assert b==(byte) (ii % 127) : ""+b+","+ ii%127 +" - "+ii;
byte d = r.getColumnFamilies().iterator().next().getColumnsMap().values().iterator().next().value()[0];
assert d==(byte) (ii % 127) : ""+d+","+ ii%127 +" - "+ii;
ii++;
}
iterator.remove();
}
assert ii == 3*1290;
assert hl.getSegmentCount(local) == 1;
}
private void unregisterBean(MBeanServer mbs)
{
try
{
mbs.unregisterMBean(new ObjectName("org.apache.cassandra.db:type=Hintlog"));
}
catch (Exception e)
{
}
}
@Test
public void test2() throws IOException, ExecutionException, InterruptedException, ConfigurationException
{
testCleanup();
}
public void main(String[] args)
{
for (int i=0;i<100000;i++)
{
cas();
lbq();
}
long start1 = System.nanoTime();
for (int i=10000000;i-->0;)
{
cas();
}
long end1 = System.nanoTime();
long start2 = System.nanoTime();
for (int i=10000000;i-->0;)
{
lbq();
}
long end2 = System.nanoTime();
System.out.println("CAS: "+(end1-start1)/1000000+", LBQ: "+ (end2-start2)/1000000);
}
private void lbq()
{
LinkedBlockingQueue<Object> messages = new LinkedBlockingQueue<Object>();
messages.add("akjdhakd kajdhad kajdh");
messages.size();
messages.add("akjdhakd kajdhad kajdh");
messages.size();
messages.add("akjdhakd kajdhad kajdh");
messages.size();
}
private static byte[] b1 = {1,1,1,1};
private static byte[] b2 = {1,1,1,2};
private static byte[] b3 = {1,1,1,3};
public static final int toInt(byte[] b) {
return b[0]<<24 | (b[1]&0xff)<<16 | (b[2]&0xff)<<8 | (b[3]&0xff);
}
int c=1;
private void cas()
{
AtomicIntegerArray ia = new AtomicIntegerArray(4);
ia.set(c++, FBUtilities.byteArrayToInt(b1));
ia.set(c++, FBUtilities.byteArrayToInt(b2));
ia.set(c++, FBUtilities.byteArrayToInt(b3));
ia.decrementAndGet(0);
for (int i=4, a = FBUtilities.byteArrayToInt(b1);i-->1 && !ia.compareAndSet(i, a, 0););
ia.decrementAndGet(0);
for (int i=4, a = FBUtilities.byteArrayToInt(b2);i-->1 && !ia.compareAndSet(i, a, 0););
ia.decrementAndGet(0);
for (int i=4, a = FBUtilities.byteArrayToInt(b3);i-->1 && !ia.compareAndSet(i, a, 0););
}
}