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