/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.master.block; import alluxio.Constants; import alluxio.clock.ManualClock; import alluxio.heartbeat.HeartbeatContext; import alluxio.heartbeat.HeartbeatScheduler; import alluxio.heartbeat.ManuallyScheduleHeartbeat; import alluxio.master.MasterRegistry; import alluxio.master.journal.Journal; import alluxio.master.journal.JournalFactory; import alluxio.thrift.Command; import alluxio.thrift.CommandType; import alluxio.util.ThreadFactoryUtils; import alluxio.util.executor.ExecutorServiceFactories; import alluxio.wire.BlockInfo; import alluxio.wire.BlockLocation; import alluxio.wire.WorkerInfo; import alluxio.wire.WorkerNetAddress; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Unit tests for {@link BlockMaster}. */ public class BlockMasterTest { private static final WorkerNetAddress NET_ADDRESS_1 = new WorkerNetAddress().setHost("localhost") .setRpcPort(80).setDataPort(81).setWebPort(82); private static final WorkerNetAddress NET_ADDRESS_2 = new WorkerNetAddress().setHost("localhost") .setRpcPort(83).setDataPort(84).setWebPort(85); private static final List<Long> NO_BLOCKS = ImmutableList.of(); private static final Map<String, List<Long>> NO_BLOCKS_ON_TIERS = ImmutableMap.of(); private BlockMaster mBlockMaster; private MasterRegistry mRegistry; private ManualClock mClock; private ExecutorService mExecutorService; /** Rule to create a new temporary folder during each test. */ @Rule public TemporaryFolder mTestFolder = new TemporaryFolder(); /** The exception expected to be thrown. */ @Rule public ExpectedException mThrown = ExpectedException.none(); @ClassRule public static ManuallyScheduleHeartbeat sManuallySchedule = new ManuallyScheduleHeartbeat( HeartbeatContext.MASTER_LOST_WORKER_DETECTION); /** * Sets up the dependencies before a test runs. */ @Before public void before() throws Exception { mRegistry = new MasterRegistry(); JournalFactory factory = new Journal.Factory(new URI(mTestFolder.newFolder().getAbsolutePath())); mClock = new ManualClock(); mExecutorService = Executors.newFixedThreadPool(2, ThreadFactoryUtils.build("TestBlockMaster-%d", true)); mBlockMaster = new DefaultBlockMaster(factory, mClock, ExecutorServiceFactories.constantExecutorServiceFactory(mExecutorService)); mRegistry.add(BlockMaster.class, mBlockMaster); mRegistry.start(true); } /** * Stops the master after a test ran. */ @After public void after() throws Exception { mRegistry.stop(); } @Test public void countBytes() throws Exception { // Register two workers long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); long worker2 = mBlockMaster.getWorkerId(NET_ADDRESS_2); List<String> tiers = Arrays.asList("MEM", "SSD"); Map<String, Long> worker1TotalBytesOnTiers = ImmutableMap.of("MEM", 10L, "SSD", 20L); Map<String, Long> worker2TotalBytesOnTiers = ImmutableMap.of("MEM", 1000L, "SSD", 2000L); Map<String, Long> worker1UsedBytesOnTiers = ImmutableMap.of("MEM", 1L, "SSD", 2L); Map<String, Long> worker2UsedBytesOnTiers = ImmutableMap.of("MEM", 100L, "SSD", 200L); mBlockMaster.workerRegister(worker1, tiers, worker1TotalBytesOnTiers, worker1UsedBytesOnTiers, NO_BLOCKS_ON_TIERS); mBlockMaster.workerRegister(worker2, tiers, worker2TotalBytesOnTiers, worker2UsedBytesOnTiers, NO_BLOCKS_ON_TIERS); // Check that byte counts are summed correctly. Assert.assertEquals(3030, mBlockMaster.getCapacityBytes()); Assert.assertEquals(303L, mBlockMaster.getUsedBytes()); Assert.assertEquals(ImmutableMap.of("MEM", 1010L, "SSD", 2020L), mBlockMaster.getTotalBytesOnTiers()); Assert.assertEquals(ImmutableMap.of("MEM", 101L, "SSD", 202L), mBlockMaster.getUsedBytesOnTiers()); } @Test public void detectLostWorkers() throws Exception { // Register a worker. long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); mBlockMaster.workerRegister(worker1, ImmutableList.of("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 10L), NO_BLOCKS_ON_TIERS); // Advance the block master's clock by an hour so that worker appears lost. mClock.setTimeMs(System.currentTimeMillis() + Constants.HOUR_MS); // Run the lost worker detector. HeartbeatScheduler.execute(HeartbeatContext.MASTER_LOST_WORKER_DETECTION); // Make sure the worker is detected as lost. List<WorkerInfo> info = mBlockMaster.getLostWorkersInfoList(); Assert.assertEquals(worker1, Iterables.getOnlyElement(info).getId()); } @Test public void workerReregisterRemembersLostWorker() throws Exception { // Register a worker. long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); mBlockMaster.workerRegister(worker1, ImmutableList.of("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 10L), NO_BLOCKS_ON_TIERS); // Advance the block master's clock by an hour so that the worker appears lost. mClock.setTimeMs(System.currentTimeMillis() + Constants.HOUR_MS); // Run the lost worker detector. HeartbeatScheduler.execute(HeartbeatContext.MASTER_LOST_WORKER_DETECTION); // Reregister the worker using its original worker id. mBlockMaster.getWorkerId(NET_ADDRESS_1); mBlockMaster.workerRegister(worker1, ImmutableList.of("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 10L), NO_BLOCKS_ON_TIERS); // Check that there are no longer any lost workers and there is a live worker. Assert.assertEquals(1, mBlockMaster.getWorkerCount()); Assert.assertEquals(0, mBlockMaster.getLostWorkersInfoList().size()); } @Test public void removeBlockTellsWorkersToRemoveTheBlock() throws Exception { // Create a worker with a block. long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); long blockId = 1L; mBlockMaster.workerRegister(worker1, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 0L), NO_BLOCKS_ON_TIERS); mBlockMaster.commitBlock(worker1, 50L, "MEM", blockId, 20L); // Remove the block mBlockMaster.removeBlocks(Arrays.asList(1L), /*delete=*/false); // Check that the worker heartbeat tells the worker to remove the block. Map<String, Long> memUsage = ImmutableMap.of("MEM", 0L); Command heartBeat = mBlockMaster .workerHeartbeat(worker1, memUsage, NO_BLOCKS, NO_BLOCKS_ON_TIERS); Assert.assertEquals(ImmutableList.of(1L), heartBeat.getData()); } @Test public void workerHeartbeatUpdatesMemoryCount() throws Exception { // Create a worker. long worker = mBlockMaster.getWorkerId(NET_ADDRESS_1); Map<String, Long> initialUsedBytesOnTiers = ImmutableMap.of("MEM", 50L); mBlockMaster.workerRegister(worker, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), initialUsedBytesOnTiers, NO_BLOCKS_ON_TIERS); // Update used bytes with a worker heartbeat. Map<String, Long> newUsedBytesOnTiers = ImmutableMap.of("MEM", 50L); mBlockMaster.workerHeartbeat(worker, newUsedBytesOnTiers, NO_BLOCKS, NO_BLOCKS_ON_TIERS); WorkerInfo workerInfo = Iterables.getOnlyElement(mBlockMaster.getWorkerInfoList()); Assert.assertEquals(50, workerInfo.getUsedBytes()); } @Test public void workerHeartbeatUpdatesRemovedBlocks() throws Exception { // Create a worker. long worker = mBlockMaster.getWorkerId(NET_ADDRESS_1); mBlockMaster.workerRegister(worker, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 0L), NO_BLOCKS_ON_TIERS); long blockId = 1L; mBlockMaster.commitBlock(worker, 50L, "MEM", blockId, 20L); // Indicate that blockId is removed on the worker. mBlockMaster.workerHeartbeat(worker, ImmutableMap.of("MEM", 0L), ImmutableList.of(blockId), NO_BLOCKS_ON_TIERS); Assert.assertTrue(mBlockMaster.getBlockInfo(blockId).getLocations().isEmpty()); } @Test public void workerHeartbeatUpdatesAddedBlocks() throws Exception { // Create two workers. long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); mBlockMaster.workerRegister(worker1, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 0L), NO_BLOCKS_ON_TIERS); long worker2 = mBlockMaster.getWorkerId(NET_ADDRESS_2); mBlockMaster.workerRegister(worker2, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 0L), NO_BLOCKS_ON_TIERS); // Commit blockId to worker1. long blockId = 1L; mBlockMaster.commitBlock(worker1, 50L, "MEM", blockId, 20L); // Send a heartbeat from worker2 saying that it's added blockId. List<Long> addedBlocks = ImmutableList.of(blockId); mBlockMaster.workerHeartbeat(worker2, ImmutableMap.of("MEM", 0L), NO_BLOCKS, ImmutableMap.of("MEM", addedBlocks)); // The block now has two locations. Assert.assertEquals(2, mBlockMaster.getBlockInfo(blockId).getLocations().size()); } @Test public void unknownWorkerHeartbeatTriggersRegisterRequest() { Command heartBeat = mBlockMaster.workerHeartbeat(0, null, null, null); Assert.assertEquals(new Command(CommandType.Register, ImmutableList.<Long>of()), heartBeat); } @Test public void stopTerminatesExecutorService() throws Exception { mBlockMaster.stop(); Assert.assertTrue(mExecutorService.isTerminated()); } @Test public void getBlockInfo() throws Exception { // Create a worker with a block. long worker1 = mBlockMaster.getWorkerId(NET_ADDRESS_1); long blockId = 1L; long blockLength = 20L; mBlockMaster.workerRegister(worker1, Arrays.asList("MEM"), ImmutableMap.of("MEM", 100L), ImmutableMap.of("MEM", 0L), NO_BLOCKS_ON_TIERS); mBlockMaster.commitBlock(worker1, 50L, "MEM", blockId, blockLength); BlockLocation blockLocation = new BlockLocation() .setTierAlias("MEM") .setWorkerAddress(NET_ADDRESS_1) .setWorkerId(worker1); BlockInfo expectedBlockInfo = new BlockInfo() .setBlockId(1L) .setLength(20L) .setLocations(ImmutableList.of(blockLocation)); Assert.assertEquals(expectedBlockInfo, mBlockMaster.getBlockInfo(blockId)); } @Test public void stop() throws Exception { mRegistry.stop(); Assert.assertTrue(mExecutorService.isShutdown()); Assert.assertTrue(mExecutorService.isTerminated()); } }