| /** |
| * 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 |
| * <p/> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p/> |
| * 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.hadoop.hdfs.server.datanode; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.hdfs.server.datanode.BPServiceActor.Scheduler; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.Timeout; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Random; |
| |
| import static java.lang.Math.abs; |
| import static org.hamcrest.core.Is.is; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.spy; |
| |
| |
| /** |
| * Verify the block report and heartbeat scheduling logic of BPServiceActor |
| * using a few different values . |
| */ |
| public class TestBpServiceActorScheduler { |
| protected static final Log LOG = LogFactory.getLog(TestBpServiceActorScheduler.class); |
| |
| @Rule |
| public Timeout timeout = new Timeout(300000); |
| |
| private static final long HEARTBEAT_INTERVAL_MS = 5000; // 5 seconds |
| private static final long LIFELINE_INTERVAL_MS = 3 * HEARTBEAT_INTERVAL_MS; |
| private static final long BLOCK_REPORT_INTERVAL_MS = 10000; // 10 seconds |
| private static final long OUTLIER_REPORT_INTERVAL_MS = 10000; // 10 seconds |
| private final Random random = new Random(System.nanoTime()); |
| |
| @Test |
| public void testInit() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| assertTrue(scheduler.isHeartbeatDue(now)); |
| assertTrue(scheduler.isBlockReportDue(scheduler.monotonicNow())); |
| } |
| } |
| |
| @Test |
| public void testScheduleBlockReportImmediate() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.scheduleBlockReport(0); |
| assertTrue(scheduler.resetBlockReportTime); |
| assertThat(scheduler.nextBlockReportTime, is(now)); |
| } |
| } |
| |
| @Test |
| public void testScheduleBlockReportDelayed() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| final long delayMs = 10; |
| scheduler.scheduleBlockReport(delayMs); |
| assertTrue(scheduler.resetBlockReportTime); |
| assertTrue(scheduler.nextBlockReportTime - now >= 0); |
| assertTrue(scheduler.nextBlockReportTime - (now + delayMs) < 0); |
| } |
| } |
| |
| /** |
| * If resetBlockReportTime is true then the next block report must be scheduled |
| * in the range [now, now + BLOCK_REPORT_INTERVAL_SEC). |
| */ |
| @Test |
| public void testScheduleNextBlockReport() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| assertTrue(scheduler.resetBlockReportTime); |
| scheduler.scheduleNextBlockReport(); |
| assertTrue(scheduler.nextBlockReportTime - (now + BLOCK_REPORT_INTERVAL_MS) < 0); |
| } |
| } |
| |
| /** |
| * If resetBlockReportTime is false then the next block report must be scheduled |
| * exactly at (now + BLOCK_REPORT_INTERVAL_SEC). |
| */ |
| @Test |
| public void testScheduleNextBlockReport2() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.resetBlockReportTime = false; |
| scheduler.scheduleNextBlockReport(); |
| assertThat(scheduler.nextBlockReportTime, is(now + BLOCK_REPORT_INTERVAL_MS)); |
| } |
| } |
| |
| /** |
| * Tests the case when a block report was delayed past its scheduled time. |
| * In that case the next block report should not be delayed for a full interval. |
| */ |
| @Test |
| public void testScheduleNextBlockReport3() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.resetBlockReportTime = false; |
| |
| // Make it look like the block report was scheduled to be sent between 1-3 |
| // intervals ago but sent just now. |
| final long blockReportDelay = |
| BLOCK_REPORT_INTERVAL_MS + random.nextInt(2 * (int) BLOCK_REPORT_INTERVAL_MS); |
| final long origBlockReportTime = now - blockReportDelay; |
| scheduler.nextBlockReportTime = origBlockReportTime; |
| scheduler.scheduleNextBlockReport(); |
| assertTrue(scheduler.nextBlockReportTime - now < BLOCK_REPORT_INTERVAL_MS); |
| assertTrue(((scheduler.nextBlockReportTime - origBlockReportTime) % BLOCK_REPORT_INTERVAL_MS) == 0); |
| } |
| } |
| |
| @Test |
| public void testScheduleHeartbeat() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.scheduleNextHeartbeat(); |
| assertFalse(scheduler.isHeartbeatDue(now)); |
| scheduler.scheduleHeartbeat(); |
| assertTrue(scheduler.isHeartbeatDue(now)); |
| } |
| } |
| |
| |
| /** |
| * Regression test for HDFS-9305. |
| * Delayed processing of a heartbeat can cause a subsequent heartbeat |
| * storm. |
| */ |
| @Test |
| public void testScheduleDelayedHeartbeat() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.scheduleNextHeartbeat(); |
| assertFalse(scheduler.isHeartbeatDue(now)); |
| |
| // Simulate a delayed heartbeat e.g. due to slow processing by NN. |
| scheduler.nextHeartbeatTime = now - (HEARTBEAT_INTERVAL_MS * 10); |
| scheduler.scheduleNextHeartbeat(); |
| |
| // Ensure that the next heartbeat is not due immediately. |
| assertFalse(scheduler.isHeartbeatDue(now)); |
| } |
| } |
| |
| @Test |
| public void testScheduleLifeline() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| scheduler.scheduleNextLifeline(now); |
| assertFalse(scheduler.isLifelineDue(now)); |
| assertThat(scheduler.getLifelineWaitTime(), is(LIFELINE_INTERVAL_MS)); |
| scheduler.scheduleNextLifeline(now - LIFELINE_INTERVAL_MS); |
| assertTrue(scheduler.isLifelineDue(now)); |
| assertThat(scheduler.getLifelineWaitTime(), is(0L)); |
| } |
| } |
| |
| @Test |
| public void testOutlierReportScheduling() { |
| for (final long now : getTimestamps()) { |
| Scheduler scheduler = makeMockScheduler(now); |
| assertTrue(scheduler.isOutliersReportDue(now)); |
| scheduler.scheduleNextOutlierReport(); |
| assertFalse(scheduler.isOutliersReportDue(now)); |
| assertFalse(scheduler.isOutliersReportDue(now + 1)); |
| assertTrue(scheduler.isOutliersReportDue( |
| now + OUTLIER_REPORT_INTERVAL_MS)); |
| } |
| } |
| |
| private Scheduler makeMockScheduler(long now) { |
| LOG.info("Using now = " + now); |
| Scheduler mockScheduler = spy(new Scheduler( |
| HEARTBEAT_INTERVAL_MS, LIFELINE_INTERVAL_MS, |
| BLOCK_REPORT_INTERVAL_MS, OUTLIER_REPORT_INTERVAL_MS)); |
| doReturn(now).when(mockScheduler).monotonicNow(); |
| mockScheduler.nextBlockReportTime = now; |
| mockScheduler.nextHeartbeatTime = now; |
| mockScheduler.nextOutliersReportTime = now; |
| return mockScheduler; |
| } |
| |
| List<Long> getTimestamps() { |
| return Arrays.asList( |
| 0L, Long.MIN_VALUE, Long.MAX_VALUE, // test boundaries |
| Long.MAX_VALUE - 1, // test integer overflow |
| abs(random.nextLong()), // positive random |
| -abs(random.nextLong())); // negative random |
| } |
| } |