View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import static org.junit.Assert.assertEquals;
22  
23  import java.io.IOException;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.concurrent.atomic.AtomicInteger;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.HBaseTestingUtility;
36  import org.apache.hadoop.hbase.HColumnDescriptor;
37  import org.apache.hadoop.hbase.HConstants;
38  import org.apache.hadoop.hbase.HRegionInfo;
39  import org.apache.hadoop.hbase.HRegionLocation;
40  import org.apache.hadoop.hbase.HTableDescriptor;
41  import org.apache.hadoop.hbase.testclassification.MediumTests;
42  import org.apache.hadoop.hbase.RegionLocations;
43  import org.apache.hadoop.hbase.ServerName;
44  import org.apache.hadoop.hbase.TableName;
45  import org.apache.hadoop.hbase.MetaTableAccessor;
46  import org.apache.hadoop.hbase.MetaTableAccessor.Visitor;
47  import org.apache.hadoop.hbase.client.Admin;
48  import org.apache.hadoop.hbase.client.Connection;
49  import org.apache.hadoop.hbase.client.ConnectionFactory;
50  import org.apache.hadoop.hbase.client.Delete;
51  import org.apache.hadoop.hbase.client.HTable;
52  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
53  import org.apache.hadoop.hbase.client.Result;
54  import org.apache.hadoop.hbase.client.Table;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.junit.AfterClass;
57  import org.junit.BeforeClass;
58  import org.junit.Test;
59  import org.junit.experimental.categories.Category;
60  
61  @Category(MediumTests.class)
62  public class TestMasterOperationsForRegionReplicas {
63    private static final Log LOG = LogFactory.getLog(TestRegionPlacement.class);
64    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
65    private static Connection CONNECTION = null;
66    private static Admin ADMIN;
67    private static int numSlaves = 2;
68    private static Configuration conf;
69  
70    @BeforeClass
71    public static void setupBeforeClass() throws Exception {
72      conf = TEST_UTIL.getConfiguration();
73      conf.setBoolean("hbase.tests.use.shortcircuit.reads", false);
74      TEST_UTIL.startMiniCluster(numSlaves);
75      CONNECTION = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
76      ADMIN = CONNECTION.getAdmin();
77      while(ADMIN.getClusterStatus().getServers().size() < numSlaves) {
78        Thread.sleep(100);
79      }
80    }
81  
82    @AfterClass
83    public static void tearDownAfterClass() throws Exception {
84      if (ADMIN != null) ADMIN.close();
85      if (CONNECTION != null && !CONNECTION.isClosed()) CONNECTION.close();
86      TEST_UTIL.shutdownMiniCluster();
87    }
88  
89    @Test
90    public void testCreateTableWithSingleReplica() throws Exception {
91      final int numRegions = 3;
92      final int numReplica = 1;
93      final TableName table = TableName.valueOf("singleReplicaTable");
94      try {
95        HTableDescriptor desc = new HTableDescriptor(table);
96        desc.setRegionReplication(numReplica);
97        desc.addFamily(new HColumnDescriptor("family"));
98        ADMIN.createTable(desc, Bytes.toBytes("A"), Bytes.toBytes("Z"), numRegions);
99  
100       validateNumberOfRowsInMeta(table, numRegions, ADMIN.getConnection());
101       List<HRegionInfo> hris = MetaTableAccessor.getTableRegions(TEST_UTIL.getZooKeeperWatcher(),
102         ADMIN.getConnection(), table);
103       assert(hris.size() == numRegions * numReplica);
104     } finally {
105       ADMIN.disableTable(table);
106       ADMIN.deleteTable(table);
107     }
108   }
109 
110   @Test
111   public void testCreateTableWithMultipleReplicas() throws Exception {
112     final TableName table = TableName.valueOf("fooTable");
113     final int numRegions = 3;
114     final int numReplica = 2;
115     try {
116       HTableDescriptor desc = new HTableDescriptor(table);
117       desc.setRegionReplication(numReplica);
118       desc.addFamily(new HColumnDescriptor("family"));
119       ADMIN.createTable(desc, Bytes.toBytes("A"), Bytes.toBytes("Z"), numRegions);
120       TEST_UTIL.waitTableEnabled(table);
121       validateNumberOfRowsInMeta(table, numRegions, ADMIN.getConnection());
122 
123       List<HRegionInfo> hris = MetaTableAccessor.getTableRegions(
124         TEST_UTIL.getZooKeeperWatcher(), ADMIN.getConnection(), table);
125       assert(hris.size() == numRegions * numReplica);
126       // check that the master created expected number of RegionState objects
127       for (int i = 0; i < numRegions; i++) {
128         for (int j = 0; j < numReplica; j++) {
129           HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hris.get(i), j);
130           RegionState state = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
131               .getRegionStates().getRegionState(replica);
132           assert (state != null);
133         }
134       }
135 
136       List<Result> metaRows = MetaTableAccessor.fullScanOfMeta(ADMIN.getConnection());
137       int numRows = 0;
138       for (Result result : metaRows) {
139         RegionLocations locations = MetaTableAccessor.getRegionLocations(result);
140         HRegionInfo hri = locations.getRegionLocation().getRegionInfo();
141         if (!hri.getTable().equals(table)) continue;
142         numRows += 1;
143         HRegionLocation[] servers = locations.getRegionLocations();
144         // have two locations for the replicas of a region, and the locations should be different
145         assert(servers.length == 2);
146         assert(!servers[0].equals(servers[1]));
147       }
148       assert(numRows == numRegions);
149 
150       // The same verification of the meta as above but with the SnapshotOfRegionAssignmentFromMeta
151       // class
152       validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica,
153         ADMIN.getConnection());
154 
155       // Now kill the master, restart it and see if the assignments are kept
156       ServerName master = TEST_UTIL.getHBaseClusterInterface().getClusterStatus().getMaster();
157       TEST_UTIL.getHBaseClusterInterface().stopMaster(master);
158       TEST_UTIL.getHBaseClusterInterface().waitForMasterToStop(master, 30000);
159       TEST_UTIL.getHBaseClusterInterface().startMaster(master.getHostname(), master.getPort());
160       TEST_UTIL.getHBaseClusterInterface().waitForActiveAndReadyMaster();
161       for (int i = 0; i < numRegions; i++) {
162         for (int j = 0; j < numReplica; j++) {
163           HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hris.get(i), j);
164           RegionState state = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
165               .getRegionStates().getRegionState(replica);
166           assert (state != null);
167         }
168       }
169       validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica,
170         ADMIN.getConnection());
171 
172       // Now shut the whole cluster down, and verify the assignments are kept so that the
173       // availability constraints are met.
174       TEST_UTIL.getConfiguration().setBoolean("hbase.master.startup.retainassign", true);
175       TEST_UTIL.shutdownMiniHBaseCluster();
176       TEST_UTIL.startMiniHBaseCluster(1, numSlaves);
177       TEST_UTIL.waitTableEnabled(table);
178       validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica,
179         ADMIN.getConnection());
180 
181       // Now shut the whole cluster down, and verify regions are assigned even if there is only
182       // one server running
183       TEST_UTIL.shutdownMiniHBaseCluster();
184       TEST_UTIL.startMiniHBaseCluster(1, 1);
185       TEST_UTIL.waitTableEnabled(table);
186       validateSingleRegionServerAssignment(ADMIN.getConnection(), numRegions, numReplica);
187       for (int i = 1; i < numSlaves; i++) { //restore the cluster
188         TEST_UTIL.getMiniHBaseCluster().startRegionServer();
189       }
190 
191       //check on alter table
192       ADMIN.disableTable(table);
193       assert(ADMIN.isTableDisabled(table));
194       //increase the replica
195       desc.setRegionReplication(numReplica + 1);
196       ADMIN.modifyTable(table, desc);
197       ADMIN.enableTable(table);
198       assert(ADMIN.isTableEnabled(table));
199       List<HRegionInfo> regions = TEST_UTIL.getMiniHBaseCluster().getMaster()
200           .getAssignmentManager().getRegionStates().getRegionsOfTable(table);
201       assert(regions.size() == numRegions * (numReplica + 1));
202 
203       //decrease the replica(earlier, table was modified to have a replica count of numReplica + 1)
204       ADMIN.disableTable(table);
205       desc.setRegionReplication(numReplica);
206       ADMIN.modifyTable(table, desc);
207       ADMIN.enableTable(table);
208       assert(ADMIN.isTableEnabled(table));
209       regions = TEST_UTIL.getMiniHBaseCluster().getMaster()
210           .getAssignmentManager().getRegionStates().getRegionsOfTable(table);
211       assert(regions.size() == numRegions * numReplica);
212       //also make sure the meta table has the replica locations removed
213       hris = MetaTableAccessor.getTableRegions(TEST_UTIL.getZooKeeperWatcher(),
214         ADMIN.getConnection(), table);
215       assert(hris.size() == numRegions * numReplica);
216       //just check that the number of default replica regions in the meta table are the same
217       //as the number of regions the table was created with, and the count of the
218       //replicas is numReplica for each region
219       Map<HRegionInfo, Integer> defaultReplicas = new HashMap<HRegionInfo, Integer>();
220       for (HRegionInfo hri : hris) {
221         Integer i;
222         HRegionInfo regionReplica0 = RegionReplicaUtil.getRegionInfoForDefaultReplica(hri);
223         defaultReplicas.put(regionReplica0,
224             (i = defaultReplicas.get(regionReplica0)) == null ? 1 : i + 1);
225       }
226       assert(defaultReplicas.size() == numRegions);
227       Collection<Integer> counts = new HashSet<Integer>(defaultReplicas.values());
228       assert(counts.size() == 1 && counts.contains(new Integer(numReplica)));
229     } finally {
230       ADMIN.disableTable(table);
231       ADMIN.deleteTable(table);
232     }
233   }
234 
235   //@Test (TODO: enable when we have support for alter_table- HBASE-10361).
236   public void testIncompleteMetaTableReplicaInformation() throws Exception {
237     final TableName table = TableName.valueOf("fooTableTest1");
238     final int numRegions = 3;
239     final int numReplica = 2;
240     try {
241       // Create a table and let the meta table be updated with the location of the
242       // region locations.
243       HTableDescriptor desc = new HTableDescriptor(table);
244       desc.setRegionReplication(numReplica);
245       desc.addFamily(new HColumnDescriptor("family"));
246       ADMIN.createTable(desc, Bytes.toBytes("A"), Bytes.toBytes("Z"), numRegions);
247       TEST_UTIL.waitTableEnabled(table);
248       Set<byte[]> tableRows = new HashSet<byte[]>();
249       List<HRegionInfo> hris = MetaTableAccessor.getTableRegions(TEST_UTIL.getZooKeeperWatcher(),
250         ADMIN.getConnection(), table);
251       for (HRegionInfo hri : hris) {
252         tableRows.add(hri.getRegionName());
253       }
254       ADMIN.disableTable(table);
255       // now delete one replica info from all the rows
256       // this is to make the meta appear to be only partially updated
257       Table metaTable = new HTable(TableName.META_TABLE_NAME, ADMIN.getConnection());
258       for (byte[] row : tableRows) {
259         Delete deleteOneReplicaLocation = new Delete(row);
260         deleteOneReplicaLocation.deleteColumns(HConstants.CATALOG_FAMILY,
261           MetaTableAccessor.getServerColumn(1));
262         deleteOneReplicaLocation.deleteColumns(HConstants.CATALOG_FAMILY,
263           MetaTableAccessor.getSeqNumColumn(1));
264         deleteOneReplicaLocation.deleteColumns(HConstants.CATALOG_FAMILY,
265           MetaTableAccessor.getStartCodeColumn(1));
266         metaTable.delete(deleteOneReplicaLocation);
267       }
268       metaTable.close();
269       // even if the meta table is partly updated, when we re-enable the table, we should
270       // get back the desired number of replicas for the regions
271       ADMIN.enableTable(table);
272       assert(ADMIN.isTableEnabled(table));
273       List<HRegionInfo> regions = TEST_UTIL.getMiniHBaseCluster().getMaster()
274           .getAssignmentManager().getRegionStates().getRegionsOfTable(table);
275       assert(regions.size() == numRegions * numReplica);
276     } finally {
277       ADMIN.disableTable(table);
278       ADMIN.deleteTable(table);
279     }
280   }
281 
282   private String printRegions(List<HRegionInfo> regions) {
283     StringBuffer strBuf = new StringBuffer();
284     for (HRegionInfo r : regions) {
285       strBuf.append(" ____ " + r.toString());
286     }
287     return strBuf.toString();
288   }
289 
290   private void validateNumberOfRowsInMeta(final TableName table, int numRegions,
291       Connection connection) throws IOException {
292     assert(ADMIN.tableExists(table));
293     final AtomicInteger count = new AtomicInteger();
294     Visitor visitor = new Visitor() {
295       @Override
296       public boolean visit(Result r) throws IOException {
297         if (HRegionInfo.getHRegionInfo(r).getTable().equals(table)) count.incrementAndGet();
298         return true;
299       }
300     };
301     MetaTableAccessor.fullScan(connection, visitor);
302     assert(count.get() == numRegions);
303   }
304 
305   private void validateFromSnapshotFromMeta(HBaseTestingUtility util, TableName table,
306       int numRegions, int numReplica, Connection connection) throws IOException {
307     SnapshotOfRegionAssignmentFromMeta snapshot = new SnapshotOfRegionAssignmentFromMeta(
308       connection);
309     snapshot.initialize();
310     Map<HRegionInfo, ServerName> regionToServerMap = snapshot.getRegionToRegionServerMap();
311     assert(regionToServerMap.size() == numRegions * numReplica + 1); //'1' for the namespace
312     Map<ServerName, List<HRegionInfo>> serverToRegionMap = snapshot.getRegionServerToRegionMap();
313     for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverToRegionMap.entrySet()) {
314       if (entry.getKey().equals(util.getHBaseCluster().getMaster().getServerName())) {
315         continue;
316       }
317       List<HRegionInfo> regions = entry.getValue();
318       Set<byte[]> setOfStartKeys = new HashSet<byte[]>();
319       for (HRegionInfo region : regions) {
320         byte[] startKey = region.getStartKey();
321         if (region.getTable().equals(table)) {
322           setOfStartKeys.add(startKey); //ignore other tables
323           LOG.info("--STARTKEY " + new String(startKey)+"--");
324         }
325       }
326       // the number of startkeys will be equal to the number of regions hosted in each server
327       // (each server will be hosting one replica of a region)
328       assertEquals(numRegions, setOfStartKeys.size());
329     }
330   }
331 
332   private void validateSingleRegionServerAssignment(Connection connection, int numRegions,
333       int numReplica) throws IOException {
334     SnapshotOfRegionAssignmentFromMeta snapshot = new SnapshotOfRegionAssignmentFromMeta(
335       connection);
336     snapshot.initialize();
337     Map<HRegionInfo, ServerName>  regionToServerMap = snapshot.getRegionToRegionServerMap();
338     assertEquals(regionToServerMap.size(), numRegions * numReplica + 1); //'1' for the namespace
339     Map<ServerName, List<HRegionInfo>> serverToRegionMap = snapshot.getRegionServerToRegionMap();
340     assertEquals(serverToRegionMap.keySet().size(), 1); // master by default not used
341     for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverToRegionMap.entrySet()) {
342       if (entry.getKey().equals(TEST_UTIL.getHBaseCluster().getMaster().getServerName())) {
343         continue;
344       }
345       assertEquals(entry.getValue().size(), numRegions * numReplica + 1); //'1' for the namespace
346     }
347   }
348 }