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.client; 019 020import static org.junit.Assert.*; 021 022import java.io.IOException; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.List; 026import org.apache.hadoop.hbase.*; 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.testclassification.ClientTests; 029import org.apache.hadoop.hbase.testclassification.LargeTests; 030import org.apache.hadoop.hbase.util.Bytes; 031import org.junit.After; 032import org.junit.AfterClass; 033import org.junit.Before; 034import org.junit.BeforeClass; 035import org.junit.ClassRule; 036import org.junit.Rule; 037import org.junit.Test; 038import org.junit.experimental.categories.Category; 039import org.junit.rules.TestName; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * Run tests related to {@link org.apache.hadoop.hbase.filter.TimestampsFilter} using HBase client APIs. 045 * Sets up the HBase mini cluster once at start. Each creates a table 046 * named for the method and does its stuff against that. 047 */ 048@Category({LargeTests.class, ClientTests.class}) 049public class TestMultipleTimestamps { 050 051 @ClassRule 052 public static final HBaseClassTestRule CLASS_RULE = 053 HBaseClassTestRule.forClass(TestMultipleTimestamps.class); 054 055 private static final Logger LOG = LoggerFactory.getLogger(TestMultipleTimestamps.class); 056 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 057 058 @Rule 059 public TestName name = new TestName(); 060 061 /** 062 * @throws java.lang.Exception 063 */ 064 @BeforeClass 065 public static void setUpBeforeClass() throws Exception { 066 TEST_UTIL.startMiniCluster(); 067 } 068 069 /** 070 * @throws java.lang.Exception 071 */ 072 @AfterClass 073 public static void tearDownAfterClass() throws Exception { 074 TEST_UTIL.shutdownMiniCluster(); 075 } 076 077 /** 078 * @throws java.lang.Exception 079 */ 080 @Before 081 public void setUp() throws Exception { 082 // Nothing to do. 083 } 084 085 /** 086 * @throws java.lang.Exception 087 */ 088 @After 089 public void tearDown() throws Exception { 090 // Nothing to do. 091 } 092 093 @Test 094 public void testReseeksWithOneColumnMiltipleTimestamp() throws IOException { 095 final TableName tableName = TableName.valueOf(name.getMethodName()); 096 byte [] FAMILY = Bytes.toBytes("event_log"); 097 byte [][] FAMILIES = new byte[][] { FAMILY }; 098 099 // create table; set versions to max... 100 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 101 102 Integer[] putRows = new Integer[] {1, 3, 5, 7}; 103 Integer[] putColumns = new Integer[] { 1, 3, 5}; 104 Long[] putTimestamps = new Long[] {1L, 2L, 3L, 4L, 5L}; 105 106 Integer[] scanRows = new Integer[] {3, 5}; 107 Integer[] scanColumns = new Integer[] {3}; 108 Long[] scanTimestamps = new Long[] {3L, 4L}; 109 int scanMaxVersions = 2; 110 111 put(ht, FAMILY, putRows, putColumns, putTimestamps); 112 113 TEST_UTIL.flush(tableName); 114 115 ResultScanner scanner = scan(ht, FAMILY, scanRows, scanColumns, 116 scanTimestamps, scanMaxVersions); 117 118 Cell [] kvs; 119 120 kvs = scanner.next().rawCells(); 121 assertEquals(2, kvs.length); 122 checkOneCell(kvs[0], FAMILY, 3, 3, 4); 123 checkOneCell(kvs[1], FAMILY, 3, 3, 3); 124 kvs = scanner.next().rawCells(); 125 assertEquals(2, kvs.length); 126 checkOneCell(kvs[0], FAMILY, 5, 3, 4); 127 checkOneCell(kvs[1], FAMILY, 5, 3, 3); 128 129 ht.close(); 130 } 131 132 @Test 133 public void testReseeksWithMultipleColumnOneTimestamp() throws IOException { 134 LOG.info(name.getMethodName()); 135 final TableName tableName = TableName.valueOf(name.getMethodName()); 136 byte [] FAMILY = Bytes.toBytes("event_log"); 137 byte [][] FAMILIES = new byte[][] { FAMILY }; 138 139 // create table; set versions to max... 140 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 141 142 Integer[] putRows = new Integer[] {1, 3, 5, 7}; 143 Integer[] putColumns = new Integer[] { 1, 3, 5}; 144 Long[] putTimestamps = new Long[] {1L, 2L, 3L, 4L, 5L}; 145 146 Integer[] scanRows = new Integer[] {3, 5}; 147 Integer[] scanColumns = new Integer[] {3,4}; 148 Long[] scanTimestamps = new Long[] {3L}; 149 int scanMaxVersions = 2; 150 151 put(ht, FAMILY, putRows, putColumns, putTimestamps); 152 153 TEST_UTIL.flush(tableName); 154 155 ResultScanner scanner = scan(ht, FAMILY, scanRows, scanColumns, 156 scanTimestamps, scanMaxVersions); 157 158 Cell[] kvs; 159 160 kvs = scanner.next().rawCells(); 161 assertEquals(1, kvs.length); 162 checkOneCell(kvs[0], FAMILY, 3, 3, 3); 163 kvs = scanner.next().rawCells(); 164 assertEquals(1, kvs.length); 165 checkOneCell(kvs[0], FAMILY, 5, 3, 3); 166 167 ht.close(); 168 } 169 170 @Test 171 public void testReseeksWithMultipleColumnMultipleTimestamp() throws 172 IOException { 173 LOG.info(name.getMethodName()); 174 175 final TableName tableName = TableName.valueOf(name.getMethodName()); 176 byte [] FAMILY = Bytes.toBytes("event_log"); 177 byte [][] FAMILIES = new byte[][] { FAMILY }; 178 179 // create table; set versions to max... 180 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 181 182 Integer[] putRows = new Integer[] {1, 3, 5, 7}; 183 Integer[] putColumns = new Integer[] { 1, 3, 5}; 184 Long[] putTimestamps = new Long[] {1L, 2L, 3L, 4L, 5L}; 185 186 Integer[] scanRows = new Integer[] {5, 7}; 187 Integer[] scanColumns = new Integer[] {3, 4, 5}; 188 Long[] scanTimestamps = new Long[] { 2L, 3L}; 189 int scanMaxVersions = 2; 190 191 put(ht, FAMILY, putRows, putColumns, putTimestamps); 192 193 TEST_UTIL.flush(tableName); 194 Scan scan = new Scan(); 195 scan.setMaxVersions(10); 196 ResultScanner scanner = ht.getScanner(scan); 197 while (true) { 198 Result r = scanner.next(); 199 if (r == null) break; 200 LOG.info("r=" + r); 201 } 202 scanner = scan(ht, FAMILY, scanRows, scanColumns, scanTimestamps, scanMaxVersions); 203 204 Cell[] kvs; 205 206 // This looks like wrong answer. Should be 2. Even then we are returning wrong result, 207 // timestamps that are 3 whereas should be 2 since min is inclusive. 208 kvs = scanner.next().rawCells(); 209 assertEquals(4, kvs.length); 210 checkOneCell(kvs[0], FAMILY, 5, 3, 3); 211 checkOneCell(kvs[1], FAMILY, 5, 3, 2); 212 checkOneCell(kvs[2], FAMILY, 5, 5, 3); 213 checkOneCell(kvs[3], FAMILY, 5, 5, 2); 214 kvs = scanner.next().rawCells(); 215 assertEquals(4, kvs.length); 216 checkOneCell(kvs[0], FAMILY, 7, 3, 3); 217 checkOneCell(kvs[1], FAMILY, 7, 3, 2); 218 checkOneCell(kvs[2], FAMILY, 7, 5, 3); 219 checkOneCell(kvs[3], FAMILY, 7, 5, 2); 220 221 ht.close(); 222 } 223 224 @Test 225 public void testReseeksWithMultipleFiles() throws IOException { 226 LOG.info(name.getMethodName()); 227 final TableName tableName = TableName.valueOf(name.getMethodName()); 228 byte [] FAMILY = Bytes.toBytes("event_log"); 229 byte [][] FAMILIES = new byte[][] { FAMILY }; 230 231 // create table; set versions to max... 232 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 233 234 Integer[] putRows1 = new Integer[] {1, 2, 3}; 235 Integer[] putColumns1 = new Integer[] { 2, 5, 6}; 236 Long[] putTimestamps1 = new Long[] {1L, 2L, 5L}; 237 238 Integer[] putRows2 = new Integer[] {6, 7}; 239 Integer[] putColumns2 = new Integer[] {3, 6}; 240 Long[] putTimestamps2 = new Long[] {4L, 5L}; 241 242 Integer[] putRows3 = new Integer[] {2, 3, 5}; 243 Integer[] putColumns3 = new Integer[] {1, 2, 3}; 244 Long[] putTimestamps3 = new Long[] {4L,8L}; 245 246 247 Integer[] scanRows = new Integer[] {3, 5, 7}; 248 Integer[] scanColumns = new Integer[] {3, 4, 5}; 249 Long[] scanTimestamps = new Long[] { 2L, 4L}; 250 int scanMaxVersions = 5; 251 252 put(ht, FAMILY, putRows1, putColumns1, putTimestamps1); 253 TEST_UTIL.flush(tableName); 254 put(ht, FAMILY, putRows2, putColumns2, putTimestamps2); 255 TEST_UTIL.flush(tableName); 256 put(ht, FAMILY, putRows3, putColumns3, putTimestamps3); 257 258 ResultScanner scanner = scan(ht, FAMILY, scanRows, scanColumns, 259 scanTimestamps, scanMaxVersions); 260 261 Cell[] kvs; 262 263 kvs = scanner.next().rawCells(); 264 assertEquals(2, kvs.length); 265 checkOneCell(kvs[0], FAMILY, 3, 3, 4); 266 checkOneCell(kvs[1], FAMILY, 3, 5, 2); 267 268 kvs = scanner.next().rawCells(); 269 assertEquals(1, kvs.length); 270 checkOneCell(kvs[0], FAMILY, 5, 3, 4); 271 272 kvs = scanner.next().rawCells(); 273 assertEquals(1, kvs.length); 274 checkOneCell(kvs[0], FAMILY, 6, 3, 4); 275 276 kvs = scanner.next().rawCells(); 277 assertEquals(1, kvs.length); 278 checkOneCell(kvs[0], FAMILY, 7, 3, 4); 279 280 ht.close(); 281 } 282 283 @Test 284 public void testWithVersionDeletes() throws Exception { 285 286 // first test from memstore (without flushing). 287 testWithVersionDeletes(false); 288 289 // run same test against HFiles (by forcing a flush). 290 testWithVersionDeletes(true); 291 } 292 293 public void testWithVersionDeletes(boolean flushTables) throws IOException { 294 LOG.info(name.getMethodName() + "_"+ (flushTables ? "flush" : "noflush")); 295 final TableName tableName = TableName.valueOf(name.getMethodName() + "_" + (flushTables ? 296 "flush" : "noflush")); 297 byte [] FAMILY = Bytes.toBytes("event_log"); 298 byte [][] FAMILIES = new byte[][] { FAMILY }; 299 300 // create table; set versions to max... 301 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 302 303 // For row:0, col:0: insert versions 1 through 5. 304 putNVersions(ht, FAMILY, 0, 0, 1, 5); 305 306 if (flushTables) { 307 TEST_UTIL.flush(tableName); 308 } 309 310 // delete version 4. 311 deleteOneVersion(ht, FAMILY, 0, 0, 4); 312 313 // request a bunch of versions including the deleted version. We should 314 // only get back entries for the versions that exist. 315 Cell kvs[] = getNVersions(ht, FAMILY, 0, 0, 316 Arrays.asList(2L, 3L, 4L, 5L)); 317 assertEquals(3, kvs.length); 318 checkOneCell(kvs[0], FAMILY, 0, 0, 5); 319 checkOneCell(kvs[1], FAMILY, 0, 0, 3); 320 checkOneCell(kvs[2], FAMILY, 0, 0, 2); 321 322 ht.close(); 323 } 324 325 @Test 326 public void testWithMultipleVersionDeletes() throws IOException { 327 LOG.info(name.getMethodName()); 328 329 final TableName tableName = TableName.valueOf(name.getMethodName()); 330 byte [] FAMILY = Bytes.toBytes("event_log"); 331 byte [][] FAMILIES = new byte[][] { FAMILY }; 332 333 // create table; set versions to max... 334 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 335 336 // For row:0, col:0: insert versions 1 through 5. 337 putNVersions(ht, FAMILY, 0, 0, 1, 5); 338 339 TEST_UTIL.flush(tableName); 340 341 // delete all versions before 4. 342 deleteAllVersionsBefore(ht, FAMILY, 0, 0, 4); 343 344 // request a bunch of versions including the deleted version. We should 345 // only get back entries for the versions that exist. 346 Cell kvs[] = getNVersions(ht, FAMILY, 0, 0, Arrays.asList(2L, 3L)); 347 assertEquals(0, kvs.length); 348 349 ht.close(); 350 } 351 352 @Test 353 public void testWithColumnDeletes() throws IOException { 354 final TableName tableName = TableName.valueOf(name.getMethodName()); 355 byte [] FAMILY = Bytes.toBytes("event_log"); 356 byte [][] FAMILIES = new byte[][] { FAMILY }; 357 358 // create table; set versions to max... 359 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 360 361 // For row:0, col:0: insert versions 1 through 5. 362 putNVersions(ht, FAMILY, 0, 0, 1, 5); 363 364 TEST_UTIL.flush(tableName); 365 366 // delete all versions before 4. 367 deleteColumn(ht, FAMILY, 0, 0); 368 369 // request a bunch of versions including the deleted version. We should 370 // only get back entries for the versions that exist. 371 Cell kvs[] = getNVersions(ht, FAMILY, 0, 0, Arrays.asList(2L, 3L)); 372 assertEquals(0, kvs.length); 373 374 ht.close(); 375 } 376 377 @Test 378 public void testWithFamilyDeletes() throws IOException { 379 final TableName tableName = TableName.valueOf(name.getMethodName()); 380 byte [] FAMILY = Bytes.toBytes("event_log"); 381 byte [][] FAMILIES = new byte[][] { FAMILY }; 382 383 // create table; set versions to max... 384 Table ht = TEST_UTIL.createTable(tableName, FAMILIES, Integer.MAX_VALUE); 385 386 // For row:0, col:0: insert versions 1 through 5. 387 putNVersions(ht, FAMILY, 0, 0, 1, 5); 388 389 TEST_UTIL.flush(tableName); 390 391 // delete all versions before 4. 392 deleteFamily(ht, FAMILY, 0); 393 394 // request a bunch of versions including the deleted version. We should 395 // only get back entries for the versions that exist. 396 Cell kvs[] = getNVersions(ht, FAMILY, 0, 0, Arrays.asList(2L, 3L)); 397 assertEquals(0, kvs.length); 398 399 ht.close(); 400 } 401 402 /** 403 * Assert that the passed in KeyValue has expected contents for the 404 * specified row, column & timestamp. 405 */ 406 private void checkOneCell(Cell kv, byte[] cf, 407 int rowIdx, int colIdx, long ts) { 408 409 String ctx = "rowIdx=" + rowIdx + "; colIdx=" + colIdx + "; ts=" + ts; 410 411 assertEquals("Row mismatch which checking: " + ctx, 412 "row:"+ rowIdx, Bytes.toString(CellUtil.cloneRow(kv))); 413 414 assertEquals("ColumnFamily mismatch while checking: " + ctx, 415 Bytes.toString(cf), Bytes.toString(CellUtil.cloneFamily(kv))); 416 417 assertEquals("Column qualifier mismatch while checking: " + ctx, 418 "column:" + colIdx, 419 Bytes.toString(CellUtil.cloneQualifier(kv))); 420 421 assertEquals("Timestamp mismatch while checking: " + ctx, 422 ts, kv.getTimestamp()); 423 424 assertEquals("Value mismatch while checking: " + ctx, 425 "value-version-" + ts, Bytes.toString(CellUtil.cloneValue(kv))); 426 } 427 428 /** 429 * Uses the TimestampFilter on a Get to request a specified list of 430 * versions for the row/column specified by rowIdx & colIdx. 431 * 432 */ 433 private Cell[] getNVersions(Table ht, byte[] cf, int rowIdx, 434 int colIdx, List<Long> versions) 435 throws IOException { 436 byte row[] = Bytes.toBytes("row:" + rowIdx); 437 byte column[] = Bytes.toBytes("column:" + colIdx); 438 Get get = new Get(row); 439 get.addColumn(cf, column); 440 get.setMaxVersions(); 441 get.setTimeRange(Collections.min(versions), Collections.max(versions)+1); 442 Result result = ht.get(get); 443 444 return result.rawCells(); 445 } 446 447 private ResultScanner scan(Table ht, byte[] cf, 448 Integer[] rowIndexes, Integer[] columnIndexes, 449 Long[] versions, int maxVersions) 450 throws IOException { 451 Arrays.asList(rowIndexes); 452 byte startRow[] = Bytes.toBytes("row:" + 453 Collections.min( Arrays.asList(rowIndexes))); 454 byte endRow[] = Bytes.toBytes("row:" + 455 Collections.max( Arrays.asList(rowIndexes))+1); 456 Scan scan = new Scan(startRow, endRow); 457 for (Integer colIdx: columnIndexes) { 458 byte column[] = Bytes.toBytes("column:" + colIdx); 459 scan.addColumn(cf, column); 460 } 461 scan.setMaxVersions(maxVersions); 462 scan.setTimeRange(Collections.min(Arrays.asList(versions)), 463 Collections.max(Arrays.asList(versions))+1); 464 ResultScanner scanner = ht.getScanner(scan); 465 return scanner; 466 } 467 468 private void put(Table ht, byte[] cf, Integer[] rowIndexes, 469 Integer[] columnIndexes, Long[] versions) 470 throws IOException { 471 for (int rowIdx: rowIndexes) { 472 byte row[] = Bytes.toBytes("row:" + rowIdx); 473 Put put = new Put(row); 474 put.setDurability(Durability.SKIP_WAL); 475 for(int colIdx: columnIndexes) { 476 byte column[] = Bytes.toBytes("column:" + colIdx); 477 for (long version: versions) { 478 put.addColumn(cf, column, version, Bytes.toBytes("value-version-" + 479 version)); 480 } 481 } 482 ht.put(put); 483 } 484 } 485 486 /** 487 * Insert in specific row/column versions with timestamps 488 * versionStart..versionEnd. 489 */ 490 private void putNVersions(Table ht, byte[] cf, int rowIdx, int colIdx, 491 long versionStart, long versionEnd) 492 throws IOException { 493 byte row[] = Bytes.toBytes("row:" + rowIdx); 494 byte column[] = Bytes.toBytes("column:" + colIdx); 495 Put put = new Put(row); 496 put.setDurability(Durability.SKIP_WAL); 497 498 for (long idx = versionStart; idx <= versionEnd; idx++) { 499 put.addColumn(cf, column, idx, Bytes.toBytes("value-version-" + idx)); 500 } 501 502 ht.put(put); 503 } 504 505 /** 506 * For row/column specified by rowIdx/colIdx, delete the cell 507 * corresponding to the specified version. 508 */ 509 private void deleteOneVersion(Table ht, byte[] cf, int rowIdx, 510 int colIdx, long version) 511 throws IOException { 512 byte row[] = Bytes.toBytes("row:" + rowIdx); 513 byte column[] = Bytes.toBytes("column:" + colIdx); 514 Delete del = new Delete(row); 515 del.addColumn(cf, column, version); 516 ht.delete(del); 517 } 518 519 /** 520 * For row/column specified by rowIdx/colIdx, delete all cells 521 * preceeding the specified version. 522 */ 523 private void deleteAllVersionsBefore(Table ht, byte[] cf, int rowIdx, 524 int colIdx, long version) 525 throws IOException { 526 byte row[] = Bytes.toBytes("row:" + rowIdx); 527 byte column[] = Bytes.toBytes("column:" + colIdx); 528 Delete del = new Delete(row); 529 del.addColumns(cf, column, version); 530 ht.delete(del); 531 } 532 533 private void deleteColumn(Table ht, byte[] cf, int rowIdx, int colIdx) throws IOException { 534 byte row[] = Bytes.toBytes("row:" + rowIdx); 535 byte column[] = Bytes.toBytes("column:" + colIdx); 536 Delete del = new Delete(row); 537 del.addColumns(cf, column); 538 ht.delete(del); 539 } 540 541 private void deleteFamily(Table ht, byte[] cf, int rowIdx) throws IOException { 542 byte row[] = Bytes.toBytes("row:" + rowIdx); 543 Delete del = new Delete(row); 544 del.addFamily(cf); 545 ht.delete(del); 546 } 547 548} 549 550