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.Assert.assertArrayEquals;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertThrows;
023
024import java.io.IOException;
025import org.apache.hadoop.hbase.DoNotRetryIOException;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.HBaseTestingUtility;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.TableNameTestRule;
030import org.apache.hadoop.hbase.TableNotEnabledException;
031import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
032import org.apache.hadoop.hbase.client.Get;
033import org.apache.hadoop.hbase.client.Put;
034import org.apache.hadoop.hbase.client.Table;
035import org.apache.hadoop.hbase.client.TableDescriptor;
036import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
037import org.apache.hadoop.hbase.testclassification.MediumTests;
038import org.apache.hadoop.hbase.testclassification.RegionServerTests;
039import org.apache.hadoop.hbase.util.Bytes;
040import org.junit.AfterClass;
041import org.junit.BeforeClass;
042import org.junit.ClassRule;
043import org.junit.Rule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046
047import org.apache.hbase.thirdparty.com.google.common.collect.Iterables;
048
049/**
050 * Test changing store file tracker implementation by altering table.
051 */
052@Category({ RegionServerTests.class, MediumTests.class })
053public class TestChangeStoreFileTracker {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057    HBaseClassTestRule.forClass(TestChangeStoreFileTracker.class);
058
059  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
060
061  @Rule
062  public final TableNameTestRule tableName = new TableNameTestRule();
063
064  @BeforeClass
065  public static void setUp() throws Exception {
066    UTIL.startMiniCluster(1);
067  }
068
069  @AfterClass
070  public static void tearDown() throws IOException {
071    UTIL.shutdownMiniCluster();
072  }
073
074  @Test(expected = DoNotRetryIOException.class)
075  public void testCreateError() throws IOException {
076    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
077      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family"))
078      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
079        StoreFileTrackerFactory.Trackers.MIGRATION.name())
080      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
081      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
082      .build();
083    UTIL.getAdmin().createTable(td);
084  }
085
086  @Test(expected = DoNotRetryIOException.class)
087  public void testModifyError1() throws IOException {
088    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
089      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
090    UTIL.getAdmin().createTable(td);
091    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
092      .setValue(StoreFileTrackerFactory.TRACKER_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
093      .build();
094    UTIL.getAdmin().modifyTable(newTd);
095  }
096
097  @Test(expected = DoNotRetryIOException.class)
098  public void testModifyError2() throws IOException {
099    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
100      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
101    UTIL.getAdmin().createTable(td);
102    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
103      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
104        StoreFileTrackerFactory.Trackers.MIGRATION.name())
105      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
106      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
107      .build();
108    UTIL.getAdmin().modifyTable(newTd);
109  }
110
111  @Test(expected = DoNotRetryIOException.class)
112  public void testModifyError3() throws IOException {
113    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
114      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
115    UTIL.getAdmin().createTable(td);
116    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
117      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
118        StoreFileTrackerFactory.Trackers.MIGRATION.name())
119      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
120      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
121      .build();
122    UTIL.getAdmin().modifyTable(newTd);
123  }
124
125  // return the TableDescriptor for creating table
126  private TableDescriptor createTableAndChangeToMigrationTracker() throws IOException {
127    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
128      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
129    UTIL.getAdmin().createTable(td);
130    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
131      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
132        StoreFileTrackerFactory.Trackers.MIGRATION.name())
133      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
134      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
135      .build();
136    UTIL.getAdmin().modifyTable(newTd);
137    return td;
138  }
139
140  @Test(expected = DoNotRetryIOException.class)
141  public void testModifyError4() throws IOException {
142    TableDescriptor td = createTableAndChangeToMigrationTracker();
143    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
144      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
145        StoreFileTrackerFactory.Trackers.MIGRATION.name())
146      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
147      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
148      .build();
149    UTIL.getAdmin().modifyTable(newTd);
150  }
151
152  @Test(expected = DoNotRetryIOException.class)
153  public void testModifyError5() throws IOException {
154    TableDescriptor td = createTableAndChangeToMigrationTracker();
155    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
156      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
157        StoreFileTrackerFactory.Trackers.MIGRATION.name())
158      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
159      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
160      .build();
161    UTIL.getAdmin().modifyTable(newTd);
162  }
163
164  @Test(expected = DoNotRetryIOException.class)
165  public void testModifyError6() throws IOException {
166    TableDescriptor td = createTableAndChangeToMigrationTracker();
167    TableDescriptor newTd =
168      TableDescriptorBuilder.newBuilder(td).setValue(StoreFileTrackerFactory.TRACKER_IMPL,
169        StoreFileTrackerFactory.Trackers.DEFAULT.name()).build();
170    UTIL.getAdmin().modifyTable(newTd);
171  }
172
173  @Test(expected = DoNotRetryIOException.class)
174  public void testModifyError7() throws IOException {
175    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
176      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
177    UTIL.getAdmin().createTable(td);
178    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(tableName.getTableName())
179      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family"))
180      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("family1"))
181        .setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL,
182          StoreFileTrackerFactory.Trackers.MIGRATION.name())
183        .build())
184      .build();
185    UTIL.getAdmin().modifyTable(newTd);
186  }
187
188  // actually a NPE as we do not specify the src and dst impl for migration store file tracker
189  @Test(expected = IOException.class)
190  public void testModifyError8() throws IOException {
191    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
192      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
193    UTIL.getAdmin().createTable(td);
194    TableDescriptor newTd =
195      TableDescriptorBuilder.newBuilder(td).setValue(StoreFileTrackerFactory.TRACKER_IMPL,
196        StoreFileTrackerFactory.Trackers.MIGRATION.name()).build();
197    UTIL.getAdmin().modifyTable(newTd);
198  }
199
200  @Test
201  public void testModifyError9() throws IOException {
202    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName.getTableName())
203      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).build();
204    UTIL.getAdmin().createTable(td);
205    UTIL.getAdmin().disableTable(td.getTableName());
206    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
207      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
208        StoreFileTrackerFactory.Trackers.MIGRATION.name())
209      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
210      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
211      .build();
212    UTIL.getAdmin().modifyTable(newTd);
213    TableDescriptor newTd2 = TableDescriptorBuilder.newBuilder(td)
214      .setValue(StoreFileTrackerFactory.TRACKER_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
215      .build();
216    // changing from MIGRATION while table is disabled is not allowed
217    assertThrows(TableNotEnabledException.class, () -> UTIL.getAdmin().modifyTable(newTd2));
218  }
219
220  private String getStoreFileName(TableName table, byte[] family) {
221    return Iterables
222      .getOnlyElement(Iterables.getOnlyElement(UTIL.getMiniHBaseCluster().getRegions(table))
223        .getStore(family).getStorefiles())
224      .getPath().getName();
225  }
226
227  @Test
228  public void testModify() throws IOException {
229    TableName tn = tableName.getTableName();
230    byte[] row = Bytes.toBytes("row");
231    byte[] family = Bytes.toBytes("family");
232    byte[] qualifier = Bytes.toBytes("qualifier");
233    byte[] value = Bytes.toBytes("value");
234    TableDescriptor td = TableDescriptorBuilder.newBuilder(tn)
235      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build();
236    UTIL.getAdmin().createTable(td);
237    try (Table table = UTIL.getConnection().getTable(tn)) {
238      table.put(new Put(row).addColumn(family, qualifier, value));
239    }
240    UTIL.flush(tn);
241    String fileName = getStoreFileName(tn, family);
242
243    TableDescriptor newTd = TableDescriptorBuilder.newBuilder(td)
244      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
245        StoreFileTrackerFactory.Trackers.MIGRATION.name())
246      .setValue(MigrationStoreFileTracker.SRC_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name())
247      .setValue(MigrationStoreFileTracker.DST_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
248      .build();
249    UTIL.getAdmin().modifyTable(newTd);
250    assertEquals(fileName, getStoreFileName(tn, family));
251    try (Table table = UTIL.getConnection().getTable(tn)) {
252      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qualifier));
253    }
254
255    TableDescriptor newTd2 = TableDescriptorBuilder.newBuilder(td)
256      .setValue(StoreFileTrackerFactory.TRACKER_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
257      .build();
258    UTIL.getAdmin().modifyTable(newTd2);
259    assertEquals(fileName, getStoreFileName(tn, family));
260    try (Table table = UTIL.getConnection().getTable(tn)) {
261      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qualifier));
262    }
263  }
264}