/* * 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.partition; import com.hazelcast.config.Config; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.MigrationEvent; import com.hazelcast.core.MigrationListener; import com.hazelcast.core.PartitionService; import com.hazelcast.spi.properties.GroupProperty; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.TestHazelcastInstanceFactory; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class PartitionMigrationListenerTest extends HazelcastTestSupport { @Test public void testMigrationListenerCalledOnlyOnceWhenMigrationHappens() throws Exception { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); Config config = new Config(); // even partition count to make migration count deterministic final int partitionCount = 10; config.setProperty(GroupProperty.PARTITION_COUNT.getName(), String.valueOf(partitionCount)); HazelcastInstance instance = factory.newHazelcastInstance(config); CountDownLatch migrationStartLatch = new CountDownLatch(1); final CountingMigrationListener migrationListener = new CountingMigrationListener(migrationStartLatch, partitionCount); instance.getPartitionService().addMigrationListener(migrationListener); warmUpPartitions(instance); final HazelcastInstance instance2 = factory.newHazelcastInstance(config); assertNodeStartedEventually(instance2); migrationStartLatch.countDown(); waitAllForSafeState(instance2, instance); assertTrueEventually(new AssertTask() { @Override public void run() throws Exception { int startedTotal = getTotal(migrationListener.migrationStarted); int completedTotal = getTotal(migrationListener.migrationCompleted); assertEquals(partitionCount / 2, startedTotal); assertEquals(startedTotal, completedTotal); } }); assertAllLessThanOrEqual(migrationListener.migrationStarted, 1); assertAllLessThanOrEqual(migrationListener.migrationCompleted, 1); } private int getTotal(AtomicInteger[] integers) { int total = 0; for (AtomicInteger count : integers) { total += count.get(); } return total; } @Test(expected = NullPointerException.class) public void testAddMigrationListener_whenNullListener() { HazelcastInstance hz = createHazelcastInstance(); PartitionService partitionService = hz.getPartitionService(); partitionService.addMigrationListener(null); } @Test public void testAddMigrationListener_whenListenerRegisteredTwice() { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance hz1 = factory.newHazelcastInstance(); PartitionService partitionService = hz1.getPartitionService(); MigrationListener listener = mock(MigrationListener.class); String id1 = partitionService.addMigrationListener(listener); String id2 = partitionService.addMigrationListener(listener); // first we check if the registration id's are different assertNotEquals(id1, id2); } @Test(expected = NullPointerException.class) public void testRemoveMigrationListener_whenNullListener() { HazelcastInstance hz = createHazelcastInstance(); PartitionService partitionService = hz.getPartitionService(); partitionService.removeMigrationListener(null); } @Test public void testRemoveMigrationListener_whenNonExistingRegistrationId() { HazelcastInstance hz = createHazelcastInstance(); PartitionService partitionService = hz.getPartitionService(); boolean result = partitionService.removeMigrationListener("notexist"); assertFalse(result); } @Test public void testRemoveMigrationListener() { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance hz1 = factory.newHazelcastInstance(); PartitionService partitionService = hz1.getPartitionService(); MigrationListener listener = mock(MigrationListener.class); String id = partitionService.addMigrationListener(listener); boolean removed = partitionService.removeMigrationListener(id); assertTrue(removed); // now we add a member HazelcastInstance hz2 = factory.newHazelcastInstance(); warmUpPartitions(hz1, hz2); // and verify that the listener isn't called. verify(listener, never()).migrationStarted(any(MigrationEvent.class)); } private void assertAllLessThanOrEqual(AtomicInteger[] integers, int expected) { for (AtomicInteger integer : integers) { assertTrue(integer.get() <= expected); } } private static class CountingMigrationListener implements MigrationListener { CountDownLatch migrationStartLatch; AtomicInteger[] migrationStarted; AtomicInteger[] migrationCompleted; AtomicInteger[] migrationFailed; CountingMigrationListener(CountDownLatch migrationStartLatch, int partitionCount) { this.migrationStartLatch = migrationStartLatch; migrationStarted = new AtomicInteger[partitionCount]; migrationCompleted = new AtomicInteger[partitionCount]; migrationFailed = new AtomicInteger[partitionCount]; for (int i = 0; i < partitionCount; i++) { migrationStarted[i] = new AtomicInteger(); migrationCompleted[i] = new AtomicInteger(); migrationFailed[i] = new AtomicInteger(); } } @Override public void migrationStarted(MigrationEvent migrationEvent) { assertOpenEventually(migrationStartLatch); migrationStarted[migrationEvent.getPartitionId()].incrementAndGet(); } @Override public void migrationCompleted(MigrationEvent migrationEvent) { migrationCompleted[migrationEvent.getPartitionId()].incrementAndGet(); } @Override public void migrationFailed(MigrationEvent migrationEvent) { migrationFailed[migrationEvent.getPartitionId()].incrementAndGet(); } } }