View Javadoc

1   /**
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
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
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.
17   */
18  package org.apache.hadoop.hbase;
19  
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.io.IOException;
27  import java.util.Map;
28  import java.util.NavigableMap;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.HBaseTestCase.FlushCache;
34  import org.apache.hadoop.hbase.HBaseTestCase.HTableIncommon;
35  import org.apache.hadoop.hbase.HBaseTestCase.Incommon;
36  import org.apache.hadoop.hbase.client.Get;
37  import org.apache.hadoop.hbase.client.HBaseAdmin;
38  import org.apache.hadoop.hbase.client.HTable;
39  import org.apache.hadoop.hbase.client.Put;
40  import org.apache.hadoop.hbase.client.Result;
41  import org.apache.hadoop.hbase.client.ResultScanner;
42  import org.apache.hadoop.hbase.client.Scan;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.junit.After;
45  import org.junit.AfterClass;
46  import org.junit.Before;
47  import org.junit.BeforeClass;
48  import org.junit.Test;
49  import org.junit.experimental.categories.Category;
50  
51  /**
52   * Port of old TestScanMultipleVersions, TestTimestamp and TestGetRowVersions
53   * from old testing framework to {@link HBaseTestingUtility}.
54   */
55  @Category(MediumTests.class)
56  public class TestMultiVersions {
57    private static final Log LOG = LogFactory.getLog(TestMultiVersions.class);
58    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
59    private HBaseAdmin admin;
60    
61    private static final int NUM_SLAVES = 3;
62  
63    @BeforeClass
64    public static void setUpBeforeClass() throws Exception {
65      UTIL.startMiniCluster(NUM_SLAVES);
66    }
67  
68    @AfterClass
69    public static void tearDownAfterClass() throws Exception {
70      UTIL.shutdownMiniCluster();
71    }
72  
73    @Before
74    public void before()
75    throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
76      this.admin = new HBaseAdmin(UTIL.getConfiguration());
77    }
78  
79    @After
80    public void after() throws IOException {
81      this.admin.close();
82    }
83  
84    /**
85    * Tests user specifiable time stamps putting, getting and scanning.  Also
86     * tests same in presence of deletes.  Test cores are written so can be
87     * run against an HRegion and against an HTable: i.e. both local and remote.
88     * 
89     * <p>Port of old TestTimestamp test to here so can better utilize the spun
90     * up cluster running more than a single test per spin up.  Keep old tests'
91     * crazyness.
92     */
93    @Test
94    public void testTimestamps() throws Exception {
95      HTableDescriptor desc = new HTableDescriptor(TableName.valueOf("testTimestamps"));
96      HColumnDescriptor hcd = new HColumnDescriptor(TimestampTestBase.FAMILY_NAME);
97      hcd.setMaxVersions(3);
98      desc.addFamily(hcd);
99      this.admin.createTable(desc);
100     HTable table = new HTable(UTIL.getConfiguration(), desc.getTableName());
101     // TODO: Remove these deprecated classes or pull them in here if this is
102     // only test using them.
103     Incommon incommon = new HTableIncommon(table);
104     TimestampTestBase.doTestDelete(incommon, new FlushCache() {
105       public void flushcache() throws IOException {
106         UTIL.getHBaseCluster().flushcache();
107       }
108      });
109 
110     // Perhaps drop and readd the table between tests so the former does
111     // not pollute this latter?  Or put into separate tests.
112     TimestampTestBase.doTestTimestampScanning(incommon, new FlushCache() {
113       public void flushcache() throws IOException {
114         UTIL.getMiniHBaseCluster().flushcache();
115       }
116     });
117 
118     table.close();
119   }
120 
121   /**
122    * Verifies versions across a cluster restart.
123    * Port of old TestGetRowVersions test to here so can better utilize the spun
124    * up cluster running more than a single test per spin up.  Keep old tests'
125    * crazyness.
126    */
127   @Test
128   public void testGetRowVersions() throws Exception {
129     final String tableName = "testGetRowVersions";
130     final byte [] contents = Bytes.toBytes("contents");
131     final byte [] row = Bytes.toBytes("row");
132     final byte [] value1 = Bytes.toBytes("value1");
133     final byte [] value2 = Bytes.toBytes("value2");
134     final long timestamp1 = 100L;
135     final long timestamp2 = 200L;
136     final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
137     HColumnDescriptor hcd = new HColumnDescriptor(contents);
138     hcd.setMaxVersions(3);
139     desc.addFamily(hcd);
140     this.admin.createTable(desc);
141     Put put = new Put(row, timestamp1);
142     put.add(contents, contents, value1);
143     HTable table = new HTable(UTIL.getConfiguration(), tableName);
144     table.put(put);
145     // Shut down and restart the HBase cluster
146     table.close();
147     UTIL.shutdownMiniHBaseCluster();
148     LOG.debug("HBase cluster shut down -- restarting");
149     UTIL.startMiniHBaseCluster(1, NUM_SLAVES);
150     // Make a new connection.  Use new Configuration instance because old one
151     // is tied to an HConnection that has since gone stale.
152     table = new HTable(new Configuration(UTIL.getConfiguration()), tableName);
153     // Overwrite previous value
154     put = new Put(row, timestamp2);
155     put.add(contents, contents, value2);
156     table.put(put);
157     // Now verify that getRow(row, column, latest) works
158     Get get = new Get(row);
159     // Should get one version by default
160     Result r = table.get(get);
161     assertNotNull(r);
162     assertFalse(r.isEmpty());
163     assertTrue(r.size() == 1);
164     byte [] value = r.getValue(contents, contents);
165     assertTrue(value.length != 0);
166     assertTrue(Bytes.equals(value, value2));
167     // Now check getRow with multiple versions
168     get = new Get(row);
169     get.setMaxVersions();
170     r = table.get(get);
171     assertTrue(r.size() == 2);
172     value = r.getValue(contents, contents);
173     assertTrue(value.length != 0);
174     assertTrue(Bytes.equals(value, value2));
175     NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map =
176       r.getMap();
177     NavigableMap<byte[], NavigableMap<Long, byte[]>> familyMap =
178       map.get(contents);
179     NavigableMap<Long, byte[]> versionMap = familyMap.get(contents);
180     assertTrue(versionMap.size() == 2);
181     assertTrue(Bytes.equals(value1, versionMap.get(timestamp1)));
182     assertTrue(Bytes.equals(value2, versionMap.get(timestamp2)));
183     table.close();
184   }
185 
186   /**
187    * Port of old TestScanMultipleVersions test here so can better utilize the
188    * spun up cluster running more than just a single test.  Keep old tests
189    * crazyness.
190    * 
191    * <p>Tests five cases of scans and timestamps.
192    * @throws Exception
193    */
194   @Test
195   public void testScanMultipleVersions() throws Exception {
196     final byte [] tableName = Bytes.toBytes("testScanMultipleVersions");
197     final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
198     desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
199     final byte [][] rows = new byte[][] {
200       Bytes.toBytes("row_0200"),
201       Bytes.toBytes("row_0800")
202     };
203     final byte [][] splitRows = new byte[][] {Bytes.toBytes("row_0500")};
204     final long [] timestamp = new long[] {100L, 1000L};
205     this.admin.createTable(desc, splitRows);
206     HTable table = new HTable(UTIL.getConfiguration(), tableName);
207     // Assert we got the region layout wanted.
208     NavigableMap<HRegionInfo, ServerName> locations = table.getRegionLocations();
209     assertEquals(2, locations.size());
210     int index = 0;
211     for (Map.Entry<HRegionInfo, ServerName> e: locations.entrySet()) {
212       HRegionInfo hri = e.getKey();
213       if (index == 0) {
214         assertTrue(Bytes.equals(HConstants.EMPTY_START_ROW, hri.getStartKey()));
215         assertTrue(Bytes.equals(hri.getEndKey(), splitRows[0]));
216       } else if (index == 1) {
217         assertTrue(Bytes.equals(splitRows[0], hri.getStartKey()));
218         assertTrue(Bytes.equals(hri.getEndKey(), HConstants.EMPTY_END_ROW));
219       }
220       index++;
221     }
222     // Insert data
223     for (int i = 0; i < locations.size(); i++) {
224       for (int j = 0; j < timestamp.length; j++) {
225         Put put = new Put(rows[i], timestamp[j]);
226         put.add(HConstants.CATALOG_FAMILY, null, timestamp[j],
227             Bytes.toBytes(timestamp[j]));
228         table.put(put);
229       }
230     }
231     // There are 5 cases we have to test. Each is described below.
232     for (int i = 0; i < rows.length; i++) {
233       for (int j = 0; j < timestamp.length; j++) {
234         Get get = new Get(rows[i]);
235         get.addFamily(HConstants.CATALOG_FAMILY);
236         get.setTimeStamp(timestamp[j]);
237         Result result = table.get(get);
238         int cellCount = 0;
239         for(@SuppressWarnings("unused")Cell kv : result.listCells()) {
240           cellCount++;
241         }
242         assertTrue(cellCount == 1);
243       }
244       table.close();
245     }
246 
247     // Case 1: scan with LATEST_TIMESTAMP. Should get two rows
248     int count = 0;
249     Scan scan = new Scan();
250     scan.addFamily(HConstants.CATALOG_FAMILY);
251     ResultScanner s = table.getScanner(scan);
252     try {
253       for (Result rr = null; (rr = s.next()) != null;) {
254         System.out.println(rr.toString());
255         count += 1;
256       }
257       assertEquals("Number of rows should be 2", 2, count);
258     } finally {
259       s.close();
260     }
261 
262     // Case 2: Scan with a timestamp greater than most recent timestamp
263     // (in this case > 1000 and < LATEST_TIMESTAMP. Should get 2 rows.
264 
265     count = 0;
266     scan = new Scan();
267     scan.setTimeRange(1000L, Long.MAX_VALUE);
268     scan.addFamily(HConstants.CATALOG_FAMILY);
269 
270     s = table.getScanner(scan);
271     try {
272       while (s.next() != null) {
273         count += 1;
274       }
275       assertEquals("Number of rows should be 2", 2, count);
276     } finally {
277       s.close();
278     }
279 
280     // Case 3: scan with timestamp equal to most recent timestamp
281     // (in this case == 1000. Should get 2 rows.
282 
283     count = 0;
284     scan = new Scan();
285     scan.setTimeStamp(1000L);
286     scan.addFamily(HConstants.CATALOG_FAMILY);
287 
288     s = table.getScanner(scan);
289     try {
290       while (s.next() != null) {
291         count += 1;
292       }
293       assertEquals("Number of rows should be 2", 2, count);
294     } finally {
295       s.close();
296     }
297 
298     // Case 4: scan with timestamp greater than first timestamp but less than
299     // second timestamp (100 < timestamp < 1000). Should get 2 rows.
300 
301     count = 0;
302     scan = new Scan();
303     scan.setTimeRange(100L, 1000L);
304     scan.addFamily(HConstants.CATALOG_FAMILY);
305 
306     s = table.getScanner(scan);
307     try {
308       while (s.next() != null) {
309         count += 1;
310       }
311       assertEquals("Number of rows should be 2", 2, count);
312     } finally {
313       s.close();
314     }
315 
316     // Case 5: scan with timestamp equal to first timestamp (100)
317     // Should get 2 rows.
318 
319     count = 0;
320     scan = new Scan();
321     scan.setTimeStamp(100L);
322     scan.addFamily(HConstants.CATALOG_FAMILY);
323 
324     s = table.getScanner(scan);
325     try {
326       while (s.next() != null) {
327         count += 1;
328       }
329       assertEquals("Number of rows should be 2", 2, count);
330     } finally {
331       s.close();
332     }
333   }
334 
335 }
336