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; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertTrue; 024 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.NavigableMap; 029import org.apache.hadoop.hbase.TimestampTestBase.FlushCache; 030import org.apache.hadoop.hbase.client.Admin; 031import org.apache.hadoop.hbase.client.Get; 032import org.apache.hadoop.hbase.client.Put; 033import org.apache.hadoop.hbase.client.Result; 034import org.apache.hadoop.hbase.client.ResultScanner; 035import org.apache.hadoop.hbase.client.Scan; 036import org.apache.hadoop.hbase.client.Table; 037import org.apache.hadoop.hbase.testclassification.MediumTests; 038import org.apache.hadoop.hbase.testclassification.MiscTests; 039import org.apache.hadoop.hbase.util.Bytes; 040import org.apache.hadoop.hbase.util.Pair; 041import org.junit.AfterClass; 042import org.junit.Before; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Rule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.rules.TestName; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052/** 053 * Port of old TestScanMultipleVersions, TestTimestamp and TestGetRowVersions 054 * from old testing framework to {@link HBaseTestingUtility}. 055 */ 056@Category({MiscTests.class, MediumTests.class}) 057public class TestMultiVersions { 058 059 @ClassRule 060 public static final HBaseClassTestRule CLASS_RULE = 061 HBaseClassTestRule.forClass(TestMultiVersions.class); 062 063 private static final Logger LOG = LoggerFactory.getLogger(TestMultiVersions.class); 064 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 065 private Admin admin; 066 067 private static final int NUM_SLAVES = 3; 068 069 @Rule 070 public TestName name = new TestName(); 071 072 @BeforeClass 073 public static void setUpBeforeClass() throws Exception { 074 UTIL.startMiniCluster(NUM_SLAVES); 075 } 076 077 @AfterClass 078 public static void tearDownAfterClass() throws Exception { 079 UTIL.shutdownMiniCluster(); 080 } 081 082 @Before 083 public void before() 084 throws MasterNotRunningException, ZooKeeperConnectionException, IOException { 085 this.admin = UTIL.getAdmin(); 086 } 087 088 /** 089 * Tests user specifiable time stamps putting, getting and scanning. Also 090 * tests same in presence of deletes. Test cores are written so can be 091 * run against an HRegion and against an HTable: i.e. both local and remote. 092 * 093 * <p>Port of old TestTimestamp test to here so can better utilize the spun 094 * up cluster running more than a single test per spin up. Keep old tests' 095 * crazyness. 096 */ 097 @Test 098 public void testTimestamps() throws Exception { 099 HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(name.getMethodName())); 100 HColumnDescriptor hcd = new HColumnDescriptor(TimestampTestBase.FAMILY_NAME); 101 hcd.setMaxVersions(3); 102 desc.addFamily(hcd); 103 this.admin.createTable(desc); 104 Table table = UTIL.getConnection().getTable(desc.getTableName()); 105 // TODO: Remove these deprecated classes or pull them in here if this is 106 // only test using them. 107 TimestampTestBase.doTestDelete(table, new FlushCache() { 108 @Override 109 public void flushcache() throws IOException { 110 UTIL.getHBaseCluster().flushcache(); 111 } 112 }); 113 114 // Perhaps drop and readd the table between tests so the former does 115 // not pollute this latter? Or put into separate tests. 116 TimestampTestBase.doTestTimestampScanning(table, new FlushCache() { 117 @Override 118 public void flushcache() throws IOException { 119 UTIL.getMiniHBaseCluster().flushcache(); 120 } 121 }); 122 123 table.close(); 124 } 125 126 /** 127 * Verifies versions across a cluster restart. 128 * Port of old TestGetRowVersions test to here so can better utilize the spun 129 * up cluster running more than a single test per spin up. Keep old tests' 130 * crazyness. 131 */ 132 @Test 133 public void testGetRowVersions() throws Exception { 134 final byte [] contents = Bytes.toBytes("contents"); 135 final byte [] row = Bytes.toBytes("row"); 136 final byte [] value1 = Bytes.toBytes("value1"); 137 final byte [] value2 = Bytes.toBytes("value2"); 138 final long timestamp1 = 100L; 139 final long timestamp2 = 200L; 140 final HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(name.getMethodName())); 141 HColumnDescriptor hcd = new HColumnDescriptor(contents); 142 hcd.setMaxVersions(3); 143 desc.addFamily(hcd); 144 this.admin.createTable(desc); 145 Put put = new Put(row, timestamp1); 146 put.addColumn(contents, contents, value1); 147 Table table = UTIL.getConnection().getTable(desc.getTableName()); 148 table.put(put); 149 // Shut down and restart the HBase cluster 150 table.close(); 151 UTIL.shutdownMiniHBaseCluster(); 152 LOG.debug("HBase cluster shut down -- restarting"); 153 UTIL.startMiniHBaseCluster(1, NUM_SLAVES); 154 // Make a new connection. 155 table = UTIL.getConnection().getTable(desc.getTableName()); 156 // Overwrite previous value 157 put = new Put(row, timestamp2); 158 put.addColumn(contents, contents, value2); 159 table.put(put); 160 // Now verify that getRow(row, column, latest) works 161 Get get = new Get(row); 162 // Should get one version by default 163 Result r = table.get(get); 164 assertNotNull(r); 165 assertFalse(r.isEmpty()); 166 assertTrue(r.size() == 1); 167 byte [] value = r.getValue(contents, contents); 168 assertTrue(value.length != 0); 169 assertTrue(Bytes.equals(value, value2)); 170 // Now check getRow with multiple versions 171 get = new Get(row); 172 get.setMaxVersions(); 173 r = table.get(get); 174 assertTrue(r.size() == 2); 175 value = r.getValue(contents, contents); 176 assertTrue(value.length != 0); 177 assertTrue(Bytes.equals(value, value2)); 178 NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = 179 r.getMap(); 180 NavigableMap<byte[], NavigableMap<Long, byte[]>> familyMap = 181 map.get(contents); 182 NavigableMap<Long, byte[]> versionMap = familyMap.get(contents); 183 assertTrue(versionMap.size() == 2); 184 assertTrue(Bytes.equals(value1, versionMap.get(timestamp1))); 185 assertTrue(Bytes.equals(value2, versionMap.get(timestamp2))); 186 table.close(); 187 } 188 189 /** 190 * Port of old TestScanMultipleVersions test here so can better utilize the 191 * spun up cluster running more than just a single test. Keep old tests 192 * crazyness. 193 * 194 * <p>Tests five cases of scans and timestamps. 195 * @throws Exception 196 */ 197 @Test 198 public void testScanMultipleVersions() throws Exception { 199 final TableName tableName = TableName.valueOf(name.getMethodName()); 200 final HTableDescriptor desc = new HTableDescriptor(tableName); 201 desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); 202 final byte [][] rows = new byte[][] { 203 Bytes.toBytes("row_0200"), 204 Bytes.toBytes("row_0800") 205 }; 206 final byte [][] splitRows = new byte[][] {Bytes.toBytes("row_0500")}; 207 final long [] timestamp = new long[] {100L, 1000L}; 208 this.admin.createTable(desc, splitRows); 209 Table table = UTIL.getConnection().getTable(tableName); 210 // Assert we got the region layout wanted. 211 Pair<byte[][], byte[][]> keys = UTIL.getConnection() 212 .getRegionLocator(tableName).getStartEndKeys(); 213 assertEquals(2, keys.getFirst().length); 214 byte[][] startKeys = keys.getFirst(); 215 byte[][] endKeys = keys.getSecond(); 216 217 for (int i = 0; i < startKeys.length; i++) { 218 if (i == 0) { 219 assertTrue(Bytes.equals(HConstants.EMPTY_START_ROW, startKeys[i])); 220 assertTrue(Bytes.equals(endKeys[i], splitRows[0])); 221 } else if (i == 1) { 222 assertTrue(Bytes.equals(splitRows[0], startKeys[i])); 223 assertTrue(Bytes.equals(endKeys[i], HConstants.EMPTY_END_ROW)); 224 } 225 } 226 // Insert data 227 List<Put> puts = new ArrayList<>(); 228 for (int i = 0; i < startKeys.length; i++) { 229 for (int j = 0; j < timestamp.length; j++) { 230 Put put = new Put(rows[i], timestamp[j]); 231 put.addColumn(HConstants.CATALOG_FAMILY, null, timestamp[j], Bytes.toBytes(timestamp[j])); 232 puts.add(put); 233 } 234 } 235 table.put(puts); 236 // There are 5 cases we have to test. Each is described below. 237 for (int i = 0; i < rows.length; i++) { 238 for (int j = 0; j < timestamp.length; j++) { 239 Get get = new Get(rows[i]); 240 get.addFamily(HConstants.CATALOG_FAMILY); 241 get.setTimestamp(timestamp[j]); 242 Result result = table.get(get); 243 int cellCount = 0; 244 for(@SuppressWarnings("unused")Cell kv : result.listCells()) { 245 cellCount++; 246 } 247 assertTrue(cellCount == 1); 248 } 249 } 250 251 // Case 1: scan with LATEST_TIMESTAMP. Should get two rows 252 int count = 0; 253 Scan scan = new Scan(); 254 scan.addFamily(HConstants.CATALOG_FAMILY); 255 ResultScanner s = table.getScanner(scan); 256 try { 257 for (Result rr = null; (rr = s.next()) != null;) { 258 System.out.println(rr.toString()); 259 count += 1; 260 } 261 assertEquals("Number of rows should be 2", 2, count); 262 } finally { 263 s.close(); 264 } 265 266 // Case 2: Scan with a timestamp greater than most recent timestamp 267 // (in this case > 1000 and < LATEST_TIMESTAMP. Should get 2 rows. 268 269 count = 0; 270 scan = new Scan(); 271 scan.setTimeRange(1000L, Long.MAX_VALUE); 272 scan.addFamily(HConstants.CATALOG_FAMILY); 273 274 s = table.getScanner(scan); 275 try { 276 while (s.next() != null) { 277 count += 1; 278 } 279 assertEquals("Number of rows should be 2", 2, count); 280 } finally { 281 s.close(); 282 } 283 284 // Case 3: scan with timestamp equal to most recent timestamp 285 // (in this case == 1000. Should get 2 rows. 286 287 count = 0; 288 scan = new Scan(); 289 scan.setTimestamp(1000L); 290 scan.addFamily(HConstants.CATALOG_FAMILY); 291 292 s = table.getScanner(scan); 293 try { 294 while (s.next() != null) { 295 count += 1; 296 } 297 assertEquals("Number of rows should be 2", 2, count); 298 } finally { 299 s.close(); 300 } 301 302 // Case 4: scan with timestamp greater than first timestamp but less than 303 // second timestamp (100 < timestamp < 1000). Should get 2 rows. 304 305 count = 0; 306 scan = new Scan(); 307 scan.setTimeRange(100L, 1000L); 308 scan.addFamily(HConstants.CATALOG_FAMILY); 309 310 s = table.getScanner(scan); 311 try { 312 while (s.next() != null) { 313 count += 1; 314 } 315 assertEquals("Number of rows should be 2", 2, count); 316 } finally { 317 s.close(); 318 } 319 320 // Case 5: scan with timestamp equal to first timestamp (100) 321 // Should get 2 rows. 322 323 count = 0; 324 scan = new Scan(); 325 scan.setTimestamp(100L); 326 scan.addFamily(HConstants.CATALOG_FAMILY); 327 328 s = table.getScanner(scan); 329 try { 330 while (s.next() != null) { 331 count += 1; 332 } 333 assertEquals("Number of rows should be 2", 2, count); 334 } finally { 335 s.close(); 336 } 337 } 338 339} 340