/*
* JBoss, Home of Professional Open Source
* Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.messaging.tests.performance.persistence;
import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
import org.jboss.messaging.core.config.impl.FileConfiguration;
import org.jboss.messaging.core.logging.Logger;
import org.jboss.messaging.core.persistence.impl.journal.JournalStorageManager;
import org.jboss.messaging.core.remoting.impl.ByteBufferWrapper;
import org.jboss.messaging.core.remoting.impl.mina.IoBufferWrapper;
import org.jboss.messaging.core.server.JournalType;
import org.jboss.messaging.core.server.Queue;
import org.jboss.messaging.core.server.impl.ServerMessageImpl;
import org.jboss.messaging.tests.util.UnitTestCase;
import org.jboss.messaging.util.SimpleString;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author <a href="mailto:clebert.suconic@jboss.com">Clebert Suconic</a>
*
*/
public class StorageManagerTimingTest extends UnitTestCase
{
private static final Logger log = Logger.getLogger(StorageManagerTimingTest.class);
protected void tearDown() throws Exception
{
super.tearDown();
assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
}
public void testAIO() throws Exception
{
// just to do some initial loading.. ignore this rate
internalTestStorage(JournalType.ASYNCIO, 1000, 1, 1);
double rate = internalTestStorage(JournalType.ASYNCIO, 60000, 1, 1)[0];
printRates("Rate of AIO, 60000 inserts / commits on every insert", rate);
rate = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 1)[0];
printRates("Rate of AIO, 30000 inserts / single commit at the end", rate);
rate = internalTestStorage(JournalType.ASYNCIO, 30000, 5, 1)[0];
printRates("Rate of AIO, 30000 inserts / commit every 5 recodds", rate);
rate = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 1)[0];
printRates("Rate of AIO, 30000 inserts / single commit at the end (again)", rate);
}
public void testAIOMultiThread() throws Exception
{
double[] rates = internalTestStorage(JournalType.ASYNCIO, 10000, -1, 1);
rates = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 5);
printRates("Rate of AIO, 30000 inserts / single commit at the end", rates);
rates = internalTestStorage(JournalType.ASYNCIO, 5000, 1, 5);
printRates("Rate of AIO, 30000 inserts / commit on every insert", rates);
}
public void testNIO() throws Exception
{
// just to do some initial loading.. ignore this rate
internalTestStorage(JournalType.NIO, 1000, 1, 1);
double rate = internalTestStorage(JournalType.NIO, 1000, 1, 1)[0];
printRates("Rate of NIO, 1000 inserts, 1000 commits", rate);
rate = internalTestStorage(JournalType.NIO, 30000, -1, 1)[0];
printRates("Rate of NIO, 30000 inserts / single commit at the end", rate);
rate = internalTestStorage(JournalType.NIO, 30000, 5, 1)[0];
printRates("Rate of NIO, 30000 inserts / commit every 5 records", rate);
}
public void testNIOMultiThread() throws Exception
{
double[] rates = internalTestStorage(JournalType.NIO, 5000, -1, 5);
printRates("Rate of NIO, 5000 inserts / single commit at the end", rates);
rates = internalTestStorage(JournalType.NIO, 5000, 1, 5);
printRates("Rate of NIO, 5000 inserts / commit on every insert", rates);
}
public double[] internalTestStorage(final JournalType journalType,
final long numberOfMessages,
final int transInterval,
final int numberOfThreads) throws Exception
{
FileConfiguration configuration = new FileConfiguration();
configuration.start();
deleteDirectory(new File(configuration.getBindingsDirectory()));
deleteDirectory(new File(configuration.getJournalDirectory()));
configuration.setJournalType(journalType);
final JournalStorageManager journal = new JournalStorageManager(configuration);
journal.start();
FakePostOffice office = new FakePostOffice();
HashMap<Long, Queue> queues = new HashMap<Long, Queue>();
journal.loadMessages(office, queues, null);
final byte[] bytes = new byte[900];
for (int i=0;i<bytes.length;i++)
{
bytes[i] = (byte)('a' + (i%20));
}
final IoBufferWrapper buffer = new IoBufferWrapper(1024);
buffer.putBytes(bytes);
final AtomicLong transactionGenerator = new AtomicLong(1);
class LocalThread extends Thread
{
int id;
int commits = 1;
Exception e;
long totalTime = 0;
public LocalThread(int id)
{
super("LocalThread:" + id);
this.id = id;
}
public void run()
{
try
{
long start = System.currentTimeMillis();
long trans = transactionGenerator.incrementAndGet();
boolean commitPending=false;
for (long i=1;i<=numberOfMessages;i++)
{
final SimpleString address = new SimpleString("Destination " + i);
ServerMessageImpl implMsg = new ServerMessageImpl(/* type */ (byte)1, /* durable */ true, /* expiration */ 0,
/* timestamp */ 0, /* priority */(byte)0, new ByteBufferWrapper(ByteBuffer.allocateDirect(1024)));
implMsg.putStringProperty(new SimpleString("Key"), new SimpleString("This String is worthless!"));
implMsg.setMessageID(i);
implMsg.setBody(buffer);
implMsg.setDestination(address);
journal.storeMessageTransactional(trans, implMsg);
commitPending = true;
if (transInterval>0 && i%transInterval == 0)
{
journal.commit(trans);
commits ++;
trans = transactionGenerator.incrementAndGet();
commitPending = false;
}
}
if (commitPending) journal.commit(trans);
long end = System.currentTimeMillis();
totalTime = end - start;
}
catch (Exception e)
{
log.error(e.getMessage(), e);
this.e = e;
}
}
}
try
{
LocalThread[] threads = new LocalThread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++)
{
threads[i] = new LocalThread(i);
}
for (int i = 0; i < numberOfThreads; i++)
{
threads[i].start();
}
for (int i = 0; i < numberOfThreads; i++)
{
threads[i].join();
}
for (int i = 0; i < numberOfThreads; i++)
{
if (threads[i].e != null)
{
throw threads[i].e;
}
}
double rates[] = new double[numberOfThreads];
for (int i=0; i<numberOfThreads; i++)
{
rates[i] = (numberOfMessages + threads[i].commits) * 1000 / threads[i].totalTime;
}
return rates;
}
finally
{
journal.stop();
}
}
private void printRates(String msg, double rate)
{
printRates(msg, new double[] { rate });
}
private void printRates(String msg, double[] rates)
{
double rate = 0;
log.info("*************************************************************************");
log.info(" " + msg + " ");
double totalRate = 0;
for (int i=0; i<rates.length; i++)
{
rate = rates[i];
totalRate += rate;
if (rates.length>1)
{
log.info( " Thread " + i + ": = " + rate + " inserts/sec (including commits)");
}
}
log.info( " Total rate : = " + totalRate + " inserts/sec (including commits)");
log.info("*************************************************************************");
}
}