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