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.regionserver; 019 020import static org.apache.hadoop.hbase.HBaseTestingUtility.COLUMNS; 021import static org.junit.Assert.assertArrayEquals; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.List; 029import org.apache.hadoop.hbase.Cell; 030import org.apache.hadoop.hbase.CellUtil; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HTableDescriptor; 035import org.apache.hadoop.hbase.KeepDeletedCells; 036import org.apache.hadoop.hbase.PrivateCellUtil; 037import org.apache.hadoop.hbase.client.Delete; 038import org.apache.hadoop.hbase.client.Get; 039import org.apache.hadoop.hbase.client.Put; 040import org.apache.hadoop.hbase.client.Result; 041import org.apache.hadoop.hbase.client.Scan; 042import org.apache.hadoop.hbase.testclassification.MediumTests; 043import org.apache.hadoop.hbase.testclassification.RegionServerTests; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; 047import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; 048import org.junit.After; 049import org.junit.Before; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055 056@Category({RegionServerTests.class, MediumTests.class}) 057public class TestKeepDeletes { 058 059 @ClassRule 060 public static final HBaseClassTestRule CLASS_RULE = 061 HBaseClassTestRule.forClass(TestKeepDeletes.class); 062 063 HBaseTestingUtility hbu = HBaseTestingUtility.createLocalHTU(); 064 private final byte[] T0 = Bytes.toBytes("0"); 065 private final byte[] T1 = Bytes.toBytes("1"); 066 private final byte[] T2 = Bytes.toBytes("2"); 067 private final byte[] T3 = Bytes.toBytes("3"); 068 private final byte[] T4 = Bytes.toBytes("4"); 069 private final byte[] T5 = Bytes.toBytes("5"); 070 private final byte[] T6 = Bytes.toBytes("6"); 071 072 private final byte[] c0 = COLUMNS[0]; 073 private final byte[] c1 = COLUMNS[1]; 074 075 @Rule public TestName name = new TestName(); 076 077 @Before 078 public void setUp() throws Exception { 079 /* HBASE-6832: [WINDOWS] Tests should use explicit timestamp for Puts, and not rely on 080 * implicit RS timing. 081 * Use an explicit timer (IncrementingEnvironmentEdge) so that the put, delete 082 * compact timestamps are tracked. Otherwise, forced major compaction will not purge 083 * Delete's having the same timestamp. see ScanQueryMatcher.match(): 084 * if (retainDeletesInOutput 085 * || (!isUserScan && (EnvironmentEdgeManager.currentTime() - timestamp) 086 * <= timeToPurgeDeletes) ... ) 087 * 088 */ 089 EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); 090 } 091 092 @After 093 public void tearDown() throws Exception { 094 EnvironmentEdgeManager.reset(); 095 } 096 097 /** 098 * Make sure that deleted rows are retained. 099 * Family delete markers are deleted. 100 * Column Delete markers are versioned 101 * Time range scan of deleted rows are possible 102 */ 103 @Test 104 public void testBasicScenario() throws Exception { 105 // keep 3 versions, rows do not expire 106 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 107 HConstants.FOREVER, KeepDeletedCells.TRUE); 108 HRegion region = hbu.createLocalHRegion(htd, null, null); 109 110 long ts = EnvironmentEdgeManager.currentTime(); 111 Put p = new Put(T1, ts); 112 p.addColumn(c0, c0, T1); 113 region.put(p); 114 p = new Put(T1, ts+1); 115 p.addColumn(c0, c0, T2); 116 region.put(p); 117 p = new Put(T1, ts+2); 118 p.addColumn(c0, c0, T3); 119 region.put(p); 120 p = new Put(T1, ts+4); 121 p.addColumn(c0, c0, T4); 122 region.put(p); 123 124 // now place a delete marker at ts+2 125 Delete d = new Delete(T1, ts+2); 126 region.delete(d); 127 128 // a raw scan can see the delete markers 129 // (one for each column family) 130 assertEquals(3, countDeleteMarkers(region)); 131 132 // get something *before* the delete marker 133 Get g = new Get(T1); 134 g.setMaxVersions(); 135 g.setTimeRange(0L, ts+2); 136 Result r = region.get(g); 137 checkResult(r, c0, c0, T2,T1); 138 139 // flush 140 region.flush(true); 141 142 // yep, T2 still there, T1 gone 143 r = region.get(g); 144 checkResult(r, c0, c0, T2); 145 146 // major compact 147 region.compact(true); 148 region.compact(true); 149 150 // one delete marker left (the others did not 151 // have older puts) 152 assertEquals(1, countDeleteMarkers(region)); 153 154 // still there (even after multiple compactions) 155 r = region.get(g); 156 checkResult(r, c0, c0, T2); 157 158 // a timerange that includes the delete marker won't see past rows 159 g.setTimeRange(0L, ts+4); 160 r = region.get(g); 161 assertTrue(r.isEmpty()); 162 163 // two more puts, this will expire the older puts. 164 p = new Put(T1, ts+5); 165 p.addColumn(c0, c0, T5); 166 region.put(p); 167 p = new Put(T1, ts+6); 168 p.addColumn(c0, c0, T6); 169 region.put(p); 170 171 // also add an old put again 172 // (which is past the max versions) 173 p = new Put(T1, ts); 174 p.addColumn(c0, c0, T1); 175 region.put(p); 176 r = region.get(g); 177 assertTrue(r.isEmpty()); 178 179 region.flush(true); 180 region.compact(true); 181 region.compact(true); 182 183 // verify that the delete marker itself was collected 184 region.put(p); 185 r = region.get(g); 186 checkResult(r, c0, c0, T1); 187 assertEquals(0, countDeleteMarkers(region)); 188 189 HBaseTestingUtility.closeRegionAndWAL(region); 190 } 191 192 /** 193 * Even when the store does not keep deletes a "raw" scan will 194 * return everything it can find (unless discarding cells is guaranteed 195 * to have no effect). 196 * Assuming this the desired behavior. Could also disallow "raw" scanning 197 * if the store does not have KEEP_DELETED_CELLS enabled. 198 * (can be changed easily) 199 */ 200 @Test 201 public void testRawScanWithoutKeepingDeletes() throws Exception { 202 // KEEP_DELETED_CELLS is NOT enabled 203 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 204 HConstants.FOREVER, KeepDeletedCells.FALSE); 205 HRegion region = hbu.createLocalHRegion(htd, null, null); 206 207 long ts = EnvironmentEdgeManager.currentTime(); 208 Put p = new Put(T1, ts); 209 p.addColumn(c0, c0, T1); 210 region.put(p); 211 212 Delete d = new Delete(T1, ts); 213 d.addColumn(c0, c0, ts); 214 region.delete(d); 215 216 // scan still returns delete markers and deletes rows 217 Scan s = new Scan(); 218 s.setRaw(true); 219 s.setMaxVersions(); 220 InternalScanner scan = region.getScanner(s); 221 List<Cell> kvs = new ArrayList<>(); 222 scan.next(kvs); 223 assertEquals(2, kvs.size()); 224 225 region.flush(true); 226 region.compact(true); 227 228 // after compaction they are gone 229 // (note that this a test with a Store without 230 // KEEP_DELETED_CELLS) 231 s = new Scan(); 232 s.setRaw(true); 233 s.setMaxVersions(); 234 scan = region.getScanner(s); 235 kvs = new ArrayList<>(); 236 scan.next(kvs); 237 assertTrue(kvs.isEmpty()); 238 239 HBaseTestingUtility.closeRegionAndWAL(region); 240 } 241 242 /** 243 * basic verification of existing behavior 244 */ 245 @Test 246 public void testWithoutKeepingDeletes() throws Exception { 247 // KEEP_DELETED_CELLS is NOT enabled 248 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 249 HConstants.FOREVER, KeepDeletedCells.FALSE); 250 HRegion region = hbu.createLocalHRegion(htd, null, null); 251 252 long ts = EnvironmentEdgeManager.currentTime(); 253 Put p = new Put(T1, ts); 254 p.addColumn(c0, c0, T1); 255 region.put(p); 256 257 Get gOne = new Get(T1); 258 gOne.setMaxVersions(); 259 gOne.setTimeRange(0L, ts + 1); 260 Result rOne = region.get(gOne); 261 assertFalse(rOne.isEmpty()); 262 263 264 Delete d = new Delete(T1, ts+2); 265 d.addColumn(c0, c0, ts); 266 region.delete(d); 267 268 // "past" get does not see rows behind delete marker 269 Get g = new Get(T1); 270 g.setMaxVersions(); 271 g.setTimeRange(0L, ts+1); 272 Result r = region.get(g); 273 assertTrue(r.isEmpty()); 274 275 // "past" scan does not see rows behind delete marker 276 Scan s = new Scan(); 277 s.setMaxVersions(); 278 s.setTimeRange(0L, ts+1); 279 InternalScanner scanner = region.getScanner(s); 280 List<Cell> kvs = new ArrayList<>(); 281 while (scanner.next(kvs)) { 282 continue; 283 } 284 assertTrue(kvs.isEmpty()); 285 286 // flushing and minor compaction keep delete markers 287 region.flush(true); 288 region.compact(false); 289 assertEquals(1, countDeleteMarkers(region)); 290 region.compact(true); 291 // major compaction deleted it 292 assertEquals(0, countDeleteMarkers(region)); 293 294 HBaseTestingUtility.closeRegionAndWAL(region); 295 } 296 297 /** 298 * The ExplicitColumnTracker does not support "raw" scanning. 299 */ 300 @Test 301 public void testRawScanWithColumns() throws Exception { 302 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 303 HConstants.FOREVER, KeepDeletedCells.TRUE); 304 Region region = hbu.createLocalHRegion(htd, null, null); 305 306 Scan s = new Scan(); 307 s.setRaw(true); 308 s.setMaxVersions(); 309 s.addColumn(c0, c0); 310 311 try { 312 region.getScanner(s); 313 fail("raw scanner with columns should have failed"); 314 } catch (org.apache.hadoop.hbase.DoNotRetryIOException dnre) { 315 // ok! 316 } 317 318 HBaseTestingUtility.closeRegionAndWAL(region); 319 } 320 321 /** 322 * Verify that "raw" scanning mode return delete markers and deletes rows. 323 */ 324 @Test 325 public void testRawScan() throws Exception { 326 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 327 HConstants.FOREVER, KeepDeletedCells.TRUE); 328 Region region = hbu.createLocalHRegion(htd, null, null); 329 330 long ts = EnvironmentEdgeManager.currentTime(); 331 Put p = new Put(T1, ts); 332 p.addColumn(c0, c0, T1); 333 region.put(p); 334 p = new Put(T1, ts+2); 335 p.addColumn(c0, c0, T2); 336 region.put(p); 337 p = new Put(T1, ts+4); 338 p.addColumn(c0, c0, T3); 339 region.put(p); 340 341 Delete d = new Delete(T1, ts+1); 342 region.delete(d); 343 344 d = new Delete(T1, ts+2); 345 d.addColumn(c0, c0, ts+2); 346 region.delete(d); 347 348 d = new Delete(T1, ts+3); 349 d.addColumns(c0, c0, ts+3); 350 region.delete(d); 351 352 Scan s = new Scan(); 353 s.setRaw(true); 354 s.setMaxVersions(); 355 InternalScanner scan = region.getScanner(s); 356 List<Cell> kvs = new ArrayList<>(); 357 scan.next(kvs); 358 assertEquals(8, kvs.size()); 359 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(0))); 360 assertArrayEquals(CellUtil.cloneValue(kvs.get(1)), T3); 361 assertTrue(CellUtil.isDelete(kvs.get(2))); 362 assertTrue(CellUtil.isDelete(kvs.get(3))); // .isDeleteType()); 363 assertArrayEquals(CellUtil.cloneValue(kvs.get(4)), T2); 364 assertArrayEquals(CellUtil.cloneValue(kvs.get(5)), T1); 365 // we have 3 CFs, so there are two more delete markers 366 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(6))); 367 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(7))); 368 369 // verify that raw scans honor the passed timerange 370 s = new Scan(); 371 s.setRaw(true); 372 s.setMaxVersions(); 373 s.setTimeRange(0, 1); 374 scan = region.getScanner(s); 375 kvs = new ArrayList<>(); 376 scan.next(kvs); 377 // nothing in this interval, not even delete markers 378 assertTrue(kvs.isEmpty()); 379 380 // filter new delete markers 381 s = new Scan(); 382 s.setRaw(true); 383 s.setMaxVersions(); 384 s.setTimeRange(0, ts+2); 385 scan = region.getScanner(s); 386 kvs = new ArrayList<>(); 387 scan.next(kvs); 388 assertEquals(4, kvs.size()); 389 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(0))); 390 assertArrayEquals(CellUtil.cloneValue(kvs.get(1)), T1); 391 // we have 3 CFs 392 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(2))); 393 assertTrue(PrivateCellUtil.isDeleteFamily(kvs.get(3))); 394 395 // filter old delete markers 396 s = new Scan(); 397 s.setRaw(true); 398 s.setMaxVersions(); 399 s.setTimeRange(ts+3, ts+5); 400 scan = region.getScanner(s); 401 kvs = new ArrayList<>(); 402 scan.next(kvs); 403 assertEquals(2, kvs.size()); 404 assertArrayEquals(CellUtil.cloneValue(kvs.get(0)), T3); 405 assertTrue(CellUtil.isDelete(kvs.get(1))); 406 407 408 HBaseTestingUtility.closeRegionAndWAL(region); 409 } 410 411 /** 412 * Verify that delete markers are removed from an otherwise empty store. 413 */ 414 @Test 415 public void testDeleteMarkerExpirationEmptyStore() throws Exception { 416 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 417 HConstants.FOREVER, KeepDeletedCells.TRUE); 418 HRegion region = hbu.createLocalHRegion(htd, null, null); 419 420 long ts = EnvironmentEdgeManager.currentTime(); 421 422 Delete d = new Delete(T1, ts); 423 d.addColumns(c0, c0, ts); 424 region.delete(d); 425 426 d = new Delete(T1, ts); 427 d.addFamily(c0); 428 region.delete(d); 429 430 d = new Delete(T1, ts); 431 d.addColumn(c0, c0, ts+1); 432 region.delete(d); 433 434 d = new Delete(T1, ts); 435 d.addColumn(c0, c0, ts+2); 436 region.delete(d); 437 438 // 1 family marker, 1 column marker, 2 version markers 439 assertEquals(4, countDeleteMarkers(region)); 440 441 // neither flush nor minor compaction removes any marker 442 region.flush(true); 443 assertEquals(4, countDeleteMarkers(region)); 444 region.compact(false); 445 assertEquals(4, countDeleteMarkers(region)); 446 447 // major compaction removes all, since there are no puts they affect 448 region.compact(true); 449 assertEquals(0, countDeleteMarkers(region)); 450 451 HBaseTestingUtility.closeRegionAndWAL(region); 452 } 453 454 /** 455 * Test delete marker removal from store files. 456 */ 457 @Test 458 public void testDeleteMarkerExpiration() throws Exception { 459 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 460 HConstants.FOREVER, KeepDeletedCells.TRUE); 461 HRegion region = hbu.createLocalHRegion(htd, null, null); 462 463 long ts = EnvironmentEdgeManager.currentTime(); 464 465 Put p = new Put(T1, ts); 466 p.addColumn(c0, c0, T1); 467 region.put(p); 468 469 // a put into another store (CF) should have no effect 470 p = new Put(T1, ts-10); 471 p.addColumn(c1, c0, T1); 472 region.put(p); 473 474 // all the following deletes affect the put 475 Delete d = new Delete(T1, ts); 476 d.addColumns(c0, c0, ts); 477 region.delete(d); 478 479 d = new Delete(T1, ts); 480 d.addFamily(c0, ts); 481 region.delete(d); 482 483 d = new Delete(T1, ts); 484 d.addColumn(c0, c0, ts+1); 485 region.delete(d); 486 487 d = new Delete(T1, ts); 488 d.addColumn(c0, c0, ts+2); 489 region.delete(d); 490 491 // 1 family marker, 1 column marker, 2 version markers 492 assertEquals(4, countDeleteMarkers(region)); 493 494 region.flush(true); 495 assertEquals(4, countDeleteMarkers(region)); 496 region.compact(false); 497 assertEquals(4, countDeleteMarkers(region)); 498 499 // another put will push out the earlier put... 500 p = new Put(T1, ts+3); 501 p.addColumn(c0, c0, T1); 502 region.put(p); 503 504 region.flush(true); 505 // no markers are collected, since there is an affected put 506 region.compact(true); 507 assertEquals(4, countDeleteMarkers(region)); 508 509 // the last collections collected the earlier put 510 // so after this collection all markers 511 region.compact(true); 512 assertEquals(0, countDeleteMarkers(region)); 513 514 HBaseTestingUtility.closeRegionAndWAL(region); 515 } 516 517 /** 518 * Test delete marker removal from store files. 519 */ 520 @Test 521 public void testWithOldRow() throws Exception { 522 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 523 HConstants.FOREVER, KeepDeletedCells.TRUE); 524 HRegion region = hbu.createLocalHRegion(htd, null, null); 525 526 long ts = EnvironmentEdgeManager.currentTime(); 527 528 Put p = new Put(T1, ts); 529 p.addColumn(c0, c0, T1); 530 region.put(p); 531 532 // a put another (older) row in the same store 533 p = new Put(T2, ts-10); 534 p.addColumn(c0, c0, T1); 535 region.put(p); 536 537 // all the following deletes affect the put 538 Delete d = new Delete(T1, ts); 539 d.addColumns(c0, c0, ts); 540 region.delete(d); 541 542 d = new Delete(T1, ts); 543 d.addFamily(c0, ts); 544 region.delete(d); 545 546 d = new Delete(T1, ts); 547 d.addColumn(c0, c0, ts+1); 548 region.delete(d); 549 550 d = new Delete(T1, ts); 551 d.addColumn(c0, c0, ts+2); 552 region.delete(d); 553 554 // 1 family marker, 1 column marker, 2 version markers 555 assertEquals(4, countDeleteMarkers(region)); 556 557 region.flush(true); 558 assertEquals(4, countDeleteMarkers(region)); 559 region.compact(false); 560 assertEquals(4, countDeleteMarkers(region)); 561 562 // another put will push out the earlier put... 563 p = new Put(T1, ts+3); 564 p.addColumn(c0, c0, T1); 565 region.put(p); 566 567 region.flush(true); 568 // no markers are collected, since there is an affected put 569 region.compact(true); 570 assertEquals(4, countDeleteMarkers(region)); 571 572 // all markers remain, since we have the older row 573 // and we haven't pushed the inlined markers past MAX_VERSIONS 574 region.compact(true); 575 assertEquals(4, countDeleteMarkers(region)); 576 577 // another put will push out the earlier put... 578 p = new Put(T1, ts+4); 579 p.addColumn(c0, c0, T1); 580 region.put(p); 581 582 // this pushed out the column and version marker 583 // but the family markers remains. THIS IS A PROBLEM! 584 region.compact(true); 585 assertEquals(1, countDeleteMarkers(region)); 586 587 // no amount of compacting is getting this of this one 588 // KEEP_DELETED_CELLS=>TTL is an option to avoid this. 589 region.compact(true); 590 assertEquals(1, countDeleteMarkers(region)); 591 592 HBaseTestingUtility.closeRegionAndWAL(region); 593 } 594 595 /** 596 * Verify correct range demarcation 597 */ 598 @Test 599 public void testRanges() throws Exception { 600 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 3, 601 HConstants.FOREVER, KeepDeletedCells.TRUE); 602 Region region = hbu.createLocalHRegion(htd, null, null); 603 604 long ts = EnvironmentEdgeManager.currentTime(); 605 Put p = new Put(T1, ts); 606 p.addColumn(c0, c0, T1); 607 p.addColumn(c0, c1, T1); 608 p.addColumn(c1, c0, T1); 609 p.addColumn(c1, c1, T1); 610 region.put(p); 611 612 p = new Put(T2, ts); 613 p.addColumn(c0, c0, T1); 614 p.addColumn(c0, c1, T1); 615 p.addColumn(c1, c0, T1); 616 p.addColumn(c1, c1, T1); 617 region.put(p); 618 619 p = new Put(T1, ts+1); 620 p.addColumn(c0, c0, T2); 621 p.addColumn(c0, c1, T2); 622 p.addColumn(c1, c0, T2); 623 p.addColumn(c1, c1, T2); 624 region.put(p); 625 626 p = new Put(T2, ts+1); 627 p.addColumn(c0, c0, T2); 628 p.addColumn(c0, c1, T2); 629 p.addColumn(c1, c0, T2); 630 p.addColumn(c1, c1, T2); 631 region.put(p); 632 633 Delete d = new Delete(T1, ts+2); 634 d.addColumns(c0, c0, ts+2); 635 region.delete(d); 636 637 d = new Delete(T1, ts+2); 638 d.addFamily(c1, ts+2); 639 region.delete(d); 640 641 d = new Delete(T2, ts+2); 642 d.addFamily(c0, ts+2); 643 region.delete(d); 644 645 // add an older delete, to make sure it is filtered 646 d = new Delete(T1, ts-10); 647 d.addFamily(c1, ts-10); 648 region.delete(d); 649 650 // ts + 2 does NOT include the delete at ts+2 651 checkGet(region, T1, c0, c0, ts+2, T2, T1); 652 checkGet(region, T1, c0, c1, ts+2, T2, T1); 653 checkGet(region, T1, c1, c0, ts+2, T2, T1); 654 checkGet(region, T1, c1, c1, ts+2, T2, T1); 655 656 checkGet(region, T2, c0, c0, ts+2, T2, T1); 657 checkGet(region, T2, c0, c1, ts+2, T2, T1); 658 checkGet(region, T2, c1, c0, ts+2, T2, T1); 659 checkGet(region, T2, c1, c1, ts+2, T2, T1); 660 661 // ts + 3 does 662 checkGet(region, T1, c0, c0, ts+3); 663 checkGet(region, T1, c0, c1, ts+3, T2, T1); 664 checkGet(region, T1, c1, c0, ts+3); 665 checkGet(region, T1, c1, c1, ts+3); 666 667 checkGet(region, T2, c0, c0, ts+3); 668 checkGet(region, T2, c0, c1, ts+3); 669 checkGet(region, T2, c1, c0, ts+3, T2, T1); 670 checkGet(region, T2, c1, c1, ts+3, T2, T1); 671 672 HBaseTestingUtility.closeRegionAndWAL(region); 673 } 674 675 /** 676 * Verify that column/version delete makers are sorted 677 * with their respective puts and removed correctly by 678 * versioning (i.e. not relying on the store earliestPutTS). 679 */ 680 @Test 681 public void testDeleteMarkerVersioning() throws Exception { 682 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 683 HConstants.FOREVER, KeepDeletedCells.TRUE); 684 HRegion region = hbu.createLocalHRegion(htd, null, null); 685 686 long ts = EnvironmentEdgeManager.currentTime(); 687 Put p = new Put(T1, ts); 688 p.addColumn(c0, c0, T1); 689 region.put(p); 690 691 // this prevents marker collection based on earliestPut 692 // (cannot keep earliest put per column in the store file) 693 p = new Put(T1, ts-10); 694 p.addColumn(c0, c1, T1); 695 region.put(p); 696 697 Delete d = new Delete(T1, ts); 698 // test corner case (Put and Delete have same TS) 699 d.addColumns(c0, c0, ts); 700 region.delete(d); 701 702 d = new Delete(T1, ts+1); 703 d.addColumn(c0, c0, ts+1); 704 region.delete(d); 705 706 d = new Delete(T1, ts+3); 707 d.addColumn(c0, c0, ts+3); 708 region.delete(d); 709 710 region.flush(true); 711 region.compact(true); 712 region.compact(true); 713 assertEquals(3, countDeleteMarkers(region)); 714 715 // add two more puts, since max version is 1 716 // the 2nd put (and all delete markers following) 717 // will be removed. 718 p = new Put(T1, ts+2); 719 p.addColumn(c0, c0, T2); 720 region.put(p); 721 722 // delete, put, delete, delete, put 723 assertEquals(3, countDeleteMarkers(region)); 724 725 p = new Put(T1, ts+3); 726 p.addColumn(c0, c0, T3); 727 region.put(p); 728 729 // This is potentially questionable behavior. 730 // This could be changed by not letting the ScanQueryMatcher 731 // return SEEK_NEXT_COL if a put is past VERSIONS, but instead 732 // return SKIP if the store has KEEP_DELETED_CELLS set. 733 // 734 // As it stands, the 1 here is correct here. 735 // There are two puts, VERSIONS is one, so after the 1st put the scanner 736 // knows that there can be no more KVs (put or delete) that have any effect. 737 // 738 // delete, put, put | delete, delete 739 assertEquals(1, countDeleteMarkers(region)); 740 741 // flush cache only sees what is in the memstore 742 region.flush(true); 743 744 // Here we have the three markers again, because the flush above 745 // removed the 2nd put before the file is written. 746 // So there's only one put, and hence the deletes already in the store 747 // files cannot be removed safely. 748 // delete, put, delete, delete 749 assertEquals(3, countDeleteMarkers(region)); 750 751 region.compact(true); 752 assertEquals(3, countDeleteMarkers(region)); 753 754 // add one more put 755 p = new Put(T1, ts+4); 756 p.addColumn(c0, c0, T4); 757 region.put(p); 758 759 region.flush(true); 760 // one trailing delete marker remains (but only one) 761 // because delete markers do not increase the version count 762 assertEquals(1, countDeleteMarkers(region)); 763 region.compact(true); 764 region.compact(true); 765 assertEquals(1, countDeleteMarkers(region)); 766 767 HBaseTestingUtility.closeRegionAndWAL(region); 768 } 769 770 /** 771 * Verify scenarios with multiple CFs and columns 772 */ 773 @Test 774 public void testWithMixedCFs() throws Exception { 775 HTableDescriptor htd = hbu.createTableDescriptor(name.getMethodName(), 0, 1, 776 HConstants.FOREVER, KeepDeletedCells.TRUE); 777 Region region = hbu.createLocalHRegion(htd, null, null); 778 779 long ts = EnvironmentEdgeManager.currentTime(); 780 781 Put p = new Put(T1, ts); 782 p.addColumn(c0, c0, T1); 783 p.addColumn(c0, c1, T1); 784 p.addColumn(c1, c0, T1); 785 p.addColumn(c1, c1, T1); 786 region.put(p); 787 788 p = new Put(T2, ts+1); 789 p.addColumn(c0, c0, T2); 790 p.addColumn(c0, c1, T2); 791 p.addColumn(c1, c0, T2); 792 p.addColumn(c1, c1, T2); 793 region.put(p); 794 795 // family markers are each family 796 Delete d = new Delete(T1, ts+1); 797 region.delete(d); 798 799 d = new Delete(T2, ts+2); 800 region.delete(d); 801 802 Scan s = new Scan(T1); 803 s.setTimeRange(0, ts+1); 804 InternalScanner scanner = region.getScanner(s); 805 List<Cell> kvs = new ArrayList<>(); 806 scanner.next(kvs); 807 assertEquals(4, kvs.size()); 808 scanner.close(); 809 810 s = new Scan(T2); 811 s.setTimeRange(0, ts+2); 812 scanner = region.getScanner(s); 813 kvs = new ArrayList<>(); 814 scanner.next(kvs); 815 assertEquals(4, kvs.size()); 816 scanner.close(); 817 818 HBaseTestingUtility.closeRegionAndWAL(region); 819 } 820 821 /** 822 * Test keeping deleted rows together with min versions set 823 */ 824 @Test 825 public void testWithMinVersions() throws Exception { 826 HTableDescriptor htd = 827 hbu.createTableDescriptor(name.getMethodName(), 3, 1000, 1, KeepDeletedCells.TRUE); 828 HRegion region = hbu.createLocalHRegion(htd, null, null); 829 830 long ts = EnvironmentEdgeManager.currentTime() - 2000; // 2s in the past 831 832 Put p = new Put(T1, ts); 833 p.addColumn(c0, c0, T3); 834 region.put(p); 835 p = new Put(T1, ts-1); 836 p.addColumn(c0, c0, T2); 837 region.put(p); 838 p = new Put(T1, ts-3); 839 p.addColumn(c0, c0, T1); 840 region.put(p); 841 p = new Put(T1, ts-4); 842 p.addColumn(c0, c0, T0); 843 region.put(p); 844 845 // all puts now are just retained because of min versions = 3 846 847 // place a family delete marker 848 Delete d = new Delete(T1, ts-1); 849 region.delete(d); 850 // and a column delete marker 851 d = new Delete(T1, ts-2); 852 d.addColumns(c0, c0, ts-1); 853 region.delete(d); 854 855 Get g = new Get(T1); 856 g.setMaxVersions(); 857 g.setTimeRange(0L, ts-2); 858 Result r = region.get(g); 859 checkResult(r, c0, c0, T1,T0); 860 861 // 3 families, one column delete marker 862 assertEquals(4, countDeleteMarkers(region)); 863 864 region.flush(true); 865 // no delete marker removes by the flush 866 assertEquals(4, countDeleteMarkers(region)); 867 868 r = region.get(g); 869 checkResult(r, c0, c0, T1); 870 p = new Put(T1, ts+1); 871 p.addColumn(c0, c0, T4); 872 region.put(p); 873 region.flush(true); 874 875 assertEquals(4, countDeleteMarkers(region)); 876 877 r = region.get(g); 878 checkResult(r, c0, c0, T1); 879 880 // this will push out the last put before 881 // family delete marker 882 p = new Put(T1, ts+2); 883 p.addColumn(c0, c0, T5); 884 region.put(p); 885 886 region.flush(true); 887 region.compact(true); 888 // the two family markers without puts are gone 889 assertEquals(2, countDeleteMarkers(region)); 890 891 // the last compactStores updated the earliestPutTs, 892 // so after the next compaction the last family delete marker is also gone 893 region.compact(true); 894 assertEquals(0, countDeleteMarkers(region)); 895 896 HBaseTestingUtility.closeRegionAndWAL(region); 897 } 898 899 /** 900 * Test keeping deleted rows together with min versions set 901 */ 902 @Test 903 public void testWithTTL() throws Exception { 904 HTableDescriptor htd = 905 hbu.createTableDescriptor(name.getMethodName(), 1, 1000, 1, KeepDeletedCells.TTL); 906 HRegion region = hbu.createLocalHRegion(htd, null, null); 907 908 long ts = EnvironmentEdgeManager.currentTime() - 2000; // 2s in the past 909 910 Put p = new Put(T1, ts); 911 p.addColumn(c0, c0, T3); 912 region.put(p); 913 914 // place an old row, to make the family marker expires anyway 915 p = new Put(T2, ts-10); 916 p.addColumn(c0, c0, T1); 917 region.put(p); 918 919 checkGet(region, T1, c0, c0, ts+1, T3); 920 // place a family delete marker 921 Delete d = new Delete(T1, ts+2); 922 region.delete(d); 923 924 checkGet(region, T1, c0, c0, ts+1, T3); 925 926 // 3 families, one column delete marker 927 assertEquals(3, countDeleteMarkers(region)); 928 929 region.flush(true); 930 // no delete marker removes by the flush 931 assertEquals(3, countDeleteMarkers(region)); 932 933 // but the Put is gone 934 checkGet(region, T1, c0, c0, ts+1); 935 936 region.compact(true); 937 // all delete marker gone 938 assertEquals(0, countDeleteMarkers(region)); 939 940 HBaseTestingUtility.closeRegionAndWAL(region); 941 } 942 943 private void checkGet(Region region, byte[] row, byte[] fam, byte[] col, 944 long time, byte[]... vals) throws IOException { 945 Get g = new Get(row); 946 g.addColumn(fam, col); 947 g.setMaxVersions(); 948 g.setTimeRange(0L, time); 949 Result r = region.get(g); 950 checkResult(r, fam, col, vals); 951 952 } 953 954 private int countDeleteMarkers(HRegion region) throws IOException { 955 Scan s = new Scan(); 956 s.setRaw(true); 957 // use max versions from the store(s) 958 s.setMaxVersions(region.getStores().iterator().next().getScanInfo().getMaxVersions()); 959 InternalScanner scan = region.getScanner(s); 960 List<Cell> kvs = new ArrayList<>(); 961 int res = 0; 962 boolean hasMore; 963 do { 964 hasMore = scan.next(kvs); 965 for (Cell kv : kvs) { 966 if(CellUtil.isDelete(kv)) { 967 res++; 968 } 969 } 970 kvs.clear(); 971 } while (hasMore); 972 scan.close(); 973 return res; 974 } 975 976 private void checkResult(Result r, byte[] fam, byte[] col, byte[] ... vals) { 977 assertEquals(r.size(), vals.length); 978 List<Cell> kvs = r.getColumnCells(fam, col); 979 assertEquals(kvs.size(), vals.length); 980 for (int i=0;i<vals.length;i++) { 981 assertArrayEquals(CellUtil.cloneValue(kvs.get(i)), vals[i]); 982 } 983 } 984 985 986} 987