/* * 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.internal.partition.impl; import com.hazelcast.internal.partition.InternalPartition; import com.hazelcast.internal.partition.MigrationInfo; import com.hazelcast.internal.partition.impl.MigrationPlanner.MigrationDecisionCallback; import com.hazelcast.nio.Address; import com.hazelcast.test.HazelcastSerialClassRunner; 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.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @RunWith(HazelcastSerialClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class MigrationPlannerTest { private MigrationDecisionCallback callback = mock(MigrationDecisionCallback.class); private MigrationPlanner migrationPlanner = new MigrationPlanner(); @Test public void test_MOVE() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5702), new Address( "localhost", 5705), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5704), -1, 0); verify(callback).migrate(new Address("localhost", 5703), 2, -1, new Address("localhost", 5705), -1, 2); } @Test public void test_COPY() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), null, new Address("localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5704), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(null, -1, -1, new Address("localhost", 5704), -1, 1); } @Test public void test_SHIFT_DOWN_withNullKeepReplicaIndex() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), null, new Address("localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5701), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, 1, new Address("localhost", 5704), -1, 0); } @Test public void test_SHIFT_DOWN_withNullNonNullKeepReplicaIndex() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5701), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5704), -1, 0); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5701), -1, 1); } @Test public void test_SHIFT_DOWN_performedBy_MOVE() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5701), new Address( "localhost", 5702), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5704), -1, 0); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5701), -1, 1); verify(callback).migrate(new Address("localhost", 5703), 2, -1, new Address("localhost", 5702), -1, 2); } @Test public void test_SHIFT_UP() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), null, new Address("localhost", 5703), new Address("localhost", 5704), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5703), new Address( "localhost", 5704), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(null, -1, -1, new Address("localhost", 5703), 2, 1); verify(callback).migrate(null, -1, -1, new Address("localhost", 5704), 3, 2); } @Test public void test_SHIFT_UPS_performedBy_MOVE() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5704), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5703), new Address( "localhost", 5704), new Address("localhost", 5705), null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5704), 3, -1, new Address("localhost", 5705), -1, 3); verify(callback).migrate(new Address("localhost", 5703), 2, -1, new Address("localhost", 5704), -1, 2); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5703), -1, 1); } @Test public void test_SHIFT_DOWN_performedAfterKnownNewReplicaOwnerKickedOutOfReplicas() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5705), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5703), new Address( "localhost", 5705), new Address("localhost", 5706), new Address("localhost", 5702), new Address("localhost", 5701), null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, 5, new Address("localhost", 5704), -1, 0); verify(callback).migrate(new Address("localhost", 5705), 3, -1, new Address("localhost", 5706), -1, 3); verify(callback).migrate(new Address("localhost", 5703), 2, -1, new Address("localhost", 5705), -1, 2); verify(callback).migrate(new Address("localhost", 5702), 1, 4, new Address("localhost", 5703), -1, 1); } @Test public void test_SHIFT_DOWN_performedBeforeNonConflicting_SHIFT_UP() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5705), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5704), new Address("localhost", 5703), new Address( "localhost", 5705), new Address("localhost", 5706), new Address("localhost", 5701), null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, 4, new Address("localhost", 5704), -1, 0); verify(callback).migrate(new Address("localhost", 5705), 3, -1, new Address("localhost", 5706), -1, 3); verify(callback).migrate(new Address("localhost", 5703), 2, -1, new Address("localhost", 5705), -1, 2); } @Test public void test_MOVE_toNull() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5705), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5705), 3, -1, null, -1, -1); } @Test public void test_SHIFT_UP_toReplicaIndexWithExistingOwner() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5704), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5704), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5704), 3, 1); } @Test public void test_MOVE_performedAfter_SHIFT_UP_toReplicaIndexWithExistingOwnerKicksItOutOfCluster() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address( "localhost", 5703), new Address("localhost", 5704), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5702), new Address("localhost", 5704), new Address( "localhost", 5703), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5704), 3, 1); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5702), -1, 0); } @Test public void test_SHIFT_UP_multipleTimes() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5702), null, new Address("localhost", 5703), new Address("localhost", 5704), null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5702), new Address("localhost", 5703), new Address( "localhost", 5704), null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(null, -1, -1, new Address("localhost", 5703), 2, 1); verify(callback).migrate(null, -1, -1, new Address("localhost", 5704), 3, 2); } @Test public void test_SHIFT_UP_nonNullSource_isNoLongerReplica() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), null, null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5702), null, null, null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5702), 1, 0); } @Test public void test_SHIFT_UP_nonNullSource_willGetAnotherMOVE() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), new Address("localhost", 5702), new Address("localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5703), new Address("localhost", 5701), null, null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, -1, new Address("localhost", 5703), 2, 0); verify(callback).migrate(new Address("localhost", 5702), 1, -1, new Address("localhost", 5701), -1, 1); } @Test public void test_SHIFT_UP_SHIFT_DOWN_atomicTogether() throws UnknownHostException { final Address[] oldAddresses = new Address[]{new Address("localhost", 5701), null, new Address("localhost", 5703), null, null, null, null}; final Address[] newAddresses = new Address[]{new Address("localhost", 5703), new Address("localhost", 5701), null, null, null, null, null}; migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); verify(callback).migrate(new Address("localhost", 5701), 0, 1, new Address("localhost", 5703), 2, 0); } @Test public void testSingleMigrationPrioritization() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, null, null, new Address("localhost", 5701), "5701", -1, -1, -1, 0); migrations.add(migration1); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(singletonList(migration1), migrations); } @Test public void testNoCopyPrioritizationAgainstCopy() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, null, null, new Address("localhost", 5701), "5701", -1, -1, -1, 0); final MigrationInfo migration2 = new MigrationInfo(0, null, null, new Address("localhost", 5702), "5702", -1, -1, -1, 1); final MigrationInfo migration3 = new MigrationInfo(0, null, null, new Address("localhost", 5703), "5702", -1, -1, -1, 2); final MigrationInfo migration4 = new MigrationInfo(0, null, null, new Address("localhost", 5704), "5702", -1, -1, -1, 3); migrations.add(migration1); migrations.add(migration2); migrations.add(migration3); migrations.add(migration4); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(asList(migration1, migration2, migration3, migration4), migrations); } @Test public void testCopyPrioritizationAgainstMove() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, null, null, new Address("localhost", 5701), "5701", -1, -1, -1, 0); final MigrationInfo migration2 = new MigrationInfo(0, null, null, new Address("localhost", 5702), "5702", -1, -1, -1, 1); final MigrationInfo migration3 = new MigrationInfo(0, new Address("localhost", 5703), "5703", new Address("localhost", 5704), "5704", 2, -1, -1, 2); final MigrationInfo migration4 = new MigrationInfo(0, new Address("localhost", 5705), "5705", new Address("localhost", 5706), "5706", 2, -1, -1, 3); final MigrationInfo migration5 = new MigrationInfo(0, null, null, new Address("localhost", 5707), "5707", -1, -1, -1, 4); migrations.add(migration1); migrations.add(migration2); migrations.add(migration3); migrations.add(migration4); migrations.add(migration5); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(asList(migration1, migration2, migration5, migration3, migration4), migrations); } @Test public void testShiftUpPrioritizationAgainstMove() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, null, null, new Address("localhost", 5701), "5701", -1, -1, -1, 0); final MigrationInfo migration2 = new MigrationInfo(0, null, null, new Address("localhost", 5702), "5702", -1, -1, -1, 1); final MigrationInfo migration3 = new MigrationInfo(0, new Address("localhost", 5705), "5705", new Address("localhost", 5706), "5706", 2, -1, -1, 3); final MigrationInfo migration4 = new MigrationInfo(0, null, null, new Address("localhost", 5707), "5707", -1, -1, 4, 2); migrations.add(migration1); migrations.add(migration2); migrations.add(migration3); migrations.add(migration4); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(asList(migration1, migration2, migration4, migration3), migrations); } @Test public void testCopyPrioritizationAgainstShiftDownToColderIndex() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, new Address("localhost", 5701), "5701", new Address("localhost", 5702), "5702", 0, 2, -1, 0); final MigrationInfo migration2 = new MigrationInfo(0, null, null, new Address("localhost", 5703), "5703", -1, -1, -1, 1); migrations.add(migration1); migrations.add(migration2); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(asList(migration2, migration1), migrations); } @Test public void testNoCopyPrioritizationAgainstShiftDownToHotterIndex() throws UnknownHostException { List<MigrationInfo> migrations = new ArrayList<MigrationInfo>(); final MigrationInfo migration1 = new MigrationInfo(0, new Address("localhost", 5701), "5701", new Address("localhost", 5702), "5702", 0, 1, -1, 0); final MigrationInfo migration2 = new MigrationInfo(0, null, null, new Address("localhost", 5703), "5703", -1, -1, -1, 2); migrations.add(migration1); migrations.add(migration2); migrationPlanner.prioritizeCopiesAndShiftUps(migrations); assertEquals(asList(migration1, migration2), migrations); } @Test public void testRandom() throws UnknownHostException { for (int i = 0; i < 100; i++) { testRandom(3); testRandom(4); testRandom(5); } } private void testRandom(int initialLen) throws java.net.UnknownHostException { Address[] oldAddresses = new Address[InternalPartition.MAX_REPLICA_COUNT]; for (int i = 0; i < initialLen; i++) { oldAddresses[i] = newAddress(5000 + i); } Address[] newAddresses = Arrays.copyOf(oldAddresses, oldAddresses.length); int newLen = (int) (Math.random() * (oldAddresses.length - initialLen + 1)); for (int i = 0; i < newLen; i++) { newAddresses[i + initialLen] = newAddress(6000 + i); } shuffle(newAddresses, initialLen + newLen); migrationPlanner.planMigrations(oldAddresses, newAddresses, callback); } private void shuffle(Address[] array, int len) { int index; Address temp; Random random = new Random(); for (int i = len - 1; i > 0; i--) { index = random.nextInt(i + 1); temp = array[index]; array[index] = array[i]; array[i] = temp; } } private Address newAddress(int port) throws java.net.UnknownHostException { return new Address("localhost", port); } }