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.client;
019
020import static org.junit.jupiter.api.Assertions.assertArrayEquals;
021import static org.junit.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertNull;
023import static org.junit.jupiter.api.Assertions.assertThrows;
024
025import java.io.IOException;
026import java.util.function.Supplier;
027import org.apache.hadoop.hbase.DoNotRetryIOException;
028import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.TableNotEnabledException;
031import org.apache.hadoop.hbase.TableNotFoundException;
032import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
033import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
034import org.apache.hadoop.hbase.testclassification.ClientTests;
035import org.apache.hadoop.hbase.testclassification.LargeTests;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.apache.hadoop.hbase.util.FutureUtils;
038import org.junit.jupiter.api.AfterAll;
039import org.junit.jupiter.api.BeforeAll;
040import org.junit.jupiter.api.Tag;
041import org.junit.jupiter.api.TestTemplate;
042
043@Tag(LargeTests.TAG)
044@Tag(ClientTests.TAG)
045@HBaseParameterizedTestTemplate(name = "{index}: policy = {0}")
046public class TestAsyncAdminModifyStoreFileTracker extends TestAsyncAdminBase {
047
048  private static final String SRC_IMPL = "hbase.store.file-tracker.migration.src.impl";
049
050  private static final String DST_IMPL = "hbase.store.file-tracker.migration.dst.impl";
051
052  public TestAsyncAdminModifyStoreFileTracker(Supplier<AsyncAdmin> admin) {
053    super(admin);
054  }
055
056  @BeforeAll
057  public static void setUpBeforeClass() throws Exception {
058    TestAsyncAdminBase.setUpBeforeClass();
059  }
060
061  @AfterAll
062  public static void tearDownAfterClass() throws Exception {
063    TestAsyncAdminBase.tearDownAfterClass();
064  }
065
066  private void verifyModifyTableResult(TableName tableName, byte[] family, byte[] qual, byte[] row,
067    byte[] value, String sft) throws IOException {
068    TableDescriptor td = admin.getDescriptor(tableName).join();
069    assertEquals(sft, td.getValue(StoreFileTrackerFactory.TRACKER_IMPL));
070    // no migration related configs
071    assertNull(td.getValue(SRC_IMPL));
072    assertNull(td.getValue(DST_IMPL));
073    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
074      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qual));
075    }
076  }
077
078  @TestTemplate
079  public void testModifyTableStoreFileTracker() throws IOException {
080    byte[] family = Bytes.toBytes("info");
081    byte[] qual = Bytes.toBytes("q");
082    byte[] row = Bytes.toBytes(0);
083    byte[] value = Bytes.toBytes(1);
084    try (Table table = TEST_UTIL.createTable(tableName, family)) {
085      table.put(new Put(row).addColumn(family, qual, value));
086    }
087    // change to FILE
088    admin.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name())
089      .join();
090    verifyModifyTableResult(tableName, family, qual, row, value,
091      StoreFileTrackerFactory.Trackers.FILE.name());
092
093    // change to FILE again, should have no effect
094    admin.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name())
095      .join();
096    verifyModifyTableResult(tableName, family, qual, row, value,
097      StoreFileTrackerFactory.Trackers.FILE.name());
098
099    // change to MIGRATION, and then to FILE
100    admin.modifyTable(TableDescriptorBuilder.newBuilder(admin.getDescriptor(tableName).join())
101      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
102        StoreFileTrackerFactory.Trackers.MIGRATION.name())
103      .setValue(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
104      .setValue(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build()).join();
105    admin.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name())
106      .join();
107    verifyModifyTableResult(tableName, family, qual, row, value,
108      StoreFileTrackerFactory.Trackers.FILE.name());
109
110    // change to MIGRATION, and then to DEFAULT
111    admin.modifyTable(TableDescriptorBuilder.newBuilder(admin.getDescriptor(tableName).join())
112      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
113        StoreFileTrackerFactory.Trackers.MIGRATION.name())
114      .setValue(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
115      .setValue(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build()).join();
116    admin.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.DEFAULT.name())
117      .join();
118    verifyModifyTableResult(tableName, family, qual, row, value,
119      StoreFileTrackerFactory.Trackers.DEFAULT.name());
120  }
121
122  private void verifyModifyColumnFamilyResult(TableName tableName, byte[] family, byte[] qual,
123    byte[] row, byte[] value, String sft) throws IOException {
124    TableDescriptor td = admin.getDescriptor(tableName).join();
125    ColumnFamilyDescriptor cfd = td.getColumnFamily(family);
126    assertEquals(sft, cfd.getConfigurationValue(StoreFileTrackerFactory.TRACKER_IMPL));
127    // no migration related configs
128    assertNull(cfd.getConfigurationValue(SRC_IMPL));
129    assertNull(cfd.getConfigurationValue(DST_IMPL));
130    assertNull(cfd.getValue(SRC_IMPL));
131    assertNull(cfd.getValue(DST_IMPL));
132    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
133      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qual));
134    }
135  }
136
137  @TestTemplate
138  public void testModifyColumnFamilyStoreFileTracker() throws IOException {
139    byte[] family = Bytes.toBytes("info");
140    byte[] qual = Bytes.toBytes("q");
141    byte[] row = Bytes.toBytes(0);
142    byte[] value = Bytes.toBytes(1);
143    try (Table table = TEST_UTIL.createTable(tableName, family)) {
144      table.put(new Put(row).addColumn(family, qual, value));
145    }
146    // change to FILE
147    admin.modifyColumnFamilyStoreFileTracker(tableName, family,
148      StoreFileTrackerFactory.Trackers.FILE.name()).join();
149    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
150      StoreFileTrackerFactory.Trackers.FILE.name());
151
152    // change to FILE again, should have no effect
153    admin.modifyColumnFamilyStoreFileTracker(tableName, family,
154      StoreFileTrackerFactory.Trackers.FILE.name()).join();
155    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
156      StoreFileTrackerFactory.Trackers.FILE.name());
157
158    // change to MIGRATION, and then to FILE
159    TableDescriptor current = admin.getDescriptor(tableName).join();
160    admin.modifyTable(TableDescriptorBuilder.newBuilder(current)
161      .modifyColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(current.getColumnFamily(family))
162        .setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL,
163          StoreFileTrackerFactory.Trackers.MIGRATION.name())
164        .setConfiguration(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
165        .setConfiguration(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build())
166      .build()).join();
167    admin.modifyColumnFamilyStoreFileTracker(tableName, family,
168      StoreFileTrackerFactory.Trackers.FILE.name()).join();
169    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
170      StoreFileTrackerFactory.Trackers.FILE.name());
171
172    // change to MIGRATION, and then to DEFAULT
173    current = admin.getDescriptor(tableName).join();
174    admin.modifyTable(TableDescriptorBuilder.newBuilder(current)
175      .modifyColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(current.getColumnFamily(family))
176        .setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL,
177          StoreFileTrackerFactory.Trackers.MIGRATION.name())
178        .setConfiguration(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
179        .setConfiguration(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build())
180      .build()).join();
181    admin.modifyColumnFamilyStoreFileTracker(tableName, family,
182      StoreFileTrackerFactory.Trackers.DEFAULT.name()).join();
183    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
184      StoreFileTrackerFactory.Trackers.DEFAULT.name());
185  }
186
187  @TestTemplate
188  public void testModifyStoreFileTrackerError() throws IOException {
189    byte[] family = Bytes.toBytes("info");
190    TEST_UTIL.createTable(tableName, family).close();
191
192    // table not exists
193    assertThrows(TableNotFoundException.class,
194      () -> FutureUtils.get(admin.modifyTableStoreFileTracker(TableName.valueOf("whatever"),
195        StoreFileTrackerFactory.Trackers.FILE.name())));
196    // family not exists
197    assertThrows(NoSuchColumnFamilyException.class,
198      () -> FutureUtils.get(admin.modifyColumnFamilyStoreFileTracker(tableName,
199        Bytes.toBytes("not_exists"), StoreFileTrackerFactory.Trackers.FILE.name())));
200    // to migration
201    assertThrows(DoNotRetryIOException.class, () -> FutureUtils.get(admin
202      .modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.MIGRATION.name())));
203    // disabled
204    admin.disableTable(tableName).join();
205    assertThrows(TableNotEnabledException.class, () -> FutureUtils.get(
206      admin.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name())));
207  }
208}