/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.ha.pipeline;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.bigdata.ha.msg.HAMessageWrapper;
import com.bigdata.io.DirectBufferPool;
import com.bigdata.io.IBufferAccess;
/**
* Test the raw socket protocol implemented by {@link HASendService} and
* {@link HAReceiveService}.
*
* @author martyn Cutcher
*/
public class TestHASendAndReceive extends AbstractHASendAndReceiveTestCase {
public TestHASendAndReceive() {
}
public TestHASendAndReceive(String name) {
super(name);
}
private HASendService sendService;
private HAReceiveService<HAMessageWrapper> receiveService;
@Override
protected void setUp() throws Exception {
super.setUp();
r = new Random();
/*
* Note: ZERO (0) indicates that a random free port will be selected. If
* you use a fixed port then there is a danger that the port will not be
* able to be reopened immediately after it has been closed, in which
* case you will see something like: "bind address already in use".
*/
final int port = getPort(0);// 3000
if (log.isInfoEnabled())
log.info("Using port=" + port);
final InetSocketAddress addr = new InetSocketAddress(port);
receiveService = new HAReceiveService<HAMessageWrapper>(addr, null);
receiveService.start();
sendService = new HASendService();
sendService.start(addr);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
if (receiveService != null) {
receiveService.terminate();
receiveService = null;
}
if (sendService != null) {
// sendService.closeIncSend();
sendService.terminate();
sendService = null;
}
}
/**
* Should we expect concurrency of the Socket send and RMI? It seems that we
* should be able to handle it whatever the logical argument. The only
* constraint should be on the processing of each pair of socket/RMI
* interactions. OTOH, if we are intending to process the OP_ACCEPT and
* OP_READ within the ReadTask that can only be processed AFTER the RMI is
* received, then we should not sen the socket until we have a returned
* FutureTask.
*
* @throws ExecutionException
* @throws InterruptedException
* @throws TimeoutException
* @throws ImmediateDownstreamReplicationException
*/
public void testSimpleExchange() throws InterruptedException, ExecutionException, TimeoutException, ImmediateDownstreamReplicationException {
final long timeout = 5000;// ms
{
final ByteBuffer tst1 = getRandomData(50);
final HAMessageWrapper msg1 = newHAWriteMessage(50, tst1);
final ByteBuffer rcv = ByteBuffer.allocate(2000);
final Future<Void> futRec = receiveService.receiveData(msg1, rcv);
final Future<Void> futSnd = sendService.send(tst1, msg1.getMarker());
futSnd.get(timeout,TimeUnit.MILLISECONDS);
futRec.get(timeout,TimeUnit.MILLISECONDS);
assertEquals(tst1, rcv);
}
{
final ByteBuffer tst2 = getRandomData(100);
final HAMessageWrapper msg2 = newHAWriteMessage(100, tst2);
final ByteBuffer rcv2 = ByteBuffer.allocate(2000);
final Future<Void> futSnd = sendService.send(tst2, msg2.getMarker());
final Future<Void> futRec = receiveService.receiveData(msg2, rcv2);
futSnd.get(timeout,TimeUnit.MILLISECONDS);
futRec.get(timeout,TimeUnit.MILLISECONDS);
assertEquals(tst2, rcv2);
}
}
/**
* Sends a large number of random buffers, confirming successful
* transmission.
*
* @throws TimeoutException
* @throws ExecutionException
* @throws InterruptedException
* @throws ImmediateDownstreamReplicationException
*/
public void testStress() throws TimeoutException, InterruptedException,
ExecutionException, ImmediateDownstreamReplicationException {
final long timeout = 5000; // ms
for (int i = 0; i < 100; i++) {
final int sze = 10000 + r.nextInt(300000);
final ByteBuffer tst = getRandomData(sze);
final HAMessageWrapper msg = newHAWriteMessage(sze, tst);
final ByteBuffer rcv = ByteBuffer.allocate(sze + r.nextInt(1024));
// FutureTask return ensures remote ready for Socket data
final Future<Void> futRec = receiveService.receiveData(msg, rcv);
final Future<Void> futSnd = sendService.send(tst, msg.getMarker());
futSnd.get(timeout,TimeUnit.MILLISECONDS);
futRec.get(timeout,TimeUnit.MILLISECONDS);
assertEquals(tst, rcv); // make sure buffer has been transmitted
}
}
/**
* Sends a large number of random buffers, confirming successful
* transmission.
*
* @throws InterruptedException
*/
public void testStressDirectBuffers() throws InterruptedException {
final long timeout = 5000; // ms
IBufferAccess tstdb = null, rcvdb = null;
int i = -1, sze = -1;
try {
tstdb = DirectBufferPool.INSTANCE.acquire();
rcvdb = DirectBufferPool.INSTANCE.acquire();
final ByteBuffer tst = tstdb.buffer();
final ByteBuffer rcv = rcvdb.buffer();
for (i = 0; i < 1000; i++) {
sze = 1 + r.nextInt(tst.capacity());
getRandomData(tst, sze);
final HAMessageWrapper msg = newHAWriteMessage(sze, tst);
assertEquals(0,tst.position());
assertEquals(sze,tst.limit());
// FutureTask return ensures remote ready for Socket data
final Future<Void> futRec = receiveService.receiveData(msg, rcv);
final Future<Void> futSnd = sendService.send(tst, msg.getMarker());
futSnd.get(timeout,TimeUnit.MILLISECONDS);
futRec.get(timeout,TimeUnit.MILLISECONDS);
// make sure buffer has been transmitted
assertEquals(tst, rcv);
if (log.isInfoEnabled() && (i<10 || i % 10 == 0))
log.info("Looks good for #" + i);
}
} catch (Throwable t) {
throw new RuntimeException("i=" + i + ", sze=" + sze + " : " + t, t);
} finally {
try {
if (tstdb != null) {
tstdb.release();
}
} finally {
if (rcvdb != null) {
rcvdb.release();
}
}
}
}
}