001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import static org.apache.hadoop.hbase.util.RecoverLeaseFSUtils.LEASE_RECOVERABLE_CLASS_NAME; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.mockito.Mockito.mock; 024import static org.mockito.Mockito.times; 025import static org.mockito.Mockito.verify; 026import static org.mockito.Mockito.when; 027 028import java.io.IOException; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.fs.FileSystem; 031import org.apache.hadoop.fs.LocalFileSystem; 032import org.apache.hadoop.fs.Path; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.apache.hadoop.hbase.testclassification.MiscTests; 037import org.apache.hadoop.hdfs.DistributedFileSystem; 038import org.junit.ClassRule; 039import org.junit.Test; 040import org.junit.experimental.categories.Category; 041 042/** 043 * Test our recoverLease loop against mocked up filesystem. 044 */ 045@Category({ MiscTests.class, MediumTests.class }) 046public class TestRecoverLeaseFSUtils { 047 048 @ClassRule 049 public static final HBaseClassTestRule CLASS_RULE = 050 HBaseClassTestRule.forClass(TestRecoverLeaseFSUtils.class); 051 052 private static final HBaseCommonTestingUtil HTU = new HBaseCommonTestingUtil(); 053 static { 054 Configuration conf = HTU.getConfiguration(); 055 conf.setInt("hbase.lease.recovery.first.pause", 10); 056 conf.setInt("hbase.lease.recovery.pause", 10); 057 } 058 059 private static Path FILE = new Path(HTU.getDataTestDir(), "file.txt"); 060 061 /** 062 * Test recover lease eventually succeeding. 063 */ 064 @Test 065 public void testRecoverLease() throws IOException { 066 long startTime = EnvironmentEdgeManager.currentTime(); 067 HTU.getConfiguration().setInt("hbase.lease.recovery.dfs.timeout", 1000); 068 CancelableProgressable reporter = mock(CancelableProgressable.class); 069 when(reporter.progress()).thenReturn(true); 070 DistributedFileSystem dfs = mock(DistributedFileSystem.class); 071 // Fail four times and pass on the fifth. 072 when(dfs.recoverLease(FILE)).thenReturn(false).thenReturn(false).thenReturn(false) 073 .thenReturn(false).thenReturn(true); 074 RecoverLeaseFSUtils.recoverFileLease(dfs, FILE, HTU.getConfiguration(), reporter); 075 verify(dfs, times(5)).recoverLease(FILE); 076 // Make sure we waited at least hbase.lease.recovery.dfs.timeout * 3 (the first two 077 // invocations will happen pretty fast... the we fall into the longer wait loop). 078 assertTrue((EnvironmentEdgeManager.currentTime() - startTime) 079 > (3 * HTU.getConfiguration().getInt("hbase.lease.recovery.dfs.timeout", 61000))); 080 } 081 082 private interface FakeLeaseRecoverable { 083 @SuppressWarnings("unused") 084 boolean recoverLease(Path p) throws IOException; 085 086 @SuppressWarnings("unused") 087 boolean isFileClosed(Path p) throws IOException; 088 } 089 090 private static abstract class RecoverableFileSystem extends FileSystem 091 implements FakeLeaseRecoverable { 092 @Override 093 public boolean recoverLease(Path p) throws IOException { 094 return true; 095 } 096 097 @Override 098 public boolean isFileClosed(Path p) throws IOException { 099 return true; 100 } 101 } 102 103 /** 104 * Test that we can use reflection to access LeaseRecoverable methods. 105 */ 106 @Test 107 public void testLeaseRecoverable() throws IOException { 108 try { 109 // set LeaseRecoverable to FakeLeaseRecoverable for testing 110 RecoverLeaseFSUtils.initializeRecoverLeaseMethod(FakeLeaseRecoverable.class.getName()); 111 RecoverableFileSystem mockFS = mock(RecoverableFileSystem.class); 112 when(mockFS.recoverLease(FILE)).thenReturn(true); 113 RecoverLeaseFSUtils.recoverFileLease(mockFS, FILE, HTU.getConfiguration()); 114 verify(mockFS, times(1)).recoverLease(FILE); 115 116 assertTrue(RecoverLeaseFSUtils.isLeaseRecoverable(mock(RecoverableFileSystem.class))); 117 } finally { 118 RecoverLeaseFSUtils.initializeRecoverLeaseMethod(LEASE_RECOVERABLE_CLASS_NAME); 119 } 120 } 121 122 /** 123 * Test that isFileClosed makes us recover lease faster. 124 */ 125 @Test 126 public void testIsFileClosed() throws IOException { 127 // Make this time long so it is plain we broke out because of the isFileClosed invocation. 128 HTU.getConfiguration().setInt("hbase.lease.recovery.dfs.timeout", 100000); 129 CancelableProgressable reporter = mock(CancelableProgressable.class); 130 when(reporter.progress()).thenReturn(true); 131 IsFileClosedDistributedFileSystem dfs = mock(IsFileClosedDistributedFileSystem.class); 132 // Now make it so we fail the first two times -- the two fast invocations, then we fall into 133 // the long loop during which we will call isFileClosed.... the next invocation should 134 // therefore return true if we are to break the loop. 135 when(dfs.recoverLease(FILE)).thenReturn(false).thenReturn(false).thenReturn(true); 136 when(dfs.isFileClosed(FILE)).thenReturn(true); 137 RecoverLeaseFSUtils.recoverFileLease(dfs, FILE, HTU.getConfiguration(), reporter); 138 verify(dfs, times(2)).recoverLease(FILE); 139 verify(dfs, times(1)).isFileClosed(FILE); 140 } 141 142 @Test 143 public void testIsLeaseRecoverable() { 144 assertTrue(RecoverLeaseFSUtils.isLeaseRecoverable(new DistributedFileSystem())); 145 assertFalse(RecoverLeaseFSUtils.isLeaseRecoverable(new LocalFileSystem())); 146 } 147 148 /** 149 * Version of DFS that has HDFS-4525 in it. 150 */ 151 private static class IsFileClosedDistributedFileSystem extends DistributedFileSystem { 152 /** 153 * Close status of a file. Copied over from HDFS-4525 154 * @return true if file is already closed 155 **/ 156 @Override 157 public boolean isFileClosed(Path f) throws IOException { 158 return false; 159 } 160 } 161}