/*
* 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 java.io.File;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
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.core.client.impl.ServerLocatorInternal;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Assert;
import org.junit.Test;
public class BackupSyncLargeMessageTest extends BackupSyncJournalTest {
@Override
protected void assertMessageBody(final int i, final ClientMessage message) {
assertLargeMessageBody(i, message);
}
@Override
protected ServerLocatorInternal getServerLocator() throws Exception {
return (ServerLocatorInternal) super.getServerLocator().setMinLargeMessageSize(MIN_LARGE_MESSAGE);
}
@Override
protected void setBody(final int i, final ClientMessage message) {
setLargeMessageBody(i, message);
}
// ------------------------
@Test
public void testDeleteLargeMessages() throws Exception {
// 200 will increase the odds of a failure
setNumberOfMessages(200);
File dir = new File(backupServer.getServer().getConfiguration().getLargeMessagesDirectory());
assertEquals("Should not have any large messages... previous test failed to clean up?", 0, getAllMessageFileIds(dir).size());
createProducerSendSomeMessages();
startBackupFinishSyncing();
receiveMsgsInRange(0, getNumberOfMessages() / 2);
finishSyncAndFailover();
final int target = getNumberOfMessages() / 2;
long timeout = System.currentTimeMillis() + 5000;
while (getAllMessageFileIds(dir).size() != target && System.currentTimeMillis() < timeout) {
Thread.sleep(50);
}
assertEquals("we really ought to delete these after delivery", target, getAllMessageFileIds(dir).size());
}
/**
* @throws Exception
*/
@Test
public void testDeleteLargeMessagesDuringSync() throws Exception {
setNumberOfMessages(200);
File backupLMdir = new File(backupServer.getServer().getConfiguration().getLargeMessagesDirectory());
File liveLMDir = new File(liveServer.getServer().getConfiguration().getLargeMessagesDirectory());
assertEquals("Should not have any large messages... previous test failed to clean up?", 0, getAllMessageFileIds(backupLMdir).size());
createProducerSendSomeMessages();
backupServer.start();
waitForComponent(backupServer.getServer(), 5);
receiveMsgsInRange(0, getNumberOfMessages() / 2);
startBackupFinishSyncing();
Thread.sleep(500);
liveServer.getServer().stop();
backupServer.getServer().waitForActivation(10, TimeUnit.SECONDS);
backupServer.stop();
Set<Long> backupLM = getAllMessageFileIds(backupLMdir);
Set<Long> liveLM = getAllMessageFileIds(liveLMDir);
assertEquals("live and backup should have the same files ", liveLM, backupLM);
assertEquals("we really ought to delete these after delivery: " + backupLM, getNumberOfMessages() / 2, backupLM.size());
assertEquals("we really ought to delete these after delivery", getNumberOfMessages() / 2, getAllMessageFileIds(backupLMdir).size());
}
/**
* LargeMessages are passed from the client to the server in chunks. Here we test the backup
* starting the data synchronization with the live in the middle of a multiple chunks large
* message upload from the client to the live server.
*
* @throws Exception
*/
@Test
public void testBackupStartsWhenLiveIsReceivingLargeMessage() throws Exception {
final ClientSession session = addClientSession(sessionFactory.createSession(true, true));
session.createQueue(FailoverTestBase.ADDRESS, FailoverTestBase.ADDRESS, null, true);
final ClientProducer producer = session.createProducer(FailoverTestBase.ADDRESS);
final ClientMessage message = session.createMessage(true);
final int largeMessageSize = 1000 * MIN_LARGE_MESSAGE;
message.setBodyInputStream(ActiveMQTestBase.createFakeLargeStream(largeMessageSize));
final AtomicBoolean caughtException = new AtomicBoolean(false);
final CountDownLatch latch = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1);
Runnable r = new Runnable() {
@Override
public void run() {
try {
latch.countDown();
producer.send(message);
sendMessages(session, producer, 20);
session.commit();
} catch (ActiveMQException e) {
e.printStackTrace();
caughtException.set(true);
} finally {
latch2.countDown();
}
}
};
Executors.defaultThreadFactory().newThread(r).start();
waitForLatch(latch);
startBackupFinishSyncing();
ActiveMQTestBase.waitForLatch(latch2);
crash(session);
assertFalse("no exceptions while sending message", caughtException.get());
session.start();
ClientConsumer consumer = session.createConsumer(FailoverTestBase.ADDRESS);
ClientMessage msg = consumer.receive(2000);
ActiveMQBuffer buffer = msg.getBodyBuffer();
for (int j = 0; j < largeMessageSize; j++) {
Assert.assertTrue("large msg , expecting " + largeMessageSize + " bytes, got " + j, buffer.readable());
Assert.assertEquals("equal at " + j, ActiveMQTestBase.getSamplebyte(j), buffer.readByte());
}
receiveMessages(consumer, 0, 20, true);
assertNull("there should be no more messages!", consumer.receiveImmediate());
consumer.close();
session.commit();
}
private Set<Long> getAllMessageFileIds(File dir) {
Set<Long> idsOnBkp = new TreeSet<>();
String[] fileList = dir.list();
if (fileList != null) {
for (String filename : fileList) {
if (filename.endsWith(".msg")) {
idsOnBkp.add(Long.valueOf(filename.split("\\.")[0]));
}
}
}
return idsOnBkp;
}
}