2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.client
;
20 import static org
.junit
.Assert
.assertEquals
;
21 import static org
.junit
.Assert
.assertTrue
;
23 import java
.util
.ArrayList
;
24 import java
.util
.Collections
;
25 import java
.util
.List
;
26 import org
.apache
.hadoop
.conf
.Configuration
;
27 import org
.apache
.hadoop
.fs
.FileSystem
;
28 import org
.apache
.hadoop
.fs
.Path
;
29 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
30 import org
.apache
.hadoop
.hbase
.HBaseTestingUtil
;
31 import org
.apache
.hadoop
.hbase
.HConstants
;
32 import org
.apache
.hadoop
.hbase
.TableName
;
33 import org
.apache
.hadoop
.hbase
.io
.encoding
.DataBlockEncoding
;
34 import org
.apache
.hadoop
.hbase
.master
.snapshot
.SnapshotManager
;
35 import org
.apache
.hadoop
.hbase
.regionserver
.BloomType
;
36 import org
.apache
.hadoop
.hbase
.regionserver
.ConstantSizeRegionSplitPolicy
;
37 import org
.apache
.hadoop
.hbase
.snapshot
.SnapshotTestingUtils
;
38 import org
.apache
.hadoop
.hbase
.testclassification
.ClientTests
;
39 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
40 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
41 import org
.apache
.hadoop
.hbase
.util
.EnvironmentEdgeManager
;
42 import org
.junit
.After
;
43 import org
.junit
.AfterClass
;
44 import org
.junit
.Before
;
45 import org
.junit
.BeforeClass
;
46 import org
.junit
.ClassRule
;
47 import org
.junit
.Test
;
48 import org
.junit
.experimental
.categories
.Category
;
49 import org
.slf4j
.Logger
;
50 import org
.slf4j
.LoggerFactory
;
53 * Test class to verify that metadata is consistent before and after a snapshot attempt.
55 @Category({MediumTests
.class, ClientTests
.class})
56 public class TestSnapshotMetadata
{
59 public static final HBaseClassTestRule CLASS_RULE
=
60 HBaseClassTestRule
.forClass(TestSnapshotMetadata
.class);
62 private static final Logger LOG
= LoggerFactory
.getLogger(TestSnapshotMetadata
.class);
64 private static final HBaseTestingUtil UTIL
= new HBaseTestingUtil();
65 private static final int NUM_RS
= 2;
66 private static final String STRING_TABLE_NAME
= "TestSnapshotMetadata";
68 private static final String MAX_VERSIONS_FAM_STR
= "fam_max_columns";
69 private static final byte[] MAX_VERSIONS_FAM
= Bytes
.toBytes(MAX_VERSIONS_FAM_STR
);
71 private static final String COMPRESSED_FAM_STR
= "fam_compressed";
72 private static final byte[] COMPRESSED_FAM
= Bytes
.toBytes(COMPRESSED_FAM_STR
);
74 private static final String BLOCKSIZE_FAM_STR
= "fam_blocksize";
75 private static final byte[] BLOCKSIZE_FAM
= Bytes
.toBytes(BLOCKSIZE_FAM_STR
);
77 private static final String BLOOMFILTER_FAM_STR
= "fam_bloomfilter";
78 private static final byte[] BLOOMFILTER_FAM
= Bytes
.toBytes(BLOOMFILTER_FAM_STR
);
80 private static final String TEST_CONF_CUSTOM_VALUE
= "TestCustomConf";
81 private static final String TEST_CUSTOM_VALUE
= "TestCustomValue";
83 private static final byte[][] families
= {
84 MAX_VERSIONS_FAM
, BLOOMFILTER_FAM
, COMPRESSED_FAM
, BLOCKSIZE_FAM
87 private static final DataBlockEncoding DATA_BLOCK_ENCODING_TYPE
= DataBlockEncoding
.FAST_DIFF
;
88 private static final BloomType BLOOM_TYPE
= BloomType
.ROW
;
89 private static final int BLOCK_SIZE
= 98;
90 private static final int MAX_VERSIONS
= 8;
93 private String originalTableDescription
;
94 private TableDescriptor originalTableDescriptor
;
95 TableName originalTableName
;
97 private static FileSystem fs
;
98 private static Path rootDir
;
101 public static void setupCluster() throws Exception
{
102 setupConf(UTIL
.getConfiguration());
103 UTIL
.startMiniCluster(NUM_RS
);
105 fs
= UTIL
.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem();
106 rootDir
= UTIL
.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
110 public static void cleanupTest() throws Exception
{
112 UTIL
.shutdownMiniCluster();
113 } catch (Exception e
) {
114 LOG
.warn("failure shutting down cluster", e
);
118 private static void setupConf(Configuration conf
) {
119 // enable snapshot support
120 conf
.setBoolean(SnapshotManager
.HBASE_SNAPSHOT_ENABLED
, true);
122 conf
.setInt("hbase.regionsever.info.port", -1);
123 // change the flush size to a small amount, regulating number of store files
124 conf
.setInt("hbase.hregion.memstore.flush.size", 25000);
125 // so make sure we get a compaction when doing a load, but keep around
126 // some files in the store
127 conf
.setInt("hbase.hstore.compaction.min", 10);
128 conf
.setInt("hbase.hstore.compactionThreshold", 10);
129 // block writes if we get to 12 store files
130 conf
.setInt("hbase.hstore.blockingStoreFiles", 12);
131 conf
.setInt("hbase.regionserver.msginterval", 100);
132 conf
.setBoolean("hbase.master.enabletable.roundrobin", true);
133 // Avoid potentially aggressive splitting which would cause snapshot to fail
134 conf
.set(HConstants
.HBASE_REGION_SPLIT_POLICY_KEY
,
135 ConstantSizeRegionSplitPolicy
.class.getName());
139 public void setup() throws Exception
{
140 admin
= UTIL
.getAdmin();
141 createTableWithNonDefaultProperties();
145 public void tearDown() throws Exception
{
146 SnapshotTestingUtils
.deleteAllSnapshots(admin
);
150 * Create a table that has non-default properties so we can see if they hold
152 private void createTableWithNonDefaultProperties() throws Exception
{
153 final long startTime
= EnvironmentEdgeManager
.currentTime();
154 final String sourceTableNameAsString
= STRING_TABLE_NAME
+ startTime
;
155 originalTableName
= TableName
.valueOf(sourceTableNameAsString
);
157 // enable replication on a column family
158 ColumnFamilyDescriptor maxVersionsColumn
= ColumnFamilyDescriptorBuilder
159 .newBuilder(MAX_VERSIONS_FAM
).setMaxVersions(MAX_VERSIONS
).build();
160 ColumnFamilyDescriptor bloomFilterColumn
= ColumnFamilyDescriptorBuilder
161 .newBuilder(BLOOMFILTER_FAM
).setBloomFilterType(BLOOM_TYPE
).build();
162 ColumnFamilyDescriptor dataBlockColumn
= ColumnFamilyDescriptorBuilder
163 .newBuilder(COMPRESSED_FAM
).setDataBlockEncoding(DATA_BLOCK_ENCODING_TYPE
).build();
164 ColumnFamilyDescriptor blockSizeColumn
=
165 ColumnFamilyDescriptorBuilder
.newBuilder(BLOCKSIZE_FAM
).setBlocksize(BLOCK_SIZE
).build();
167 TableDescriptor tableDescriptor
= TableDescriptorBuilder
168 .newBuilder(TableName
.valueOf(sourceTableNameAsString
)).setColumnFamily(maxVersionsColumn
)
169 .setColumnFamily(bloomFilterColumn
).setColumnFamily(dataBlockColumn
)
170 .setColumnFamily(blockSizeColumn
).setValue(TEST_CUSTOM_VALUE
, TEST_CUSTOM_VALUE
)
171 .setValue(TEST_CONF_CUSTOM_VALUE
, TEST_CONF_CUSTOM_VALUE
).build();
172 assertTrue(tableDescriptor
.getValues().size() > 0);
174 admin
.createTable(tableDescriptor
);
175 Table original
= UTIL
.getConnection().getTable(originalTableName
);
176 originalTableName
= TableName
.valueOf(sourceTableNameAsString
);
177 originalTableDescriptor
= admin
.getDescriptor(originalTableName
);
178 originalTableDescription
= originalTableDescriptor
.toStringCustomizedValues();
185 * Verify that the describe for a cloned table matches the describe from the original.
188 public void testDescribeMatchesAfterClone() throws Exception
{
189 // Clone the original table
190 final String clonedTableNameAsString
= "clone" + originalTableName
;
191 final TableName clonedTableName
= TableName
.valueOf(clonedTableNameAsString
);
192 final String snapshotNameAsString
= "snapshot" + originalTableName
193 + EnvironmentEdgeManager
.currentTime();
194 final String snapshotName
= snapshotNameAsString
;
196 // restore the snapshot into a cloned table and examine the output
197 List
<byte[]> familiesList
= new ArrayList
<>();
198 Collections
.addAll(familiesList
, families
);
200 // Create a snapshot in which all families are empty
201 SnapshotTestingUtils
.createSnapshotAndValidate(admin
, originalTableName
, null,
202 familiesList
, snapshotNameAsString
, rootDir
, fs
, /* onlineSnapshot= */ false);
204 admin
.cloneSnapshot(snapshotName
, clonedTableName
);
205 Table clonedTable
= UTIL
.getConnection().getTable(clonedTableName
);
206 TableDescriptor cloneHtd
= admin
.getDescriptor(clonedTableName
);
208 originalTableDescription
.replace(originalTableName
.getNameAsString(),clonedTableNameAsString
),
209 cloneHtd
.toStringCustomizedValues());
211 // Verify the custom fields
212 assertEquals(originalTableDescriptor
.getValues().size(),
213 cloneHtd
.getValues().size());
214 assertEquals(TEST_CUSTOM_VALUE
, cloneHtd
.getValue(TEST_CUSTOM_VALUE
));
215 assertEquals(TEST_CONF_CUSTOM_VALUE
, cloneHtd
.getValue(TEST_CONF_CUSTOM_VALUE
));
216 assertEquals(originalTableDescriptor
.getValues(), cloneHtd
.getValues());
218 admin
.enableTable(originalTableName
);
223 * Verify that the describe for a restored table matches the describe for one the original.
226 public void testDescribeMatchesAfterRestore() throws Exception
{
227 runRestoreWithAdditionalMetadata(false);
231 * Verify that if metadata changed after a snapshot was taken, that the old metadata replaces the
232 * new metadata during a restore
235 public void testDescribeMatchesAfterMetadataChangeAndRestore() throws Exception
{
236 runRestoreWithAdditionalMetadata(true);
240 * Verify that when the table is empty, making metadata changes after the restore does not affect
241 * the restored table's original metadata
245 public void testDescribeOnEmptyTableMatchesAfterMetadataChangeAndRestore() throws Exception
{
246 runRestoreWithAdditionalMetadata(true, false);
249 private void runRestoreWithAdditionalMetadata(boolean changeMetadata
) throws Exception
{
250 runRestoreWithAdditionalMetadata(changeMetadata
, true);
253 private void runRestoreWithAdditionalMetadata(boolean changeMetadata
, boolean addData
)
256 if (admin
.isTableDisabled(originalTableName
)) {
257 admin
.enableTable(originalTableName
);
260 // populate it with data
261 final byte[] familyForUpdate
= BLOCKSIZE_FAM
;
263 List
<byte[]> familiesWithDataList
= new ArrayList
<>();
264 List
<byte[]> emptyFamiliesList
= new ArrayList
<>();
266 Table original
= UTIL
.getConnection().getTable(originalTableName
);
267 UTIL
.loadTable(original
, familyForUpdate
); // family arbitrarily chosen
270 for (byte[] family
: families
) {
271 if (family
!= familyForUpdate
) {
272 emptyFamiliesList
.add(family
);
275 familiesWithDataList
.add(familyForUpdate
);
277 Collections
.addAll(emptyFamiliesList
, families
);
280 // take a "disabled" snapshot
281 final String snapshotNameAsString
= "snapshot" + originalTableName
282 + EnvironmentEdgeManager
.currentTime();
284 SnapshotTestingUtils
.createSnapshotAndValidate(admin
, originalTableName
,
285 familiesWithDataList
, emptyFamiliesList
, snapshotNameAsString
, rootDir
, fs
,
286 /* onlineSnapshot= */ false);
288 admin
.enableTable(originalTableName
);
290 if (changeMetadata
) {
291 final String newFamilyNameAsString
= "newFamily" + EnvironmentEdgeManager
.currentTime();
292 final byte[] newFamilyName
= Bytes
.toBytes(newFamilyNameAsString
);
294 admin
.disableTable(originalTableName
);
295 ColumnFamilyDescriptor familyDescriptor
= ColumnFamilyDescriptorBuilder
.of(newFamilyName
);
296 admin
.addColumnFamily(originalTableName
, familyDescriptor
);
297 assertTrue("New column family was not added.",
298 admin
.getDescriptor(originalTableName
).toString().contains(newFamilyNameAsString
));
302 if (!admin
.isTableDisabled(originalTableName
)) {
303 admin
.disableTable(originalTableName
);
306 admin
.restoreSnapshot(snapshotNameAsString
);
307 admin
.enableTable(originalTableName
);
309 // verify that the descrption is reverted
310 try (Table original
= UTIL
.getConnection().getTable(originalTableName
)) {
311 assertEquals(originalTableDescriptor
, admin
.getDescriptor(originalTableName
));
312 assertEquals(originalTableDescriptor
, original
.getDescriptor());