/** * 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.hadoop.conf.Configuration; import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdfs.*; import org.apache.hadoop.hdfs.net.*; import org.apache.hadoop.hdfs.protocol.*; import org.apache.hadoop.hdfs.protocol.datatransfer.*; import org.apache.hadoop.hdfs.server.datanode.metrics.DataNodeMetrics; import org.apache.hadoop.util.DataChecksum; import static org.apache.hadoop.hdfs.DFSConfigKeys.*; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; import org.mockito.ArgumentCaptor; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Arrays; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.*; /** * Mock-based unit test to verify that the DataXceiver correctly handles the * LazyPersist hint from clients. */ public class TestDataXceiverLazyPersistHint { @Rule public Timeout timeout = new Timeout(300000); private enum PeerLocality { LOCAL, REMOTE } private enum NonLocalLazyPersist { ALLOWED, NOT_ALLOWED } /** * Ensure that the correct hint is passed to the block receiver when * the client is local. */ @Test public void testWithLocalClient() throws IOException { ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); DataXceiver xceiver = makeStubDataXceiver( PeerLocality.LOCAL, NonLocalLazyPersist.NOT_ALLOWED, captor); for (Boolean lazyPersistSetting : Arrays.asList(true, false)) { issueWriteBlockCall(xceiver, lazyPersistSetting); assertThat(captor.getValue(), is(lazyPersistSetting)); } } /** * Ensure that hint is always false when the client is remote. */ @Test public void testWithRemoteClient() throws IOException { ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); DataXceiver xceiver = makeStubDataXceiver( PeerLocality.REMOTE, NonLocalLazyPersist.NOT_ALLOWED, captor); for (Boolean lazyPersistSetting : Arrays.asList(true, false)) { issueWriteBlockCall(xceiver, lazyPersistSetting); assertThat(captor.getValue(), is(false)); } } /** * Ensure that the correct hint is passed to the block receiver when * the client is remote AND dfs.datanode.allow.non.local.lazy.persist * is set to true. */ @Test public void testOverrideWithRemoteClient() throws IOException { ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); DataXceiver xceiver = makeStubDataXceiver( PeerLocality.REMOTE, NonLocalLazyPersist.ALLOWED, captor); for (Boolean lazyPersistSetting : Arrays.asList(true, false)) { issueWriteBlockCall(xceiver, lazyPersistSetting); assertThat(captor.getValue(), is(lazyPersistSetting)); } } /** * Issue a write block call with dummy parameters. The only parameter useful * for this test is the value of lazyPersist. */ private void issueWriteBlockCall(DataXceiver xceiver, boolean lazyPersist) throws IOException { xceiver.writeBlock( new ExtendedBlock("Dummy-pool", 0L), StorageType.RAM_DISK, null, "Dummy-Client", new DatanodeInfo[0], new StorageType[0], mock(DatanodeInfo.class), BlockConstructionStage.PIPELINE_SETUP_CREATE, 0, 0, 0, 0, DataChecksum.newDataChecksum(DataChecksum.Type.NULL, 0), CachingStrategy.newDefaultStrategy(), lazyPersist, false, null); } // Helper functions to setup the mock objects. private static DataXceiver makeStubDataXceiver( PeerLocality locality, NonLocalLazyPersist nonLocalLazyPersist, final ArgumentCaptor<Boolean> captor) throws IOException { DataXceiver xceiverSpy = spy(DataXceiver.create( getMockPeer(locality), getMockDn(nonLocalLazyPersist), mock(DataXceiverServer.class))); doReturn(mock(BlockReceiver.class)).when(xceiverSpy).getBlockReceiver( any(ExtendedBlock.class), any(StorageType.class), any(DataInputStream.class), anyString(), anyString(), any(BlockConstructionStage.class), anyLong(), anyLong(), anyLong(), anyString(), any(DatanodeInfo.class), any(DataNode.class), any(DataChecksum.class), any(CachingStrategy.class), captor.capture(), anyBoolean()); doReturn(mock(DataOutputStream.class)).when(xceiverSpy) .getBufferedOutputStream(); return xceiverSpy; } private static Peer getMockPeer(PeerLocality locality) { Peer peer = mock(Peer.class); when(peer.isLocal()).thenReturn(locality == PeerLocality.LOCAL); when(peer.getRemoteAddressString()).thenReturn("1.1.1.1:1000"); when(peer.getLocalAddressString()).thenReturn("2.2.2.2:2000"); return peer; } private static DataNode getMockDn(NonLocalLazyPersist nonLocalLazyPersist) { Configuration conf = new HdfsConfiguration(); conf.setBoolean( DFS_DATANODE_NON_LOCAL_LAZY_PERSIST, nonLocalLazyPersist == NonLocalLazyPersist.ALLOWED); DNConf dnConf = new DNConf(conf); DataNodeMetrics mockMetrics = mock(DataNodeMetrics.class); DataNode mockDn = mock(DataNode.class); when(mockDn.getDnConf()).thenReturn(dnConf); when(mockDn.getConf()).thenReturn(conf); when(mockDn.getMetrics()).thenReturn(mockMetrics); return mockDn; } }