package org.jgroups.protocols;
import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test the behavior of the ACK_SEQUENCER protocol layer
*
* @author Pedro Ruivo
* @since 3.1
*/
@Test(groups = Global.FUNCTIONAL, sequential = true)
public class ACK_SEQUENCERTest {
private static final int NUMBER_OF_MEMBERS = 3;
private static final short SEQUENCER_ID = 1;
private static final short ACK_SEQUENCER_ID = 2;
private ACK_SEQUENCER[] ackSequencers;
private UpProtocol[] upProtocols;
private DownProtocol[] downProtocols;
private Address[] members;
@BeforeClass
public void setUpClass() {
ackSequencers = new ACK_SEQUENCER[NUMBER_OF_MEMBERS];
upProtocols = new UpProtocol[NUMBER_OF_MEMBERS];
downProtocols = new DownProtocol[NUMBER_OF_MEMBERS];
members = new Address[NUMBER_OF_MEMBERS];
}
@BeforeMethod
public void setUpProtocols() {
for (int i = 0; i < NUMBER_OF_MEMBERS; ++i) {
members[i] = Util.createRandomAddress("A" + i);
upProtocols[i] = new UpProtocol();
downProtocols[i] = new DownProtocol();
ackSequencers[i] = new ACK_SEQUENCER();
ackSequencers[i].setUpProtocol(upProtocols[i]);
upProtocols[i].setDownProtocol(ackSequencers[i]);
SEQUENCER sequencer = new SEQUENCER();
sequencer.setDownProtocol(downProtocols[i]);
downProtocols[i].setUpProtocol(sequencer);
sequencer.setUpProtocol(ackSequencers[i]);
ackSequencers[i].setDownProtocol(sequencer);
sequencer.setId(SEQUENCER_ID);
ackSequencers[i].setId(ACK_SEQUENCER_ID);
upProtocols[i].down(new Event(Event.SET_LOCAL_ADDRESS, members[i]));
}
}
@AfterMethod
public void cleanProtocols() {
for (int i = 0; i < NUMBER_OF_MEMBERS; ++i) {
members[i] = null;
upProtocols[i] = null;
downProtocols[i] = null;
ackSequencers[i] = null;
System.gc();
}
}
public void testNonSequencerInStack() {
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
Protocol p = new UNICAST();
p.setDownProtocol(new FRAG2());
ack_sequencer.setDownProtocol(p);
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
checkSequencerHeaderID((short) -1, ack_sequencer);
Message ack = createAckMessage(members[0], 1);
String data = randomData();
ack.setObject(data);
ack_sequencer.up(new Event(Event.MSG, ack));
assert upProtocols[0].lastMessageDeliver != null : "Expected a delivered message";
assert upProtocols[0].lastMessageDeliver.getObject().equals(data) : "Wrong delivered message";
}
public void testStaticNumberOfExpectedFailedMembers() {
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
ack_sequencer.setExpectedNumberOfFailedMembers(5);
ack_sequencer.setPercentageOfFailedMembers(-1);
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
checkSequencerHeaderID(SEQUENCER_ID, ack_sequencer);
checkActualNumberOfMembers(0, ack_sequencer);
checkExpectedFailedMembers(5, ack_sequencer);
List<Address> viewMember = new LinkedList<Address>();
viewMember.add(members[0]);
View view = new View(members[0], 1, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(1, ack_sequencer);
checkExpectedFailedMembers(5, ack_sequencer);
checkCoordinator(ack_sequencer);
for (int i = 2; i <= 10; ++i) {
viewMember.add(Util.createRandomAddress("B" + i));
view = new View(members[0], i, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(i, ack_sequencer);
checkExpectedFailedMembers(5, ack_sequencer);
checkCoordinator(ack_sequencer);
}
}
public void testDynamicNumberOfExpectedFailedMembers() {
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
ack_sequencer.setExpectedNumberOfFailedMembers(0);
ack_sequencer.setPercentageOfFailedMembers(0.5); //50% + 1
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
checkSequencerHeaderID(SEQUENCER_ID, ack_sequencer);
checkActualNumberOfMembers(0, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
List<Address> viewMember = new LinkedList<Address>();
viewMember.add(members[0]);
View view = new View(members[0], 1, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(1, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
checkCoordinator(ack_sequencer);
for (int i = 2; i <= 10; ++i) {
viewMember.add(Util.createRandomAddress("B" + i));
view = new View(members[0], i, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(i, ack_sequencer);
checkExpectedFailedMembers(i / 2 + 1, ack_sequencer);
checkCoordinator(ack_sequencer);
}
}
public void testDynamicNumberOfExpectedFailedMembers2() {
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
ack_sequencer.setExpectedNumberOfFailedMembers(0);
ack_sequencer.setPercentageOfFailedMembers(0); //0% + 1
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
checkSequencerHeaderID(SEQUENCER_ID, ack_sequencer);
checkActualNumberOfMembers(0, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
List<Address> viewMember = new LinkedList<Address>();
viewMember.add(members[0]);
View view = new View(members[0], 1, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(1, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
checkCoordinator(ack_sequencer);
for (int i = 2; i <= 10; ++i) {
viewMember.add(Util.createRandomAddress("B" + i));
view = new View(members[0], i, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(i, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
checkCoordinator(ack_sequencer);
}
}
public void testDynamicNumberOfExpectedFailedMembers3() {
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
ack_sequencer.setExpectedNumberOfFailedMembers(0);
ack_sequencer.setPercentageOfFailedMembers(1); //100% + 1
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
checkSequencerHeaderID(SEQUENCER_ID, ack_sequencer);
checkActualNumberOfMembers(0, ack_sequencer);
checkExpectedFailedMembers(1, ack_sequencer);
List<Address> viewMember = new LinkedList<Address>();
viewMember.add(members[0]);
View view = new View(members[0], 1, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(1, ack_sequencer);
checkExpectedFailedMembers(2, ack_sequencer);
checkCoordinator(ack_sequencer);
for (int i = 2; i <= 10; ++i) {
viewMember.add(Util.createRandomAddress("B" + i));
view = new View(members[0], i, viewMember);
ack_sequencer.up(new Event(Event.VIEW_CHANGE, view));
checkActualNumberOfMembers(i, ack_sequencer);
checkExpectedFailedMembers(i+1, ack_sequencer);
checkCoordinator(ack_sequencer);
}
}
public void testNumberOfExpectedFailedMembers() {
initAckSequencers();
ACK_SEQUENCER ack_sequencer = ackSequencers[0];
ack_sequencer.setExpectedNumberOfFailedMembers(0);
checkExpectedFailedMembers(0, ack_sequencer);
ack_sequencer.setExpectedNumberOfFailedMembers(10);
checkExpectedFailedMembers(10, ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(0);
checkExpectedFailedMembers(1, ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(0.34);
checkExpectedFailedMembers(2, ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(0.5);
checkExpectedFailedMembers(2, ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(0.67);
checkExpectedFailedMembers(3, ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(1);
checkExpectedFailedMembers(4, ack_sequencer);
ack_sequencer.setExpectedNumberOfFailedMembers(5);
checkExpectedFailedMembers(5, ack_sequencer);
}
public void testNoAcksNeeded() {
initAckSequencers();
setNumberOfExpectedFailedMembers(0);
Message sentMsg = sendBroadcast(0);
checkMessageSent(0);
final Message broadcast = getAndRemoveMessage(downProtocols[0]);
final CountDownLatch countDownLatch = new CountDownLatch(NUMBER_OF_MEMBERS);
for (final DownProtocol downProtocol : downProtocols) {
new Thread() {
@Override
public void run() {
downProtocol.up(new Event(Event.MSG, broadcast));
countDownLatch.countDown();
}
}.start();
}
try {
countDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in all members";
}
for (int i = 0; i < NUMBER_OF_MEMBERS; ++i) {
checkMessageDelivered(i);
Message deliveredMsg = getAndRemoveMessage(upProtocols[i]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
}
checkNoMessageSent(0);
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
checkMessageSent(i);
Message msg = getAndRemoveMessage(downProtocols[i]);
assert msg.getHeader(ACK_SEQUENCER_ID) != null : "Expected an ack for *no* coordinators";
}
}
public void testOneAcksNeeded() {
initAckSequencers();
setNumberOfExpectedFailedMembers(1);
Message sentMsg = sendBroadcast(0);
checkMessageSent(0);
final Message broadcast = getAndRemoveMessage(downProtocols[0]);
final CountDownLatch noCoordinatorCountDownLatch = new CountDownLatch(NUMBER_OF_MEMBERS - 1);
final CountDownLatch coordinatorCountDownLatch = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
downProtocols[0].up(new Event(Event.MSG, broadcast));
coordinatorCountDownLatch.countDown();
}
}.start();
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
final int finalI = i;
new Thread() {
@Override
public void run() {
downProtocols[finalI].up(new Event(Event.MSG, broadcast));
noCoordinatorCountDownLatch.countDown();
}
}.start();
}
try {
noCoordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in *no* coordinator members";
}
final Message[] acks = new Message[NUMBER_OF_MEMBERS - 1];
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
checkMessageDelivered(i);
Message deliveredMsg = getAndRemoveMessage(upProtocols[i]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkMessageSent(i);
Message msg = getAndRemoveMessage(downProtocols[i]);
assert msg.getHeader(ACK_SEQUENCER_ID) != null : "Expected an ack for *no* coordinator member";
acks[i-1] = msg;
}
//message must be blocked in coordinator
checkNoMessageDelivered(0);
assert coordinatorCountDownLatch.getCount() == 1 : "Expected a blocked coordinator";
downProtocols[0].up(new Event(Event.MSG, acks[0]));
try {
coordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in coordinator members";
}
checkMessageDelivered(0);
Message deliveredMsg = getAndRemoveMessage(upProtocols[0]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkNoMessageSent(0);
}
public void testSameNumberOfAcksNeededAsMembers() {
initAckSequencers();
setNumberOfExpectedFailedMembers(NUMBER_OF_MEMBERS);
Message sentMsg = sendBroadcast(0);
checkMessageSent(0);
final Message broadcast = getAndRemoveMessage(downProtocols[0]);
final CountDownLatch noCoordinatorCountDownLatch = new CountDownLatch(NUMBER_OF_MEMBERS - 1);
final CountDownLatch coordinatorCountDownLatch = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
downProtocols[0].up(new Event(Event.MSG, broadcast));
coordinatorCountDownLatch.countDown();
}
}.start();
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
final int finalI = i;
new Thread() {
@Override
public void run() {
downProtocols[finalI].up(new Event(Event.MSG, broadcast));
noCoordinatorCountDownLatch.countDown();
}
}.start();
}
try {
noCoordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in *no* coordinator members";
}
Message[] acks = new Message[NUMBER_OF_MEMBERS - 1];
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
checkMessageDelivered(i);
Message deliveredMsg = getAndRemoveMessage(upProtocols[i]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkMessageSent(i);
Message msg = getAndRemoveMessage(downProtocols[i]);
assert msg.getHeader(ACK_SEQUENCER_ID) != null : "Expected an ack for *no* coordinator member";
acks[i-1] = msg;
}
for (final Message ack : acks) {
//message must be blocked in coordinator
checkNoMessageDelivered(0);
assert coordinatorCountDownLatch.getCount() == 1 : "Expected a blocked coordinator";
downProtocols[0].up(new Event(Event.MSG, ack));
}
try {
coordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in coordinator members";
}
checkMessageDelivered(0);
Message deliveredMsg = getAndRemoveMessage(upProtocols[0]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkNoMessageSent(0);
}
public void testHighNumberOfAcksNeededThanMembers() {
initAckSequencers();
setNumberOfExpectedFailedMembers(NUMBER_OF_MEMBERS + 2);
Message sentMsg = sendBroadcast(0);
checkMessageSent(0);
final Message broadcast = getAndRemoveMessage(downProtocols[0]);
final CountDownLatch noCoordinatorCountDownLatch = new CountDownLatch(NUMBER_OF_MEMBERS - 1);
final CountDownLatch coordinatorCountDownLatch = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
downProtocols[0].up(new Event(Event.MSG, broadcast));
coordinatorCountDownLatch.countDown();
}
}.start();
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
final int finalI = i;
new Thread() {
@Override
public void run() {
downProtocols[finalI].up(new Event(Event.MSG, broadcast));
noCoordinatorCountDownLatch.countDown();
}
}.start();
}
try {
noCoordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in *no* coordinator members";
}
Message[] acks = new Message[NUMBER_OF_MEMBERS - 1];
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
checkMessageDelivered(i);
Message deliveredMsg = getAndRemoveMessage(upProtocols[i]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkMessageSent(i);
Message msg = getAndRemoveMessage(downProtocols[i]);
assert msg.getHeader(ACK_SEQUENCER_ID) != null : "Expected an ack for *no* coordinator member";
acks[i-1] = msg;
}
for (final Message ack : acks) {
//message must be blocked in coordinator
checkNoMessageDelivered(0);
assert coordinatorCountDownLatch.getCount() == 1 : "Expected a blocked coordinator";
downProtocols[0].up(new Event(Event.MSG, ack));
}
try {
coordinatorCountDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Delivered message expected in coordinator members";
}
checkMessageDelivered(0);
Message deliveredMsg = getAndRemoveMessage(upProtocols[0]);
assert deliveredMsg.equals(sentMsg) : "Delivered message is different from the sent message";
checkNoMessageSent(0);
}
private Message getAndRemoveMessage(DownProtocol downProtocol) {
Message msg = downProtocol.lastMessageSent;
downProtocol.lastMessageSent = null;
return msg;
}
private Message getAndRemoveMessage(UpProtocol upProtocol) {
Message msg = upProtocol.lastMessageDeliver;
upProtocol.lastMessageDeliver = null;
return msg;
}
private Message sendBroadcast(int idx) {
Message msg = new Message();
msg.setSrc(members[idx]);
msg.setDest(null);
msg.setObject(randomData());
upProtocols[idx].down(new Event(Event.MSG, msg));
return msg;
}
private void setNumberOfExpectedFailedMembers(int numberOfExpectedFailedMembers) {
for (ACK_SEQUENCER ack_sequencer : ackSequencers) {
ack_sequencer.setExpectedNumberOfFailedMembers(numberOfExpectedFailedMembers);
}
}
private void initAckSequencers() {
for (ACK_SEQUENCER ack_sequencer : ackSequencers) {
try {
ack_sequencer.start();
} catch (Exception e) {
assert false : "Exception while starting the ACK_SEQUENCER. " + e.getLocalizedMessage();
}
}
List<Address> viewMembers = Arrays.asList(members);
View view = new View(members[0], 1, viewMembers);
for (Protocol p : upProtocols) {
p.down(new Event(Event.VIEW_CHANGE, view));
}
checkSequencerHeaderID(SEQUENCER_ID, ackSequencers[0]);
checkCoordinator(ackSequencers[0]);
for (ACK_SEQUENCER ack_sequencer : ackSequencers) {
checkActualNumberOfMembers(viewMembers.size(), ack_sequencer);
ack_sequencer.setPercentageOfFailedMembers(-1);
}
for (int i = 1; i < NUMBER_OF_MEMBERS; ++i) {
checkNoCoordinator(ackSequencers[i]);
checkCoordinatorAddress(ackSequencers[i]);
}
}
private void checkMessageSent(int idx) {
assert downProtocols[idx].lastMessageSent != null : "Expected a sent message for index " + idx;
}
private void checkMessageDelivered(int idx) {
assert upProtocols[idx].lastMessageDeliver != null : "Expected a delivered message for index " + idx;
}
private void checkNoMessageSent(int idx) {
assert downProtocols[idx].lastMessageSent == null : "Expected *no* sent message for index " + idx +
". Message sent is " + downProtocols[idx].lastMessageSent;
}
private void checkNoMessageDelivered(int idx) {
assert upProtocols[idx].lastMessageDeliver == null : "Expected *no* delivered message for index " + idx +
". Message delivered is " + upProtocols[idx].lastMessageDeliver;
}
private void checkSequencerHeaderID(short expected, ACK_SEQUENCER ack_sequencer) {
assert expected == ack_sequencer.getSequencerHeaderID() : "Wrong SEQUENCER header ID. " + expected +
"!=" + ack_sequencer.getSequencerHeaderID();
}
private void checkCoordinator(ACK_SEQUENCER ack_sequencer) {
assert ack_sequencer.isCoordinator() : "It is expected to be the coordinator";
}
private void checkNoCoordinator(ACK_SEQUENCER ack_sequencer) {
assert !ack_sequencer.isCoordinator() : "It is expected *not* to be the coordinator";
}
private void checkCoordinatorAddress(ACK_SEQUENCER ack_sequencer) {
assert members[0].equals(ack_sequencer.getCoordinatorAddress()) : "Wrong coordinator address. " + members[0] +
"!=" + ack_sequencer.getCoordinatorAddress();
}
private void checkActualNumberOfMembers(int expected, ACK_SEQUENCER ack_sequencer) {
assert expected == ack_sequencer.getActualNumberOfMembers() : "Wrong number of actual members. " + expected +
"!=" + ack_sequencer.getActualNumberOfMembers();
}
private void checkExpectedFailedMembers(int expected, ACK_SEQUENCER ack_sequencer) {
assert expected == ack_sequencer.getExpectedNumberOfFailedMembers() : "Wrong number of expected failed members " +
expected + "!=" + ack_sequencer.getExpectedNumberOfFailedMembers();
}
private Message createAckMessage(Address originalSender, long seqNo) {
Message msg = new Message();
ACK_SEQUENCER.AckSequencerHeader header = new ACK_SEQUENCER.AckSequencerHeader(originalSender, seqNo);
msg.putHeader(ACK_SEQUENCER_ID, header);
return msg;
}
private String randomData() {
return Integer.toOctalString(new Random().nextInt());
}
private class DownProtocol extends Protocol {
volatile Message lastMessageSent;
@Override
public Object down(Event evt) {
if (evt.getType() == Event.MSG) {
lastMessageSent = (Message) evt.getArg();
}
return null;
}
}
private class UpProtocol extends Protocol {
volatile Message lastMessageDeliver;
@Override
public Object up(Event evt) {
if (evt.getType() == Event.MSG) {
lastMessageDeliver = (Message) evt.getArg();
}
return null;
}
}
}