/* * 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.activemq.artemis.tests.integration.cluster.failover; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.ActiveMQDuplicateIdException; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException; import org.apache.activemq.artemis.api.core.ActiveMQTransactionOutcomeUnknownException; import org.apache.activemq.artemis.api.core.ActiveMQTransactionRolledBackException; import org.apache.activemq.artemis.api.core.Interceptor; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.TransportConfiguration; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.client.MessageHandler; import org.apache.activemq.artemis.api.core.client.ServerLocator; import org.apache.activemq.artemis.api.core.client.SessionFailureListener; import org.apache.activemq.artemis.core.client.impl.ClientSessionFactoryInternal; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.core.server.cluster.ha.BackupPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.HAPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.ReplicaPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.ReplicatedPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.SharedStoreMasterPolicy; import org.apache.activemq.artemis.core.server.cluster.ha.SharedStoreSlavePolicy; import org.apache.activemq.artemis.core.server.files.FileMoveManager; import org.apache.activemq.artemis.core.server.impl.InVMNodeManager; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.jms.client.ActiveMQTextMessage; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.integration.cluster.util.TestableServer; import org.apache.activemq.artemis.tests.util.CountDownSessionFailureListener; import org.apache.activemq.artemis.tests.util.TransportConfigurationUtils; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class FailoverTest extends FailoverTestBase { private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER; protected static final int NUM_MESSAGES = 100; protected ServerLocator locator; protected ClientSessionFactoryInternal sf; @Override @Before public void setUp() throws Exception { super.setUp(); locator = getServerLocator(); } protected ClientSession createSession(ClientSessionFactory sf1, boolean autoCommitSends, boolean autoCommitAcks, int ackBatchSize) throws Exception { return addClientSession(sf1.createSession(autoCommitSends, autoCommitAcks, ackBatchSize)); } protected ClientSession createSession(ClientSessionFactory sf1, boolean autoCommitSends, boolean autoCommitAcks) throws Exception { return addClientSession(sf1.createSession(autoCommitSends, autoCommitAcks)); } protected ClientSession createSession(ClientSessionFactory sf1) throws Exception { return addClientSession(sf1.createSession()); } protected ClientSession createSession(ClientSessionFactory sf1, boolean xa, boolean autoCommitSends, boolean autoCommitAcks) throws Exception { return addClientSession(sf1.createSession(xa, autoCommitSends, autoCommitAcks)); } // https://issues.jboss.org/browse/HORNETQ-685 @Test(timeout = 120000) public void testTimeoutOnFailover() throws Exception { locator.setCallTimeout(1000).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setAckBatchSize(0).setReconnectAttempts(-1); ((InVMNodeManager) nodeManager).failoverPause = 500; ClientSessionFactoryInternal sf1 = (ClientSessionFactoryInternal) createSessionFactory(locator); final ClientSession session = createSession(sf1, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); final CountDownLatch latch = new CountDownLatch(10); final CountDownLatch latchFailed = new CountDownLatch(1); Runnable r = new Runnable() { @Override public void run() { for (int i = 0; i < 500; i++) { ClientMessage message = session.createMessage(true); message.putIntProperty("counter", i); try { System.out.println("Sent " + i); producer.send(message); if (i < 10) { latch.countDown(); if (latch.getCount() == 0) { latchFailed.await(10, TimeUnit.SECONDS); } } } catch (Exception e) { // this is our retry try { if (!producer.isClosed()) producer.send(message); } catch (ActiveMQException e1) { e1.printStackTrace(); } } } } }; Thread t = new Thread(r); t.start(); Assert.assertTrue("latch released", latch.await(10, TimeUnit.SECONDS)); crash(session); latchFailed.countDown(); t.join(30000); if (t.isAlive()) { t.interrupt(); Assert.fail("Thread still alive"); } Assert.assertTrue(backupServer.getServer().waitForActivation(5, TimeUnit.SECONDS)); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < 500; i++) { ClientMessage m = consumer.receive(1000); Assert.assertNotNull("message #=" + i, m); // assertEquals(i, m.getIntProperty("counter").intValue()); } } // https://issues.jboss.org/browse/HORNETQ-685 @Test(timeout = 120000) public void testTimeoutOnFailoverConsume() throws Exception { locator.setCallTimeout(5000).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setAckBatchSize(0).setBlockOnAcknowledge(true).setReconnectAttempts(-1).setAckBatchSize(0); ((InVMNodeManager) nodeManager).failoverPause = 5000L; ClientSessionFactoryInternal sf1 = (ClientSessionFactoryInternal) createSessionFactory(locator); final ClientSession session = createSession(sf1, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < 500; i++) { ClientMessage message = session.createMessage(true); message.putIntProperty("counter", i); producer.send(message); } final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch endLatch = new CountDownLatch(1); final ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); final Map<Integer, ClientMessage> received = new HashMap<>(); consumer.setMessageHandler(new MessageHandler() { @Override public void onMessage(ClientMessage message) { Integer counter = message.getIntProperty("counter"); received.put(counter, message); try { log.debug("acking message = id = " + message.getMessageID() + ", counter = " + message.getIntProperty("counter")); message.acknowledge(); } catch (ActiveMQException e) { e.printStackTrace(); return; } log.debug("Acked counter = " + counter); if (counter.equals(10)) { latch.countDown(); } if (received.size() == 500) { endLatch.countDown(); } } }); latch.await(10, TimeUnit.SECONDS); log.info("crashing session"); crash(session); endLatch.await(60, TimeUnit.SECONDS); Assert.assertTrue("received only " + received.size(), received.size() == 500); session.close(); } @Test(timeout = 120000) public void testTimeoutOnFailoverConsumeBlocked() throws Exception { locator.setCallTimeout(5000).setBlockOnNonDurableSend(true).setConsumerWindowSize(0).setBlockOnDurableSend(true).setAckBatchSize(0).setBlockOnAcknowledge(true).setReconnectAttempts(-1).setAckBatchSize(0); ((InVMNodeManager) nodeManager).failoverPause = 5000L; ClientSessionFactoryInternal sf1 = (ClientSessionFactoryInternal) createSessionFactory(locator); final ClientSession session = createSession(sf1, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < 500; i++) { ClientMessage message = session.createMessage(true); message.putIntProperty("counter", i); message.putBooleanProperty("end", i == 499); producer.send(message); } final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch endLatch = new CountDownLatch(1); final ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); final Map<Integer, ClientMessage> received = new HashMap<>(); Thread t = new Thread() { @Override public void run() { ClientMessage message = null; try { while ((message = getMessage()) != null) { Integer counter = message.getIntProperty("counter"); received.put(counter, message); try { log.info("acking message = id = " + message.getMessageID() + ", counter = " + message.getIntProperty("counter")); message.acknowledge(); } catch (ActiveMQException e) { e.printStackTrace(); continue; } log.info("Acked counter = " + counter); if (counter.equals(10)) { latch.countDown(); } if (received.size() == 500) { endLatch.countDown(); } if (message.getBooleanProperty("end")) { break; } } } catch (Exception e) { Assert.fail("failing due to exception " + e); } } private ClientMessage getMessage() { while (true) { try { ClientMessage msg = consumer.receive(20000); if (msg == null) { log.info("Returning null message on consuming"); } return msg; } catch (ActiveMQObjectClosedException oce) { throw new RuntimeException(oce); } catch (ActiveMQException ignored) { // retry ignored.printStackTrace(); } } } }; t.start(); latch.await(10, TimeUnit.SECONDS); log.info("crashing session"); crash(session); endLatch.await(60, TimeUnit.SECONDS); t.join(); Assert.assertTrue("received only " + received.size(), received.size() == 500); session.close(); } // https://issues.jboss.org/browse/HORNETQ-685 @Test(timeout = 120000) public void testTimeoutOnFailoverTransactionCommit() throws Exception { locator.setCallTimeout(5000).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setAckBatchSize(0).setReconnectAttempts(-1); ((InVMNodeManager) nodeManager).failoverPause = 5000L; ClientSessionFactoryInternal sf1 = (ClientSessionFactoryInternal) createSessionFactory(locator); final ClientSession session = createSession(sf1, true, false, false); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final CountDownLatch connectionFailed = new CountDownLatch(1); session.addFailureListener(new SessionFailureListener() { @Override public void beforeReconnect(ActiveMQException exception) { } @Override public void connectionFailed(ActiveMQException exception, boolean failedOver) { } @Override public void connectionFailed(ActiveMQException exception, boolean failedOver, String scaleDownTargetNodeID) { connectionFailed.countDown(); } }); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.start(xid, XAResource.TMNOFLAGS); for (int i = 0; i < 500; i++) { ClientMessage message = session.createMessage(true); message.putIntProperty("counter", i); producer.send(message); } session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); crash(false, session); try { session.commit(xid, false); } catch (XAException e) { //there is still an edge condition that we must deal with Assert.assertTrue(connectionFailed.await(10, TimeUnit.SECONDS)); session.commit(xid, false); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < 500; i++) { ClientMessage m = consumer.receive(1000); Assert.assertNotNull(m); Assert.assertEquals(i, m.getIntProperty("counter").intValue()); } } // https://issues.jboss.org/browse/HORNETQ-685 @Test(timeout = 120000) public void testTimeoutOnFailoverTransactionRollback() throws Exception { locator.setCallTimeout(2000).setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setAckBatchSize(0).setReconnectAttempts(-1); ((InVMNodeManager) nodeManager).failoverPause = 5000L; ClientSessionFactoryInternal sf1 = (ClientSessionFactoryInternal) createSessionFactory(locator); final ClientSession session = createSession(sf1, true, false, false); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.start(xid, XAResource.TMNOFLAGS); for (int i = 0; i < 500; i++) { ClientMessage message = session.createMessage(true); message.putIntProperty("counter", i); producer.send(message); } session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); crash(false, session); try { session.rollback(xid); } catch (XAException e) { try { //there is still an edge condition that we must deal with session.rollback(xid); } catch (Exception ignored) { log.trace(ignored.getMessage(), ignored); } } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage m = consumer.receive(1000); Assert.assertNull(m); } /** * see http://jira.jboss.org/browse/HORNETQ-522 * * @throws Exception */ @Test(timeout = 120000) public void testNonTransactedWithZeroConsumerWindowSize() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setAckBatchSize(0).setReconnectAttempts(-1); createClientSessionFactory(); ClientSession session = createSession(sf, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message = session.createMessage(true); setBody(i, message); message.putIntProperty("counter", i); producer.send(message); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); final CountDownLatch latch = new CountDownLatch(NUM_MESSAGES); consumer.setMessageHandler(new MessageHandler() { @Override public void onMessage(ClientMessage message) { latch.countDown(); } }); session.start(); crash(session); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); } protected void createClientSessionFactory() throws Exception { sf = (ClientSessionFactoryInternal) createSessionFactory(locator); } @Test(timeout = 120000) public void testNonTransacted() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); crash(session); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); receiveDurableMessages(consumer); session.close(); sf.close(); Assert.assertEquals(0, sf.numSessions()); Assert.assertEquals(0, sf.numConnections()); } /** * Basic fail-back test. * * @throws Exception */ @Test(timeout = 120000) public void testFailBack() throws Exception { boolean doFailBack = true; HAPolicy haPolicy = backupServer.getServer().getHAPolicy(); if (haPolicy instanceof ReplicaPolicy) { ((ReplicaPolicy) haPolicy).setMaxSavedReplicatedJournalsSize(1); } simpleFailover(haPolicy instanceof ReplicaPolicy, doFailBack); tearDown(); setUp(); } @Test(timeout = 120000) public void testFailBackLiveRestartsBackupIsGone() throws Exception { locator.setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, NUM_MESSAGES); producer.close(); session.commit(); SimpleString liveId = liveServer.getServer().getNodeID(); crash(session); session.start(); ClientConsumer consumer = addClientConsumer(session.createConsumer(FailoverTestBase.ADDRESS)); receiveMessages(consumer); assertNoMoreMessages(consumer); consumer.close(); session.commit(); Assert.assertEquals("backup must be running with the same nodeID", liveId, backupServer.getServer().getNodeID()); sf.close(); backupServer.crash(); Thread.sleep(100); Assert.assertFalse("backup is not running", backupServer.isStarted()); Assert.assertFalse("must NOT be a backup", liveServer.getServer().getHAPolicy() instanceof BackupPolicy); adaptLiveConfigForReplicatedFailBack(liveServer); beforeRestart(liveServer); liveServer.start(); Assert.assertTrue("live initialized...", liveServer.getServer().waitForActivation(15, TimeUnit.SECONDS)); sf = (ClientSessionFactoryInternal) createSessionFactory(locator); ClientSession session2 = createSession(sf, false, false); session2.start(); ClientConsumer consumer2 = session2.createConsumer(FailoverTestBase.ADDRESS); boolean replication = liveServer.getServer().getHAPolicy() instanceof ReplicatedPolicy; if (replication) receiveMessages(consumer2, 0, NUM_MESSAGES, true); assertNoMoreMessages(consumer2); session2.commit(); } @Test(timeout = 120000) public void testSimpleFailover() throws Exception { HAPolicy haPolicy = backupServer.getServer().getHAPolicy(); simpleFailover(haPolicy instanceof ReplicaPolicy, false); } @Test(timeout = 120000) public void testWithoutUsingTheBackup() throws Exception { locator.setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, NUM_MESSAGES); producer.close(); session.commit(); backupServer.stop(); // Backup stops! backupServer.start(); waitForRemoteBackupSynchronization(backupServer.getServer()); session.start(); ClientConsumer consumer = addClientConsumer(session.createConsumer(FailoverTestBase.ADDRESS)); receiveMessages(consumer); assertNoMoreMessages(consumer); consumer.close(); session.commit(); session.start(); producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, NUM_MESSAGES); producer.close(); session.commit(); backupServer.stop(); // Backup stops! beforeRestart(backupServer); backupServer.start(); waitForRemoteBackupSynchronization(backupServer.getServer()); backupServer.stop(); // Backup stops! liveServer.stop(); beforeRestart(liveServer); liveServer.start(); liveServer.getServer().waitForActivation(10, TimeUnit.SECONDS); ClientSession session2 = createSession(sf, false, false); session2.start(); ClientConsumer consumer2 = session2.createConsumer(FailoverTestBase.ADDRESS); receiveMessages(consumer2, 0, NUM_MESSAGES, true); assertNoMoreMessages(consumer2); session2.commit(); } /** * @param doFailBack * @throws Exception */ private void simpleFailover(boolean isReplicated, boolean doFailBack) throws Exception { locator.setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, NUM_MESSAGES); producer.close(); session.commit(); SimpleString liveId = liveServer.getServer().getNodeID(); crash(session); session.start(); ClientConsumer consumer = addClientConsumer(session.createConsumer(FailoverTestBase.ADDRESS)); receiveMessages(consumer); assertNoMoreMessages(consumer); consumer.close(); producer = addClientProducer(session.createProducer(FailoverTestBase.ADDRESS)); sendMessages(session, producer, NUM_MESSAGES); producer.close(); session.commit(); Assert.assertEquals("backup must be running with the same nodeID", liveId, backupServer.getServer().getNodeID()); if (doFailBack) { Assert.assertFalse("must NOT be a backup", liveServer.getServer().getHAPolicy().isBackup()); adaptLiveConfigForReplicatedFailBack(liveServer); beforeRestart(liveServer); liveServer.start(); Assert.assertTrue("live initialized...", liveServer.getServer().waitForActivation(40, TimeUnit.SECONDS)); int i = 0; while (!backupServer.isStarted() && i++ < 100) { Thread.sleep(100); } liveServer.getServer().waitForActivation(5, TimeUnit.SECONDS); Assert.assertTrue(backupServer.isStarted()); if (isReplicated) { FileMoveManager moveManager = new FileMoveManager(backupServer.getServer().getConfiguration().getJournalLocation(), 0); Assert.assertEquals(1, moveManager.getNumberOfFolders()); } } else { backupServer.stop(); beforeRestart(backupServer); backupServer.start(); Assert.assertTrue(backupServer.getServer().waitForActivation(10, TimeUnit.SECONDS)); } ClientSession session2 = createSession(sf, false, false); session2.start(); ClientConsumer consumer2 = session2.createConsumer(FailoverTestBase.ADDRESS); receiveMessages(consumer2, 0, NUM_MESSAGES, true); assertNoMoreMessages(consumer2); session2.commit(); } /** * @param consumer * @throws ActiveMQException */ private void assertNoMoreMessages(ClientConsumer consumer) throws ActiveMQException { ClientMessage msg = consumer.receiveImmediate(); Assert.assertNull("there should be no more messages to receive! " + msg, msg); } protected void createSessionFactory() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); } @Test(timeout = 120000) public void testConsumeTransacted() throws Exception { createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); final int numMessages = 10; sendMessages(session, producer, numMessages); session.commit(); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < numMessages; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull("Just crashed? " + (i == 6) + " " + i, message); message.acknowledge(); // TODO: The test won't pass if you uncomment this line // assertEquals(i, (int)message.getIntProperty("counter")); if (i == 5) { crash(session); } } try { session.commit(); Assert.fail("session must have rolled back on failover"); } catch (ActiveMQTransactionRolledBackException trbe) { //ok } catch (ActiveMQException e) { Assert.fail("Invalid Exception type:" + e.getType()); } consumer.close(); consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < numMessages; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull("Expecting message #" + i, message); message.acknowledge(); } session.commit(); session.close(); } /** * @return * @throws Exception */ protected ClientSession createSessionAndQueue() throws Exception { ClientSession session = createSession(sf, false, false); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); return session; } // https://jira.jboss.org/jira/browse/HORNETQ-285 @Test(timeout = 120000) public void testFailoverOnInitialConnection() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setFailoverOnInitialConnection(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); // Crash live server crash(); ClientSession session = createSession(sf); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessages(session, producer, NUM_MESSAGES); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); receiveMessages(consumer); session.close(); } @Test(timeout = 120000) public void testTransactedMessagesSentSoRollback() throws Exception { createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); crash(session); Assert.assertTrue(session.isRollbackOnly()); try { session.commit(); Assert.fail("Should throw exception"); } catch (ActiveMQTransactionRolledBackException trbe) { //ok } catch (ActiveMQException e) { Assert.fail("Invalid Exception type:" + e.getType()); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull("message should be null! Was: " + message, message); session.close(); } /** * Test that once the transacted session has throw a TRANSACTION_ROLLED_BACK exception, * it can be reused again */ @Test(timeout = 120000) public void testTransactedMessagesSentSoRollbackAndContinueWork() throws Exception { createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); crash(session); Assert.assertTrue(session.isRollbackOnly()); try { session.commit(); Assert.fail("Should throw exception"); } catch (ActiveMQTransactionRolledBackException trbe) { //ok } catch (ActiveMQException e) { Assert.fail("Invalid Exception type:" + e.getType()); } ClientMessage message = session.createMessage(false); int counter = RandomUtil.randomInt(); message.putIntProperty("counter", counter); producer.send(message); // session is working again session.commit(); session.start(); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); message = consumer.receive(1000); Assert.assertNotNull("expecting a message", message); Assert.assertEquals(counter, message.getIntProperty("counter").intValue()); session.close(); } @Test(timeout = 120000) public void testTransactedMessagesNotSentSoNoRollback() throws Exception { try { createSessionFactory(); ClientSession session = createSessionAndQueue(); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); session.commit(); crash(session); // committing again should work since didn't send anything since last commit Assert.assertFalse(session.isRollbackOnly()); session.commit(); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); receiveDurableMessages(consumer); Assert.assertNull(consumer.receiveImmediate()); session.commit(); session.close(); } finally { try { liveServer.getServer().stop(); } catch (Throwable ignored) { } try { backupServer.getServer().stop(); } catch (Throwable ignored) { } } } @Test(timeout = 120000) public void testTransactedMessagesWithConsumerStartedBeforeFailover() throws Exception { createSessionFactory(); ClientSession session = createSessionAndQueue(); // create a consumer and start the session before failover ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); // messages will be delivered to the consumer when the session is committed session.commit(); Assert.assertFalse(session.isRollbackOnly()); crash(session); session.commit(); session.close(); session = createSession(sf, false, false); consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); receiveDurableMessages(consumer); Assert.assertNull(consumer.receiveImmediate()); session.commit(); } @Test(timeout = 120000) public void testTransactedMessagesConsumedSoRollback() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session1, producer); session1.commit(); ClientSession session2 = createSession(sf, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); receiveMessages(consumer); crash(session2); Assert.assertTrue(session2.isRollbackOnly()); try { session2.commit(); Assert.fail("Should throw exception"); } catch (ActiveMQTransactionRolledBackException trbe) { //ok } catch (ActiveMQException e) { Assert.fail("Invalid Exception type:" + e.getType()); } } @Test(timeout = 120000) public void testTransactedMessagesNotConsumedSoNoRollback() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); sendMessages(session1, producer, NUM_MESSAGES); session1.commit(); ClientSession session2 = createSession(sf, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); receiveMessages(consumer, 0, NUM_MESSAGES / 2, true); session2.commit(); consumer.close(); crash(session2); Assert.assertFalse(session2.isRollbackOnly()); consumer = session2.createConsumer(FailoverTestBase.ADDRESS); for (int i = NUM_MESSAGES / 2; i < NUM_MESSAGES; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull("expecting message " + i, message); assertMessageBody(i, message); Assert.assertEquals(i, message.getIntProperty("counter").intValue()); message.acknowledge(); } session2.commit(); Assert.assertNull(consumer.receiveImmediate()); } @Test(timeout = 120000) public void testXAMessagesSentSoRollbackOnEnd() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, false, false); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); session.start(xid, XAResource.TMNOFLAGS); sendMessagesSomeDurable(session, producer); crash(session); try { session.end(xid, XAResource.TMSUCCESS); Assert.fail("Should throw exception"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_RMFAIL, e.errorCode); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull(message); } @Test(timeout = 120000) //start a tx but sending messages after crash public void testXAMessagesSentSoRollbackOnEnd2() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, false, false); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); session.start(xid, XAResource.TMNOFLAGS); crash(session); // sendMessagesSomeDurable(session, producer); producer.send(createMessage(session, 1, true)); try { session.end(xid, XAResource.TMSUCCESS); Assert.fail("Should throw exception"); } catch (XAException e) { // Assert.assertEquals(XAException.XAER_NOTA, e.errorCode); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull(message); } @Test(timeout = 120000) public void testXAMessagesSentSoRollbackOnPrepare() throws Exception { createSessionFactory(); final ClientSession session = createSession(sf, true, false, false); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); session.start(xid, XAResource.TMNOFLAGS); sendMessagesSomeDurable(session, producer); session.end(xid, XAResource.TMSUCCESS); crash(session); try { session.prepare(xid); Assert.fail("Should throw exception"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_RMFAIL, e.errorCode); // XXXX session.rollback(); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull(message); producer.close(); consumer.close(); } // This might happen if 1PC optimisation kicks in @Test(timeout = 120000) public void testXAMessagesSentSoRollbackOnCommit() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, false, false); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); session.start(xid, XAResource.TMNOFLAGS); sendMessagesSomeDurable(session, producer); session.end(xid, XAResource.TMSUCCESS); crash(session); try { session.commit(xid, false); Assert.fail("Should throw exception"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_NOTA, e.errorCode); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull(message); } @Test(timeout = 120000) public void testXAMessagesNotSentSoNoRollbackOnCommit() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, false, false); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); session.start(xid, XAResource.TMNOFLAGS); sendMessagesSomeDurable(session, producer); session.end(xid, XAResource.TMSUCCESS); session.prepare(xid); session.commit(xid, false); crash(session); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); Xid xid2 = new XidImpl("tfytftyf".getBytes(), 54654, "iohiuohiuhgiu".getBytes()); session.start(xid2, XAResource.TMNOFLAGS); receiveDurableMessages(consumer); session.end(xid2, XAResource.TMSUCCESS); session.prepare(xid2); session.commit(xid2, false); } @Test(timeout = 120000) public void testXAMessagesConsumedSoRollbackOnEnd() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session1, producer); session1.commit(); ClientSession session2 = createSession(sf, true, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session2.start(xid, XAResource.TMNOFLAGS); receiveMessages(consumer); crash(session2); try { session2.end(xid, XAResource.TMSUCCESS); Assert.fail("Should throw exception"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_RMFAIL, e.errorCode); } } @Test(timeout = 120000) public void testXAMessagesConsumedSoRollbackOnEnd2() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < NUM_MESSAGES; i++) { // some are durable, some are not! producer.send(createMessage(session1, i, true)); } session1.commit(); ClientSession session2 = createSession(sf, true, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session2.start(xid, XAResource.TMNOFLAGS); crash(session2); receiveMessages(consumer); try { session2.end(xid, XAResource.TMSUCCESS); Assert.fail("Should throw exception"); } catch (XAException e) { } // Since the end was not accepted, the messages should be redelivered receiveMessages(consumer); } @Test(timeout = 120000) public void testXAMessagesConsumedSoRollbackOnPrepare() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session1, producer); session1.commit(); ClientSession session2 = createSession(sf, true, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session2.start(xid, XAResource.TMNOFLAGS); receiveMessages(consumer); session2.end(xid, XAResource.TMSUCCESS); crash(session2); try { session2.prepare(xid); Assert.fail("Should throw exception"); } catch (XAException e) { Assert.assertEquals(XAException.XAER_RMFAIL, e.errorCode); } } // 1PC optimisation @Test(timeout = 120000) public void testXAMessagesConsumedSoRollbackOnCommit() throws Exception { createSessionFactory(); ClientSession session1 = createSessionAndQueue(); ClientProducer producer = session1.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session1, producer); session1.commit(); ClientSession session2 = createSession(sf, true, false, false); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); Xid xid = new XidImpl("uhuhuhu".getBytes(), 126512, "auhsduashd".getBytes()); session2.start(xid, XAResource.TMNOFLAGS); receiveMessages(consumer); session2.end(xid, XAResource.TMSUCCESS); // session2.prepare(xid); crash(session2); try { session2.commit(xid, false); Assert.fail("Should throw exception"); } catch (XAException e) { // it should be rolled back Assert.assertEquals(XAException.XAER_NOTA, e.errorCode); } session1.close(); session2.close(); } @Test(timeout = 120000) public void testCreateNewFactoryAfterFailover() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setFailoverOnInitialConnection(true); sf = createSessionFactoryAndWaitForTopology(locator, 2); ClientSession session = sendAndConsume(sf, true); crash(true, session); session.close(); long timeout; timeout = System.currentTimeMillis() + 5000; while (timeout > System.currentTimeMillis()) { try { createClientSessionFactory(); break; } catch (Exception e) { // retrying Thread.sleep(100); } } session = sendAndConsume(sf, true); } @Test(timeout = 120000) public void testFailoverMultipleSessionsWithConsumers() throws Exception { createSessionFactory(); final int numSessions = 5; final int numConsumersPerSession = 5; Map<ClientSession, List<ClientConsumer>> sessionConsumerMap = new HashMap<>(); for (int i = 0; i < numSessions; i++) { ClientSession session = createSession(sf, true, true); List<ClientConsumer> consumers = new ArrayList<>(); for (int j = 0; j < numConsumersPerSession; j++) { SimpleString queueName = new SimpleString("queue" + i + "-" + j); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, queueName, null, true); ClientConsumer consumer = session.createConsumer(queueName); consumers.add(consumer); } sessionConsumerMap.put(session, consumers); } ClientSession sendSession = createSession(sf, true, true); ClientProducer producer = sendSession.createProducer(FailoverTestBase.ADDRESS); sendMessages(sendSession, producer, NUM_MESSAGES); Set<ClientSession> sessionSet = sessionConsumerMap.keySet(); ClientSession[] sessions = new ClientSession[sessionSet.size()]; sessionSet.toArray(sessions); crash(sessions); for (ClientSession session : sessionConsumerMap.keySet()) { session.start(); } for (List<ClientConsumer> consumerList : sessionConsumerMap.values()) { for (ClientConsumer consumer : consumerList) { receiveMessages(consumer); } } } /* * Browser will get reset to beginning after failover */ @Test(timeout = 120000) public void testFailWithBrowser() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS, true); session.start(); receiveMessages(consumer, 0, NUM_MESSAGES, false); crash(session); receiveDurableMessages(consumer); } protected void sendMessagesSomeDurable(ClientSession session, ClientProducer producer) throws Exception { for (int i = 0; i < NUM_MESSAGES; i++) { // some are durable, some are not! producer.send(createMessage(session, i, isDurable(i))); } } @Test(timeout = 120000) public void testFailThenReceiveMoreMessagesAfterFailover() throws Exception { createSessionFactory(); ClientSession session = createSession(sf, true, true); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); // Receive MSGs but don't ack! for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message = consumer.receive(1000); Assert.assertNotNull(message); assertMessageBody(i, message); Assert.assertEquals(i, message.getIntProperty("counter").intValue()); } crash(session); // Should get the same ones after failover since we didn't ack receiveDurableMessages(consumer); } protected void receiveDurableMessages(ClientConsumer consumer) throws ActiveMQException { // During failover non-persistent messages may disappear but in certain cases they may survive. // For that reason the test is validating all the messages but being permissive with non-persistent messages // The test will just ack any non-persistent message, however when arriving it must be in order ClientMessage repeatMessage = null; for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message; if (repeatMessage != null) { message = repeatMessage; repeatMessage = null; } else { message = consumer.receive(1000); } if (message != null) { int msgInternalCounter = message.getIntProperty("counter").intValue(); if (msgInternalCounter == i + 1) { // The test can only jump to the next message if the current iteration is meant for non-durable Assert.assertFalse("a message on counter=" + i + " was expected", isDurable(i)); // message belongs to the next iteration.. let's just ignore it repeatMessage = message; continue; } } if (isDurable(i)) { Assert.assertNotNull(message); } if (message != null) { assertMessageBody(i, message); Assert.assertEquals(i, message.getIntProperty("counter").intValue()); message.acknowledge(); } } } private boolean isDurable(int i) { return i % 2 == 0; } @Test(timeout = 120000) public void testFailThenReceiveMoreMessagesAfterFailover2() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); ClientSession session = createSession(sf, true, true, 0); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessagesSomeDurable(session, producer); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); receiveMessages(consumer); crash(session); // Send some more for (int i = NUM_MESSAGES; i < NUM_MESSAGES * 2; i++) { producer.send(createMessage(session, i, isDurable(i))); } receiveMessages(consumer, NUM_MESSAGES, NUM_MESSAGES * 2, true); } protected void receiveMessages(ClientConsumer consumer) throws ActiveMQException { receiveMessages(consumer, 0, NUM_MESSAGES, true); } @Test(timeout = 120000) public void testSimpleSendAfterFailoverDurableTemporary() throws Exception { doSimpleSendAfterFailover(true, true); } @Test(timeout = 120000) public void testSimpleSendAfterFailoverNonDurableTemporary() throws Exception { doSimpleSendAfterFailover(false, true); } @Test(timeout = 120000) public void testSimpleSendAfterFailoverDurableNonTemporary() throws Exception { doSimpleSendAfterFailover(true, false); } @Test(timeout = 120000) public void testSimpleSendAfterFailoverNonDurableNonTemporary() throws Exception { doSimpleSendAfterFailover(false, false); } private void doSimpleSendAfterFailover(final boolean durable, final boolean temporary) throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); ClientSession session = createSession(sf, true, true, 0); if (temporary) { session.createTemporaryQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null); } else { session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, durable); } ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); crash(session); sendMessagesSomeDurable(session, producer); receiveMessages(consumer); } @Test(timeout = 120000) public void testForceBlockingReturn() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true).setReconnectAttempts(-1); createClientSessionFactory(); // Add an interceptor to delay the send method so we can get time to cause failover before it returns liveServer.getServer().getRemotingService().addIncomingInterceptor(new DelayInterceptor()); final ClientSession session = createSession(sf, true, true, 0); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); class Sender extends Thread { @Override public void run() { ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("message"); try { producer.send(message); } catch (ActiveMQException e1) { this.e = e1; } } volatile ActiveMQException e; } Sender sender = new Sender(); sender.start(); crash(session); sender.join(); Assert.assertNotNull(sender.e); Assert.assertNotNull(sender.e.getCause()); Assert.assertEquals(sender.e.getType(), ActiveMQExceptionType.UNBLOCKED); Assert.assertEquals(((ActiveMQException) sender.e.getCause()).getType(), ActiveMQExceptionType.DISCONNECTED); session.close(); } @Test(timeout = 120000) public void testCommitOccurredUnblockedAndResendNoDuplicates() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setReconnectAttempts(-1).setBlockOnAcknowledge(true); sf = createSessionFactoryAndWaitForTopology(locator, 2); final ClientSession session = createSession(sf, false, false); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); String txID = "my-tx-id"; for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message = session.createMessage(true); if (i == 0) { // Only need to add it on one message per tx message.putStringProperty(Message.HDR_DUPLICATE_DETECTION_ID, new SimpleString(txID)); } setBody(i, message); message.putIntProperty("counter", i); producer.send(message); } class Committer extends Thread { DelayInterceptor2 interceptor = new DelayInterceptor2(); @Override public void run() { try { sf.getServerLocator().addIncomingInterceptor(interceptor); session.commit(); } catch (ActiveMQTransactionRolledBackException trbe) { // Ok - now we retry the commit after removing the interceptor sf.getServerLocator().removeIncomingInterceptor(interceptor); try { session.commit(); failed = false; } catch (ActiveMQException e2) { throw new RuntimeException(e2); } } catch (ActiveMQTransactionOutcomeUnknownException toue) { // Ok - now we retry the commit after removing the interceptor sf.getServerLocator().removeIncomingInterceptor(interceptor); try { session.commit(); failed = false; } catch (ActiveMQException e2) { throw new RuntimeException(e2); } } catch (ActiveMQException e) { //ignore } } volatile boolean failed = true; } Committer committer = new Committer(); // Commit will occur, but response will never get back, connection is failed, and commit // should be unblocked with transaction rolled back committer.start(); // Wait for the commit to occur and the response to be discarded Assert.assertTrue(committer.interceptor.await()); crash(session); committer.join(); Assert.assertFalse("second attempt succeed?", committer.failed); session.close(); ClientSession session2 = createSession(sf, false, false); producer = session2.createProducer(FailoverTestBase.ADDRESS); // We now try and resend the messages since we get a transaction rolled back exception // but the commit actually succeeded, duplicate detection should kick in and prevent dups for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message = session2.createMessage(true); if (i == 0) { // Only need to add it on one message per tx message.putStringProperty(Message.HDR_DUPLICATE_DETECTION_ID, new SimpleString(txID)); } setBody(i, message); message.putIntProperty("counter", i); producer.send(message); } try { session2.commit(); Assert.fail("expecting DUPLICATE_ID_REJECTED exception"); } catch (ActiveMQDuplicateIdException dide) { //ok } catch (ActiveMQException e) { Assert.fail("Invalid Exception type:" + e.getType()); } ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); receiveMessages(consumer); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull(message); } @Test(timeout = 120000) public void testCommitDidNotOccurUnblockedAndResend() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setBlockOnAcknowledge(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); final ClientSession session = createSession(sf, false, false); session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, true); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); sendMessages(session, producer, NUM_MESSAGES); class Committer extends Thread { @Override public void run() { Interceptor interceptor = new DelayInterceptor3(); try { liveServer.addInterceptor(interceptor); session.commit(); } catch (ActiveMQTransactionRolledBackException trbe) { // Ok - now we retry the commit after removing the interceptor liveServer.removeInterceptor(interceptor); try { session.commit(); failed = false; } catch (ActiveMQException e2) { } } catch (ActiveMQTransactionOutcomeUnknownException toue) { // Ok - now we retry the commit after removing the interceptor liveServer.removeInterceptor(interceptor); try { session.commit(); failed = false; } catch (ActiveMQException e2) { } } catch (ActiveMQException e) { //ignore } } volatile boolean failed = true; } Committer committer = new Committer(); committer.start(); crash(session); committer.join(); Assert.assertFalse("commiter failed should be false", committer.failed); session.close(); ClientSession session2 = createSession(sf, false, false); producer = session2.createProducer(FailoverTestBase.ADDRESS); // We now try and resend the messages since we get a transaction rolled back exception sendMessages(session2, producer, NUM_MESSAGES); session2.commit(); ClientConsumer consumer = session2.createConsumer(FailoverTestBase.ADDRESS); session2.start(); receiveMessages(consumer); ClientMessage message = consumer.receiveImmediate(); Assert.assertNull("expecting null message", message); } @Test(timeout = 120000) public void testBackupServerNotRemoved() throws Exception { // HORNETQ-720 Disabling test for replicating backups. if (!(backupServer.getServer().getHAPolicy() instanceof SharedStoreSlavePolicy)) { return; } locator.setFailoverOnInitialConnection(true); createSessionFactory(); ClientSession session = sendAndConsume(sf, true); CountDownSessionFailureListener listener = new CountDownSessionFailureListener(session); session.addFailureListener(listener); backupServer.stop(); liveServer.crash(); // To reload security or other settings that are read during startup beforeRestart(backupServer); backupServer.start(); Assert.assertTrue("session failure listener", listener.getLatch().await(5, TimeUnit.SECONDS)); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); ClientMessage message = session.createMessage(true); setBody(0, message); producer.send(message); } @Test(timeout = 120000) public void testLiveAndBackupLiveComesBack() throws Exception { locator.setFailoverOnInitialConnection(true); createSessionFactory(); final CountDownLatch latch = new CountDownLatch(1); ClientSession session = sendAndConsume(sf, true); session.addFailureListener(new CountDownSessionFailureListener(latch, session)); backupServer.stop(); liveServer.crash(); beforeRestart(liveServer); // To reload security or other settings that are read during startup beforeRestart(liveServer); liveServer.start(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); ClientMessage message = session.createMessage(true); setBody(0, message); producer.send(message); } @Test(timeout = 120000) public void testLiveAndBackupLiveComesBackNewFactory() throws Exception { locator.setFailoverOnInitialConnection(true); createSessionFactory(); final CountDownLatch latch = new CountDownLatch(1); ClientSession session = sendAndConsume(sf, true); session.addFailureListener(new CountDownSessionFailureListener(latch, session)); backupServer.stop(); liveServer.crash(); // To reload security or other settings that are read during startup beforeRestart(liveServer); liveServer.start(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); ClientMessage message = session.createMessage(true); setBody(0, message); producer.send(message); session.close(); sf.close(); createClientSessionFactory(); session = createSession(sf); ClientConsumer cc = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage cm = cc.receive(5000); Assert.assertNotNull(cm); Assert.assertEquals("message0", cm.getBodyBuffer().readString()); } @Test(timeout = 120000) public void testLiveAndBackupBackupComesBackNewFactory() throws Exception { locator.setBlockOnNonDurableSend(true).setBlockOnDurableSend(true).setFailoverOnInitialConnection(true).setReconnectAttempts(-1); sf = createSessionFactoryAndWaitForTopology(locator, 2); ClientSession session = sendAndConsume(sf, true); CountDownSessionFailureListener listener = new CountDownSessionFailureListener(session); session.addFailureListener(listener); backupServer.stop(); liveServer.crash(); // To reload security or other settings that are read during startup beforeRestart(backupServer); if (!backupServer.getServer().getHAPolicy().isSharedStore()) { // XXX // this test would not make sense in the remote replication use case, without the following backupServer.getServer().setHAPolicy(new SharedStoreMasterPolicy()); } backupServer.start(); Assert.assertTrue("session failure listener", listener.getLatch().await(5, TimeUnit.SECONDS)); ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); ClientMessage message = session.createMessage(true); setBody(0, message); producer.send(message); session.close(); sf.close(); createClientSessionFactory(); session = createSession(sf); ClientConsumer cc = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); ClientMessage cm = cc.receive(5000); Assert.assertNotNull(cm); Assert.assertEquals("message0", cm.getBodyBuffer().readString()); } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- @Override protected TransportConfiguration getAcceptorTransportConfiguration(final boolean live) { return TransportConfigurationUtils.getInVMAcceptor(live); } @Override protected TransportConfiguration getConnectorTransportConfiguration(final boolean live) { return TransportConfigurationUtils.getInVMConnector(live); } protected void beforeRestart(TestableServer liveServer1) { // no-op } protected ClientSession sendAndConsume(final ClientSessionFactory sf1, final boolean createQueue) throws Exception { ClientSession session = createSession(sf1, false, true, true); if (createQueue) { session.createQueue(FailoverTestBase.ADDRESS, RoutingType.MULTICAST, FailoverTestBase.ADDRESS, null, false); } ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS); for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message = session.createMessage(ActiveMQTextMessage.TYPE, false, 0, System.currentTimeMillis(), (byte) 1); message.putIntProperty(new SimpleString("count"), i); message.getBodyBuffer().writeString("aardvarks"); producer.send(message); } ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS); session.start(); for (int i = 0; i < NUM_MESSAGES; i++) { ClientMessage message2 = consumer.receive(); Assert.assertEquals("aardvarks", message2.getBodyBuffer().readString()); Assert.assertEquals(i, message2.getObjectProperty(new SimpleString("count"))); message2.acknowledge(); } ClientMessage message3 = consumer.receiveImmediate(); Assert.assertNull(message3); return session; } // Inner classes ------------------------------------------------- }