/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.collection.impl.queue; import com.hazelcast.config.Config; import com.hazelcast.config.ListenerConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.HazelcastInstanceNotActiveException; import com.hazelcast.core.IQueue; import com.hazelcast.core.MemberAttributeEvent; import com.hazelcast.core.MembershipEvent; import com.hazelcast.core.MembershipListener; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.TestHazelcastInstanceFactory; import com.hazelcast.test.TestThread; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import com.hazelcast.util.EmptyStatement; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static com.hazelcast.spi.properties.GroupProperty.OPERATION_CALL_TIMEOUT_MILLIS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class QueueAdvancedTest extends HazelcastTestSupport { @Test public void testOffer() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; IQueue<String> q1 = h1.getQueue("default"); IQueue<String> q2 = h2.getQueue("default"); for (int i = 0; i < 100; i++) { assertTrue("Expected q1.offer() to succeed", q1.offer("item" + i, 100, SECONDS)); assertTrue("Expected q2.offer() to succeed", q2.offer("item" + i, 100, SECONDS)); } assertEquals("item0", q1.peek()); assertEquals("item0", q2.peek()); for (int i = 0; i < 100; i++) { assertEquals("item" + i, q1.poll()); assertEquals("item" + i, q2.poll()); } } /** * Test for issue 730 (Google). */ @Test public void testDeadTaker() throws Exception { Config config = new Config(); final CountDownLatch shutdownLatch = new CountDownLatch(1); config.addListenerConfig(new ListenerConfig().setImplementation(new MembershipListener() { @Override public void memberAdded(MembershipEvent membershipEvent) { } @Override public void memberRemoved(MembershipEvent membershipEvent) { shutdownLatch.countDown(); } @Override public void memberAttributeChanged(MemberAttributeEvent memberAttributeEvent) { } })); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(config); final HazelcastInstance h1 = instances[0]; final HazelcastInstance h2 = instances[1]; warmUpPartitions(h1, h2); final IQueue<String> q1 = h1.getQueue("default"); final IQueue<String> q2 = h2.getQueue("default"); final CountDownLatch startLatch = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { try { assertTrue("Expected startLatch.await() to succeed within 10 seconds", startLatch.await(10, SECONDS)); Thread.sleep(5000); h2.getLifecycleService().terminate(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { startLatch.countDown(); String value = q2.take(); fail("Should not be able to take value from queue, but got: " + value); } catch (HazelcastInstanceNotActiveException e) { EmptyStatement.ignore(e); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); assertTrue("Expected shutdownLatch.await() to succeed within 1 minute", shutdownLatch.await(1, MINUTES)); q1.offer("item"); assertEquals(1, q1.size()); assertEquals("item", q1.poll()); } @Test public void testShutdown() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; warmUpPartitions(h2, h1); IQueue<String> q1 = h1.getQueue("default"); IQueue<String> q2 = h2.getQueue("default"); for (int i = 0; i < 40; i++) { assertTrue("Expected q1.offer() to succeed", q1.offer("item" + i, 100, SECONDS)); } h1.getLifecycleService().shutdown(); for (int i = 40; i < 100; i++) { assertTrue("Expected q2.offer() to succeed", q2.offer("item" + i, 100, SECONDS)); } for (int i = 0; i < 100; i++) { assertEquals("item" + i, q2.poll()); } } @Test public void testPollNull() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; IQueue q1 = h1.getQueue("default"); IQueue q2 = h2.getQueue("default"); for (int i = 0; i < 100; i++) { assertNull(q1.poll()); assertNull(q2.poll()); } assertNull(q1.poll(2, SECONDS)); assertNull(q2.poll(2, SECONDS)); } @Test public void testTake() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; final IQueue<String> q1 = h1.getQueue("default"); final IQueue<String> q2 = h2.getQueue("default"); final CountDownLatch offerLatch = new CountDownLatch(2 * 100); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); for (int i = 0; i < 100; i++) { if (q1.offer("item")) { offerLatch.countDown(); } if (q2.offer("item")) { offerLatch.countDown(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); assertOpenEventually(offerLatch); ExecutorService es = Executors.newFixedThreadPool(50); final CountDownLatch latch = new CountDownLatch(200); for (int i = 0; i < 100; i++) { es.execute(new Runnable() { @Override public void run() { try { if ("item".equals(q1.take())) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); es.execute(new Runnable() { @Override public void run() { try { if ("item".equals(q2.take())) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); } assertOpenEventually(latch); es.shutdown(); } @Test public void testPollLong() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; final IQueue<String> q1 = h1.getQueue("default"); final IQueue<String> q2 = h2.getQueue("default"); final CountDownLatch offerLatch = new CountDownLatch(2 * 100); Thread.sleep(1000); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { if (q1.offer("item")) { offerLatch.countDown(); } if (q2.offer("item")) { offerLatch.countDown(); } } } }).start(); assertOpenEventually(offerLatch); ExecutorService es = Executors.newFixedThreadPool(50); final CountDownLatch latch = new CountDownLatch(200); Thread.sleep(3000); for (int i = 0; i < 100; i++) { es.execute(new Runnable() { @Override public void run() { try { if ("item".equals(q1.poll(5, SECONDS))) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); es.execute(new Runnable() { @Override public void run() { try { if ("item".equals(q2.poll(5, SECONDS))) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); } assertOpenEventually(latch); es.shutdown(); } @Test public void testOfferLong() throws Exception { Config config = new Config(); config.getQueueConfig("default").setMaxSize(200); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(config); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; final IQueue<String> q1 = h1.getQueue("default"); final IQueue<String> q2 = h2.getQueue("default"); for (int i = 0; i < 100; i++) { assertTrue("Expected q1.offer() to succeed", q1.offer("item" + i, 100, SECONDS)); assertTrue("Expected q2.offer() to succeed", q2.offer("item" + i, 100, SECONDS)); } assertFalse(q1.offer("item")); assertFalse(q2.offer("item")); assertFalse(q1.offer("item", 2, SECONDS)); assertFalse(q2.offer("item", 2, SECONDS)); final CountDownLatch pollLatch = new CountDownLatch(200); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); for (int i = 0; i < 100; i++) { if (("item" + i).equals(q1.poll(2, SECONDS))) { pollLatch.countDown(); } if (("item" + i).equals(q2.poll(2, SECONDS))) { pollLatch.countDown(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); assertOpenEventually(pollLatch); ExecutorService es = Executors.newFixedThreadPool(50); final CountDownLatch latch = new CountDownLatch(200); for (int i = 0; i < 100; i++) { es.execute(new Runnable() { @Override public void run() { try { if (q1.offer("item", 30, SECONDS)) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); es.execute(new Runnable() { @Override public void run() { try { if (q2.offer("item", 30, SECONDS)) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }); } assertOpenEventually(latch); es.shutdown(); } /** * Test case for issue 289. * <p/> * 1. Create HazelcastInstance h1 and h2, then get a queue from each (same queue name) * 2. Put a message on queue from h2 * 3. Take a message off on queue from h1 * 4. Shutdown h1, then check if queue is still empty on h2 */ @Test public void testQueueAfterShutdown() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; IQueue<String> q1 = h1.getQueue("default"); IQueue<String> q2 = h2.getQueue("default"); q2.offer("item"); assertEquals(1, q1.size()); assertEquals(1, q2.size()); assertEquals("item", q1.take()); assertEquals(0, q1.size()); assertEquals(0, q2.size()); h1.getLifecycleService().shutdown(); assertEquals(0, q2.size()); } @Test public void testQueueAfterShutdown_switchedInstanceOrder() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; IQueue<String> q1 = h1.getQueue("default"); IQueue<String> q2 = h2.getQueue("default"); q1.offer("item"); assertEquals(1, q1.size()); assertEquals(1, q2.size()); assertEquals("item", q2.take()); assertEquals(0, q1.size()); assertEquals(0, q2.size()); h2.getLifecycleService().shutdown(); assertEquals(0, q1.size()); } @Test public void queueEntriesShouldBeConsistentAfterShutdown() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; Queue<String> q1 = h1.getQueue("q"); Queue<String> q2 = h2.getQueue("q"); for (int i = 0; i < 5; i++) { q1.offer("item" + i); } assertEquals(5, q1.size()); assertEquals(5, q2.size()); assertEquals("item0", q2.poll()); assertEquals("item1", q2.poll()); assertEquals("item2", q2.poll()); assertSizeEventually(2, q1); assertSizeEventually(2, q2); h1.getLifecycleService().shutdown(); assertSizeEventually(2, q2); } @Test public void queueEntriesShouldBeConsistentAfterShutdown_switchedInstanceOrder() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); HazelcastInstance h1 = instances[0]; HazelcastInstance h2 = instances[1]; Queue<String> q1 = h1.getQueue("q"); Queue<String> q2 = h2.getQueue("q"); for (int i = 0; i < 5; i++) { q2.offer("item" + i); } assertEquals(5, q1.size()); assertEquals(5, q2.size()); assertEquals("item0", q1.poll()); assertEquals("item1", q1.poll()); assertEquals("item2", q1.poll()); assertSizeEventually(2, q1); assertSizeEventually(2, q2); h2.getLifecycleService().shutdown(); assertSizeEventually(2, q1); } @Test public void testAddAllBackup() { HazelcastInstance[] instances = createHazelcastInstances(); HazelcastInstance instance1 = instances[0]; HazelcastInstance instance2 = instances[1]; String name = generateKeyOwnedBy(instance1); IQueue<String> queue1 = instance1.getQueue(name); IQueue<String> queue2 = instance2.getQueue(name); List<String> list = new ArrayList<String>(); for (int i = 0; i < 4; i++) { list.add("item" + i); } assertTrue("Expected queue1.addAll() to succeed", queue1.addAll(list)); instance1.shutdown(); assertSizeEventually(4, queue2); assertIterableEquals(queue2, "item0", "item1", "item2", "item3"); } @Test public void testClearBackup() { HazelcastInstance[] instances = createHazelcastInstances(); HazelcastInstance instance1 = instances[0]; HazelcastInstance instance2 = instances[1]; String name = generateKeyOwnedBy(instance1); IQueue<String> queue1 = instance1.getQueue(name); IQueue<String> queue2 = instance2.getQueue(name); for (int i = 0; i < 4; i++) { queue1.offer("item" + i); } assertSizeEventually(4, queue2); assertIterableEquals(queue2, "item0", "item1", "item2", "item3"); queue1.clear(); instance1.shutdown(); assertSizeEventually(0, queue2); } @Test public void testRemoveBackup() { HazelcastInstance[] instances = createHazelcastInstances(); HazelcastInstance instance1 = instances[0]; HazelcastInstance instance2 = instances[1]; String name = generateKeyOwnedBy(instance1); IQueue<String> queue1 = instance1.getQueue(name); IQueue<String> queue2 = instance2.getQueue(name); for (int i = 0; i < 4; i++) { queue1.offer("item" + i); } assertSizeEventually(4, queue2); assertIterableEquals(queue2, "item0", "item1", "item2", "item3"); queue1.remove("item0"); queue1.remove("item1"); instance1.shutdown(); assertSizeEventually(2, queue2); assertIterableEquals(queue2, "item2", "item3"); } @Test public void testCompareAndRemoveBackup() { HazelcastInstance[] instances = createHazelcastInstances(); HazelcastInstance instance1 = instances[0]; HazelcastInstance instance2 = instances[1]; String name = generateKeyOwnedBy(instance1); IQueue<String> queue1 = instance1.getQueue(name); IQueue<String> queue2 = instance2.getQueue(name); for (int i = 0; i < 4; i++) { queue1.offer("item" + i); } assertSizeEventually(4, queue2); assertIterableEquals(queue2, "item0", "item1", "item2", "item3"); List<String> list = new ArrayList<String>(); list.add("item0"); list.add("item1"); list.add("item2"); assertTrue("Expected queue1.removeAll() to succeed", queue1.removeAll(list)); instance1.shutdown(); assertSizeEventually(1, queue2); assertIterableEquals(queue2, "item3"); } @Test public void testDrainBackup() { HazelcastInstance[] instances = createHazelcastInstances(); HazelcastInstance instance1 = instances[0]; HazelcastInstance instance2 = instances[1]; String name = generateKeyOwnedBy(instance1); IQueue<String> queue1 = instance1.getQueue(name); IQueue<String> queue2 = instance2.getQueue(name); for (int i = 0; i < 4; i++) { queue1.offer("item" + i); } assertSizeEventually(4, queue2); assertIterableEquals(queue2, "item0", "item1", "item2", "item3"); List<String> list = new ArrayList<String>(); queue1.drainTo(list, 2); instance1.shutdown(); assertSizeEventually(2, queue2); assertIterableEquals(queue2, "item2", "item3"); } @Test public void testTakeInterruption() { Config config = new Config() .setProperty(OPERATION_CALL_TIMEOUT_MILLIS.getName(), "10000"); HazelcastInstance instance = createHazelcastInstance(config); final IQueue<Thread> queue = instance.getQueue(randomName()); final CountDownLatch takeLatch = new CountDownLatch(1); TestThread thread = new TestThread() { @Override public void doRun() throws Throwable { takeLatch.countDown(); queue.take(); } }; thread.start(); assertOpenEventually(takeLatch); thread.interrupt(); thread.assertFailsEventually(InterruptedException.class); } @Test public void testPutInterruption() { Config config = new Config() .setProperty(OPERATION_CALL_TIMEOUT_MILLIS.getName(), "10000"); config.getQueueConfig("default").setMaxSize(1); HazelcastInstance instance = createHazelcastInstance(config); final IQueue<String> queue = instance.getQueue(randomName()); assertTrue("Expected queue.offer() to succeed", queue.offer("item")); final CountDownLatch putLatch = new CountDownLatch(1); TestThread thread = new TestThread() { @Override public void doRun() throws Throwable { putLatch.countDown(); queue.put("item"); } }; thread.start(); assertOpenEventually(putLatch); thread.interrupt(); thread.assertFailsEventually(InterruptedException.class); } private HazelcastInstance[] createHazelcastInstances() { String configName = randomString(); Config config = new Config(); config.getQueueConfig(configName).setMaxSize(100); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); return factory.newInstances(config); } }