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}