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.regionserver.storefiletracker; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertThrows; 022import static org.junit.jupiter.api.Assertions.fail; 023 024import java.util.Collections; 025import org.apache.hadoop.hbase.TableName; 026import org.apache.hadoop.hbase.TestRefreshHFilesBase; 027import org.apache.hadoop.hbase.master.region.MasterRegionFactory; 028import org.apache.hadoop.hbase.regionserver.CreateStoreFileWriterParams; 029import org.apache.hadoop.hbase.testclassification.RegionServerTests; 030import org.apache.hadoop.hbase.testclassification.SmallTests; 031import org.junit.jupiter.api.AfterEach; 032import org.junit.jupiter.api.BeforeEach; 033import org.junit.jupiter.api.Tag; 034import org.junit.jupiter.api.Test; 035 036@Tag(RegionServerTests.TAG) 037@Tag(SmallTests.TAG) 038public class TestStoreFileTrackerBaseReadOnlyMode extends TestRefreshHFilesBase { 039 private DummyStoreFileTrackerForReadOnlyMode tracker; 040 041 TableName tableName = TableName.valueOf("TestStoreFileTrackerBaseReadOnlyMode"); 042 043 @BeforeEach 044 public void setup() throws Exception { 045 // When true is passed only setup for readonly property is done. 046 // The initial ReadOnly property will be false for table creation 047 baseSetup(true); 048 } 049 050 @AfterEach 051 public void tearDown() throws Exception { 052 baseTearDown(); 053 } 054 055 private void verifyLoadInReadOnlyMode(boolean readOnlyMode, TableName table, 056 boolean expectReadOnly, String msg) throws Exception { 057 try { 058 setReadOnlyMode(readOnlyMode); 059 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, table); 060 tracker.load(); 061 assertEquals(expectReadOnly, tracker.wasReadOnlyLoad(), msg); 062 } finally { 063 setReadOnlyMode(false); 064 } 065 } 066 067 @Test 068 public void testLoadNonWritableTableWhenGlobalReadOnlyEnabled() throws Exception { 069 verifyLoadInReadOnlyMode(true, tableName, true, 070 "For non-writable tables, the doLoadStoreFiles() should get called with readOnly=true"); 071 } 072 073 @Test 074 public void testLoadMetaTableWhenGlobalReadOnlyEnabled() throws Exception { 075 verifyLoadInReadOnlyMode(true, TableName.META_TABLE_NAME, false, 076 "As meta table is always writable, the doLoadStoreFiles should not get called with readOnly=false even if readonly mode is enabled"); 077 } 078 079 @Test 080 public void testLoadMasterStoreTableWhenGlobalReadOnlyEnabled() throws Exception { 081 // As master:store table is always writable, the doLoadStoreFiles should not get called with 082 // readOnly=true 083 verifyLoadInReadOnlyMode(true, MasterRegionFactory.TABLE_NAME, false, 084 "As master:store table is always writable, the doLoadStoreFiles should not get called with readOnly=false even if readonly mode is enabled"); 085 } 086 087 @Test 088 public void testLoadWhenGlobalReadOnlyDisabled() throws Exception { 089 // When readonly mode is disabled, then it should not interfere with normal functionality 090 verifyLoadInReadOnlyMode(false, tableName, false, 091 "As readonly mode is not set, the doLoadStoreFiles() should get called with readOnly=false"); 092 } 093 094 private void verifyReplaceInReadOnlyMode(boolean readOnlyMode, TableName table, 095 boolean expectCompactionExecuted, String msg) { 096 try { 097 setReadOnlyMode(readOnlyMode); 098 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, table); 099 tracker.replace(Collections.emptyList(), Collections.emptyList()); 100 assertEquals(expectCompactionExecuted, tracker.wasCompactionExecuted(), msg); 101 } catch (Exception e) { 102 throw new RuntimeException(e); 103 } finally { 104 setReadOnlyMode(false); 105 } 106 } 107 108 @Test 109 public void testReplaceSkippedForNonWritableTableWhenGlobalReadOnlyEnabled() throws Exception { 110 verifyReplaceInReadOnlyMode(true, tableName, false, 111 "Compaction should not be executed for non-writable table in readonly mode"); 112 } 113 114 @Test 115 public void testReplaceExecutedForMetaTableWhenGlobalReadOnlyEnabled() throws Exception { 116 verifyReplaceInReadOnlyMode(true, TableName.META_TABLE_NAME, true, 117 "Compaction should be executed for meta table in readonly mode"); 118 } 119 120 @Test 121 public void testReplaceExecutedForMasterStoreTableWhenGlobalReadOnlyEnabled() throws Exception { 122 verifyReplaceInReadOnlyMode(true, MasterRegionFactory.TABLE_NAME, true, 123 "Compaction should be executed for master:store table in readonly mode"); 124 } 125 126 @Test 127 public void testReplaceExecutedWhenGlobalReadOnlyDisabled() throws Exception { 128 verifyReplaceInReadOnlyMode(false, tableName, true, 129 "Compaction should be executed for any table when readonly mode is disabled"); 130 } 131 132 private void verifyAddInReadOnlyMode(boolean readOnlyMode, TableName table, 133 boolean expectAddExecuted, String msg) { 134 try { 135 setReadOnlyMode(readOnlyMode); 136 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, table); 137 tracker.add(Collections.emptyList()); 138 assertEquals(expectAddExecuted, tracker.wasAddExecuted(), msg); 139 } catch (Exception e) { 140 throw new RuntimeException(e); 141 } finally { 142 setReadOnlyMode(false); 143 } 144 } 145 146 @Test 147 public void testAddSkippedForNonWritableTableWhenGlobalReadOnlyEnabled() throws Exception { 148 verifyAddInReadOnlyMode(true, tableName, false, 149 "Add should not be executed for non-writable table in readonly mode"); 150 } 151 152 @Test 153 public void testAddExecutedForMetaTableWhenGlobalReadOnlyEnabled() throws Exception { 154 verifyAddInReadOnlyMode(true, TableName.META_TABLE_NAME, true, 155 "Add should be executed for meta table in readonly mode"); 156 } 157 158 @Test 159 public void testAddExecutedForMasterStoreTableWhenGlobalReadOnlyEnabled() throws Exception { 160 verifyAddInReadOnlyMode(true, MasterRegionFactory.TABLE_NAME, true, 161 "Add should be executed for master:store table in readonly mode"); 162 } 163 164 @Test 165 public void testAddExecutedWhenGlobalReadOnlyDisabled() throws Exception { 166 verifyAddInReadOnlyMode(false, tableName, true, 167 "Add should be executed for any table when readonly mode is disabled"); 168 } 169 170 private void verifySetInReadOnlyMode(boolean readOnlyMode, TableName table, 171 boolean expectSetExecuted, String msg) { 172 try { 173 setReadOnlyMode(readOnlyMode); 174 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, table); 175 tracker.set(Collections.emptyList()); 176 assertEquals(expectSetExecuted, tracker.wasSetExecuted(), msg); 177 } catch (Exception e) { 178 throw new RuntimeException(e); 179 } finally { 180 setReadOnlyMode(false); 181 } 182 } 183 184 @Test 185 public void testSetSkippedForNonWritableTableWhenGlobalReadOnlyEnabled() throws Exception { 186 verifySetInReadOnlyMode(true, tableName, false, 187 "Set should not be executed for non-writable table in readonly mode"); 188 } 189 190 @Test 191 public void testSetExecutedForMetaTableWhenGlobalReadOnlyEnabled() throws Exception { 192 verifySetInReadOnlyMode(true, TableName.META_TABLE_NAME, true, 193 "Set should be executed for meta table in readonly mode"); 194 } 195 196 @Test 197 public void testSetExecutedForMasterStoreTableWhenGlobalReadOnlyEnabled() throws Exception { 198 verifySetInReadOnlyMode(true, MasterRegionFactory.TABLE_NAME, true, 199 "Set should be executed for master:store table in readonly mode"); 200 } 201 202 @Test 203 public void testSetExecutedWhenGlobalReadOnlyDisabled() throws Exception { 204 verifySetInReadOnlyMode(false, tableName, true, 205 "Set should be executed for any table when readonly mode is disabled"); 206 } 207 208 private CreateStoreFileWriterParams createParams() { 209 return CreateStoreFileWriterParams.create().maxKeyCount(4).isCompaction(false) 210 .includeMVCCReadpoint(true).includesTag(false).shouldDropBehind(false); 211 } 212 213 private void assertIllegalStateThrown(TableName tableName) { 214 try { 215 setReadOnlyMode(true); 216 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, tableName); 217 assertThrows(IllegalStateException.class, () -> tracker.createWriter(createParams()), 218 "Expected IllegalStateException"); 219 } finally { 220 setReadOnlyMode(false); 221 } 222 } 223 224 private void assertNoIllegalStateThrown(TableName tableName) { 225 try { 226 setReadOnlyMode(true); 227 tracker = new DummyStoreFileTrackerForReadOnlyMode(conf, true, tableName); 228 try { 229 tracker.createWriter(createParams()); 230 } catch (IllegalStateException e) { 231 fail("Should not throw IllegalStateException for table " + tableName); 232 } catch (Exception e) { 233 // Ignore other exceptions as they are not the focus of this test 234 } 235 } finally { 236 setReadOnlyMode(false); 237 } 238 } 239 240 @Test 241 public void testCreateWriterThrowExceptionWhenGlobalReadOnlyEnabled() throws Exception { 242 assertIllegalStateThrown(tableName); 243 } 244 245 @Test 246 public void testCreateWriterNoExceptionMetaTableWhenGlobalReadOnlyEnabled() throws Exception { 247 assertNoIllegalStateThrown(TableName.META_TABLE_NAME); 248 } 249 250 @Test 251 public void testCreateWriterNoExceptionMasterStoreTableWhenGlobalReadOnlyEnabled() 252 throws Exception { 253 assertNoIllegalStateThrown(MasterRegionFactory.TABLE_NAME); 254 } 255}